vendor: 5,498 bytes, lines 1-190
1/*! DataTables 2.0.5 2 * © SpryMedia Ltd - datatables.net/license 3 */ 4 5/** 6 * @summary DataTables 7 * @description Paginate, search and order HTML tables 8 * @version 2.0.5 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 /*
vendor: 25,146 bytes, lines 1078-1988
1078 * It is useful to have variables which are scoped locally so only the 1079 * DataTables functions can access them and they don't leak into global space. 1080 * At the same time these functions are often useful over multiple files in the 1081 * core and API, so we list, or at least document, all variables which are used 1082 * by DataTables as private variables here. This also ensures that there is no 1083 * clashing of variable names and that they can easily referenced for reuse. 1084 */ 1085 1086 1087 // Defined else where 1088 // _selector_run 1089 // _selector_opts 1090 // _selector_row_indexes 1091 1092 var _ext; // DataTable.ext 1093 var _Api; // DataTable.Api 1094 var _api_register; // DataTable.Api.register 1095 var _api_registerPlural; // DataTable.Api.registerPlural 1096 1097 var _re_dic = {}; 1098 var _re_new_lines = /[\r\n\u2028]/g; 1099 var _re_html = /<([^>]*>)/g; 1100 var _max_str_len = Math.pow(2, 28); 1101 1102 // This is not strict ISO8601 - Date.parse() is quite lax, although 1103 // implementations differ between browsers. 1104 var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/; 1105 1106 // Escape regular expression special characters 1107 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); 1108 1109 // https://en.wikipedia.org/wiki/Foreign_exchange_market 1110 // - \u20BD - Russian ruble. 1111 // - \u20a9 - South Korean Won 1112 // - \u20BA - Turkish Lira 1113 // - \u20B9 - Indian Rupee 1114 // - R - Brazil (R$) and South Africa 1115 // - fr - Swiss Franc 1116 // - kr - Swedish krona, Norwegian krone and Danish krone 1117 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many 1118 // - É - Bitcoin 1119 // - Î - Ethereum 1120 // standards as thousands separators. 1121 var _re_formatted_numeric = /['\u00A0,$£â¬Â¥%\u2009\u202F\u20BD\u20a9\u20BArfkÉÎ]/gi; 1122 1123 1124 var _empty = function ( d ) { 1125 return !d || d === true || d === '-' ? true : false; 1126 }; 1127 1128 1129 var _intVal = function ( s ) { 1130 var integer = parseInt( s, 10 ); 1131 return !isNaN(integer) && isFinite(s) ? integer : null; 1132 }; 1133 1134 // Convert from a formatted number with characters other than `.` as the 1135 // decimal place, to a Javascript number 1136 var _numToDecimal = function ( num, decimalPoint ) { 1137 // Cache created regular expressions for speed as this function is called often 1138 if ( ! _re_dic[ decimalPoint ] ) { 1139 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); 1140 } 1141 return typeof num === 'string' && decimalPoint !== '.' ? 1142 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : 1143 num; 1144 }; 1145 1146 1147 var _isNumber = function ( d, decimalPoint, formatted ) { 1148 var type = typeof d; 1149 var strType = type === 'string'; 1150 1151 if ( type === 'number' || type === 'bigint') { 1152 return true; 1153 } 1154 1155 // If empty return immediately so there must be a number if it is a 1156 // formatted string (this stops the string "k", or "kr", etc being detected 1157 // as a formatted number for currency 1158 if ( _empty( d ) ) { 1159 return true; 1160 } 1161 1162 if ( decimalPoint && strType ) { 1163 d = _numToDecimal( d, decimalPoint ); 1164 } 1165 1166 if ( formatted && strType ) { 1167 d = d.replace( _re_formatted_numeric, '' ); 1168 } 1169 1170 return !isNaN( parseFloat(d) ) && isFinite( d ); 1171 }; 1172 1173 1174 // A string without HTML in it can be considered to be HTML still 1175 var _isHtml = function ( d ) { 1176 return _empty( d ) || typeof d === 'string'; 1177 }; 1178 1179 // Is a string a number surrounded by HTML? 1180 var _htmlNumeric = function ( d, decimalPoint, formatted ) { 1181 if ( _empty( d ) ) { 1182 return true; 1183 } 1184 1185 // input and select strings mean that this isn't just a number 1186 if (typeof d === 'string' && d.match(/<(input|select)/i)) { 1187 return null; 1188 } 1189 1190 var html = _isHtml( d ); 1191 return ! html ? 1192 null : 1193 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? 1194 true : 1195 null; 1196 }; 1197 1198 1199 var _pluck = function ( a, prop, prop2 ) { 1200 var out = []; 1201 var i=0, ien=a.length; 1202 1203 // Could have the test in the loop for slightly smaller code, but speed 1204 // is essential here 1205 if ( prop2 !== undefined ) { 1206 for ( ; i<ien ; i++ ) { 1207 if ( a[i] && a[i][ prop ] ) { 1208 out.push( a[i][ prop ][ prop2 ] ); 1209 } 1210 } 1211 } 1212 else { 1213 for ( ; i<ien ; i++ ) { 1214 if ( a[i] ) { 1215 out.push( a[i][ prop ] ); 1216 } 1217 } 1218 } 1219 1220 return out; 1221 }; 1222 1223 1224 // Basically the same as _pluck, but rather than looping over `a` we use `order` 1225 // as the indexes to pick from `a` 1226 var _pluck_order = function ( a, order, prop, prop2 ) 1227 { 1228 var out = []; 1229 var i=0, ien=order.length; 1230 1231 // Could have the test in the loop for slightly smaller code, but speed 1232 // is essential here 1233 if ( prop2 !== undefined ) { 1234 for ( ; i<ien ; i++ ) { 1235 if ( a[ order[i] ][ prop ] ) { 1236 out.push( a[ order[i] ][ prop ][ prop2 ] ); 1237 } 1238 } 1239 } 1240 else { 1241 for ( ; i<ien ; i++ ) { 1242 if ( a[ order[i] ] ) { 1243 out.push( a[ order[i] ][ prop ] ); 1244 } 1245 } 1246 } 1247 1248 return out; 1249 }; 1250 1251 1252 var _range = function ( len, start ) 1253 { 1254 var out = []; 1255 var end; 1256 1257 if ( start === undefined ) { 1258 start = 0; 1259 end = len; 1260 } 1261 else { 1262 end = start; 1263 start = len; 1264 } 1265 1266 for ( var i=start ; i<end ; i++ ) { 1267 out.push( i ); 1268 } 1269 1270 return out; 1271 }; 1272 1273 1274 var _removeEmpty = function ( a ) 1275 { 1276 var out = []; 1277 1278 for ( var i=0, ien=a.length ; i<ien ; i++ ) { 1279 if ( a[i] ) { // careful - will remove all falsy values! 1280 out.push( a[i] ); 1281 } 1282 } 1283 1284 return out; 1285 }; 1286 1287 // Replaceable function in api.util 1288 var _stripHtml = function (input) { 1289 // Irrelevant check to workaround CodeQL's false positive on the regex 1290 if (input.length > _max_str_len) { 1291 throw new Error('Exceeded max str len'); 1292 } 1293 1294 var previous; 1295 1296 input = input.replace(_re_html, ''); // Complete tags 1297 1298 // Safety for incomplete script tag - use do / while to ensure that 1299 // we get all instances 1300 do { 1301 previous = input; 1302 input = input.replace(/<script/i, ''); 1303 } while (input !== previous); 1304 1305 return previous; 1306 }; 1307 1308 // Replaceable function in api.util 1309 var _escapeHtml = function ( d ) { 1310 if (Array.isArray(d)) { 1311 d = d.join(','); 1312 } 1313 1314 return typeof d === 'string' ? 1315 d 1316 .replace(/&/g, '&') 1317 .replace(/</g, '<') 1318 .replace(/>/g, '>') 1319 .replace(/"/g, '"') : 1320 d; 1321 }; 1322 1323 // Remove diacritics from a string by decomposing it and then removing 1324 // non-ascii characters 1325 var _normalize = function (str, both) { 1326 if (typeof str !== 'string') { 1327 return str; 1328 } 1329 1330 // It is faster to just run `normalize` than it is to check if 1331 // we need to with a regex! 1332 var res = str.normalize("NFD"); 1333 1334 // Equally, here we check if a regex is needed or not 1335 return res.length !== str.length 1336 ? (both === true ? str + ' ' : '' ) + res.replace(/[\u0300-\u036f]/g, "") 1337 : res; 1338 } 1339 1340 /** 1341 * Determine if all values in the array are unique. This means we can short 1342 * cut the _unique method at the cost of a single loop. A sorted array is used 1343 * to easily check the values. 1344 * 1345 * @param {array} src Source array 1346 * @return {boolean} true if all unique, false otherwise 1347 * @ignore 1348 */ 1349 var _areAllUnique = function ( src ) { 1350 if ( src.length < 2 ) { 1351 return true; 1352 } 1353 1354 var sorted = src.slice().sort(); 1355 var last = sorted[0]; 1356 1357 for ( var i=1, ien=sorted.length ; i<ien ; i++ ) { 1358 if ( sorted[i] === last ) { 1359 return false; 1360 } 1361 1362 last = sorted[i]; 1363 } 1364 1365 return true; 1366 }; 1367 1368 1369 /** 1370 * Find the unique elements in a source array. 1371 * 1372 * @param {array} src Source array 1373 * @return {array} Array of unique items 1374 * @ignore 1375 */ 1376 var _unique = function ( src ) 1377 { 1378 if (Array.from && Set) { 1379 return Array.from(new Set(src)); 1380 } 1381 1382 if ( _areAllUnique( src ) ) { 1383 return src.slice(); 1384 } 1385 1386 // A faster unique method is to use object keys to identify used values, 1387 // but this doesn't work with arrays or objects, which we must also 1388 // consider. See jsperf.app/compare-array-unique-versions/4 for more 1389 // information. 1390 var 1391 out = [], 1392 val, 1393 i, ien=src.length, 1394 j, k=0; 1395 1396 again: for ( i=0 ; i<ien ; i++ ) { 1397 val = src[i]; 1398 1399 for ( j=0 ; j<k ; j++ ) { 1400 if ( out[j] === val ) { 1401 continue again; 1402 } 1403 } 1404 1405 out.push( val ); 1406 k++; 1407 } 1408 1409 return out; 1410 }; 1411 1412 // Surprisingly this is faster than [].concat.apply 1413 // https://jsperf.com/flatten-an-array-loop-vs-reduce/2 1414 var _flatten = function (out, val) { 1415 if (Array.isArray(val)) { 1416 for (var i=0 ; i<val.length ; i++) { 1417 _flatten(out, val[i]); 1418 } 1419 } 1420 else { 1421 out.push(val); 1422 } 1423 1424 return out; 1425 } 1426 1427 // Similar to jQuery's addClass, but use classList.add 1428 function _addClass(el, name) { 1429 if (name) { 1430 name.split(' ').forEach(function (n) { 1431 if (n) { 1432 // `add` does deduplication, so no need to check `contains` 1433 el.classList.add(n); 1434 } 1435 }); 1436 } 1437 } 1438 1439 /** 1440 * DataTables utility methods 1441 * 1442 * This namespace provides helper methods that DataTables uses internally to 1443 * create a DataTable, but which are not exclusively used only for DataTables. 1444 * These methods can be used by extension authors to save the duplication of 1445 * code. 1446 * 1447 * @namespace 1448 */ 1449 DataTable.util = { 1450 /** 1451 * Return a string with diacritic characters decomposed 1452 * @param {*} mixed Function or string to normalize 1453 * @param {*} both Return original string and the normalized string 1454 * @returns String or undefined 1455 */ 1456 diacritics: function (mixed, both) { 1457 var type = typeof mixed; 1458 1459 if (type !== 'function') { 1460 return _normalize(mixed, both); 1461 } 1462 _normalize = mixed; 1463 }, 1464 1465 /** 1466 * Debounce a function 1467 * 1468 * @param {function} fn Function to be called 1469 * @param {integer} freq Call frequency in mS 1470 * @return {function} Wrapped function 1471 */ 1472 debounce: function ( fn, timeout ) { 1473 var timer; 1474 1475 return function () { 1476 var that = this; 1477 var args = arguments; 1478 1479 clearTimeout(timer); 1480 1481 timer = setTimeout( function () { 1482 fn.apply(that, args); 1483 }, timeout || 250 ); 1484 }; 1485 }, 1486 1487 /** 1488 * Throttle the calls to a function. Arguments and context are maintained 1489 * for the throttled function. 1490 * 1491 * @param {function} fn Function to be called 1492 * @param {integer} freq Call frequency in mS 1493 * @return {function} Wrapped function 1494 */ 1495 throttle: function ( fn, freq ) { 1496 var 1497 frequency = freq !== undefined ? freq : 200, 1498 last, 1499 timer; 1500 1501 return function () { 1502 var 1503 that = this, 1504 now = +new Date(), 1505 args = arguments; 1506 1507 if ( last && now < last + frequency ) { 1508 clearTimeout( timer ); 1509 1510 timer = setTimeout( function () { 1511 last = undefined; 1512 fn.apply( that, args ); 1513 }, frequency ); 1514 } 1515 else { 1516 last = now; 1517 fn.apply( that, args ); 1518 } 1519 }; 1520 }, 1521 1522 /** 1523 * Escape a string such that it can be used in a regular expression 1524 * 1525 * @param {string} val string to escape 1526 * @returns {string} escaped string 1527 */ 1528 escapeRegex: function ( val ) { 1529 return val.replace( _re_escape_regex, '\\$1' ); 1530 }, 1531 1532 /** 1533 * Create a function that will write to a nested object or array 1534 * @param {*} source JSON notation string 1535 * @returns Write function 1536 */ 1537 set: function ( source ) { 1538 if ( $.isPlainObject( source ) ) { 1539 /* Unlike get, only the underscore (global) option is used for for 1540 * setting data since we don't know the type here. This is why an object 1541 * option is not documented for `mData` (which is read/write), but it is 1542 * for `mRender` which is read only. 1543 */ 1544 return DataTable.util.set( source._ ); 1545 } 1546 else if ( source === null ) { 1547 // Nothing to do when the data source is null 1548 return function () {}; 1549 } 1550 else if ( typeof source === 'function' ) { 1551 return function (data, val, meta) { 1552 source( data, 'set', val, meta ); 1553 }; 1554 } 1555 else if ( 1556 typeof source === 'string' && (source.indexOf('.') !== -1 || 1557 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1558 ) { 1559 // Like the get, we need to get data from a nested object 1560 var setData = function (data, val, src) { 1561 var a = _fnSplitObjNotation( src ), b; 1562 var aLast = a[a.length-1]; 1563 var arrayNotation, funcNotation, o, innerSrc; 1564 1565 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) { 1566 // Protect against prototype pollution 1567 if (a[i] === '__proto__' || a[i] === 'constructor') { 1568 throw new Error('Cannot set prototype values'); 1569 } 1570 1571 // Check if we are dealing with an array notation request 1572 arrayNotation = a[i].match(__reArray); 1573 funcNotation = a[i].match(__reFn); 1574 1575 if ( arrayNotation ) { 1576 a[i] = a[i].replace(__reArray, ''); 1577 data[ a[i] ] = []; 1578 1579 // Get the remainder of the nested object to set so we can recurse 1580 b = a.slice(); 1581 b.splice( 0, i+1 ); 1582 innerSrc = b.join('.'); 1583 1584 // Traverse each entry in the array setting the properties requested 1585 if ( Array.isArray( val ) ) { 1586 for ( var j=0, jLen=val.length ; j<jLen ; j++ ) { 1587 o = {}; 1588 setData( o, val[j], innerSrc ); 1589 data[ a[i] ].push( o ); 1590 } 1591 } 1592 else { 1593 // We've been asked to save data to an array, but it 1594 // isn't array data to be saved. Best that can be done 1595 // is to just save the value. 1596 data[ a[i] ] = val; 1597 } 1598 1599 // The inner call to setData has already traversed through the remainder 1600 // of the source and has set the data, thus we can exit here 1601 return; 1602 } 1603 else if ( funcNotation ) { 1604 // Function call 1605 a[i] = a[i].replace(__reFn, ''); 1606 data = data[ a[i] ]( val ); 1607 } 1608 1609 // If the nested object doesn't currently exist - since we are 1610 // trying to set the value - create it 1611 if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) { 1612 data[ a[i] ] = {}; 1613 } 1614 data = data[ a[i] ]; 1615 } 1616 1617 // Last item in the input - i.e, the actual set 1618 if ( aLast.match(__reFn ) ) { 1619 // Function call 1620 data = data[ aLast.replace(__reFn, '') ]( val ); 1621 } 1622 else { 1623 // If array notation is used, we just want to strip it and use the property name 1624 // and assign the value. If it isn't used, then we get the result we want anyway 1625 data[ aLast.replace(__reArray, '') ] = val; 1626 } 1627 }; 1628 1629 return function (data, val) { // meta is also passed in, but not used 1630 return setData( data, val, source ); 1631 }; 1632 } 1633 else { 1634 // Array or flat object mapping 1635 return function (data, val) { // meta is also passed in, but not used 1636 data[source] = val; 1637 }; 1638 } 1639 }, 1640 1641 /** 1642 * Create a function that will read nested objects from arrays, based on JSON notation 1643 * @param {*} source JSON notation string 1644 * @returns Value read 1645 */ 1646 get: function ( source ) { 1647 if ( $.isPlainObject( source ) ) { 1648 // Build an object of get functions, and wrap them in a single call 1649 var o = {}; 1650 $.each( source, function (key, val) { 1651 if ( val ) { 1652 o[key] = DataTable.util.get( val ); 1653 } 1654 } ); 1655 1656 return function (data, type, row, meta) { 1657 var t = o[type] || o._; 1658 return t !== undefined ? 1659 t(data, type, row, meta) : 1660 data; 1661 }; 1662 } 1663 else if ( source === null ) { 1664 // Give an empty string for rendering / sorting etc 1665 return function (data) { // type, row and meta also passed, but not used 1666 return data; 1667 }; 1668 } 1669 else if ( typeof source === 'function' ) { 1670 return function (data, type, row, meta) { 1671 return source( data, type, row, meta ); 1672 }; 1673 } 1674 else if ( 1675 typeof source === 'string' && (source.indexOf('.') !== -1 || 1676 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1677 ) { 1678 /* If there is a . in the source string then the data source is in a 1679 * nested object so we loop over the data for each level to get the next 1680 * level down. On each loop we test for undefined, and if found immediately 1681 * return. This allows entire objects to be missing and sDefaultContent to 1682 * be used if defined, rather than throwing an error 1683 */ 1684 var fetchData = function (data, type, src) { 1685 var arrayNotation, funcNotation, out, innerSrc; 1686 1687 if ( src !== "" ) { 1688 var a = _fnSplitObjNotation( src ); 1689 1690 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) { 1691 // Check if we are dealing with special notation 1692 arrayNotation = a[i].match(__reArray); 1693 funcNotation = a[i].match(__reFn); 1694 1695 if ( arrayNotation ) { 1696 // Array notation 1697 a[i] = a[i].replace(__reArray, ''); 1698 1699 // Condition allows simply [] to be passed in 1700 if ( a[i] !== "" ) { 1701 data = data[ a[i] ]; 1702 } 1703 out = []; 1704 1705 // Get the remainder of the nested object to get 1706 a.splice( 0, i+1 ); 1707 innerSrc = a.join('.'); 1708 1709 // Traverse each entry in the array getting the properties requested 1710 if ( Array.isArray( data ) ) { 1711 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) { 1712 out.push( fetchData( data[j], type, innerSrc ) ); 1713 } 1714 } 1715 1716 // If a string is given in between the array notation indicators, that 1717 // is used to join the strings together, otherwise an array is returned 1718 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1); 1719 data = (join==="") ? out : out.join(join); 1720 1721 // The inner call to fetchData has already traversed through the remainder 1722 // of the source requested, so we exit from the loop 1723 break; 1724 } 1725 else if ( funcNotation ) { 1726 // Function call 1727 a[i] = a[i].replace(__reFn, ''); 1728 data = data[ a[i] ](); 1729 continue; 1730 } 1731 1732 if (data === null || data[ a[i] ] === null) { 1733 return null; 1734 } 1735 else if ( data === undefined || data[ a[i] ] === undefined ) { 1736 return undefined; 1737 } 1738 1739 data = data[ a[i] ]; 1740 } 1741 } 1742 1743 return data; 1744 }; 1745 1746 return function (data, type) { // row and meta also passed, but not used 1747 return fetchData( data, type, source ); 1748 }; 1749 } 1750 else { 1751 // Array or flat object mapping 1752 return function (data) { // row and meta also passed, but not used 1753 return data[source]; 1754 }; 1755 } 1756 }, 1757 1758 stripHtml: function (mixed) { 1759 var type = typeof mixed; 1760 1761 if (type === 'function') { 1762 _stripHtml = mixed; 1763 return; 1764 } 1765 else if (type === 'string') { 1766 return _stripHtml(mixed); 1767 } 1768 return mixed; 1769 }, 1770 1771 escapeHtml: function (mixed) { 1772 var type = typeof mixed; 1773 1774 if (type === 'function') { 1775 _escapeHtml = mixed; 1776 return; 1777 } 1778 else if (type === 'string' || Array.isArray(mixed)) { 1779 return _escapeHtml(mixed); 1780 } 1781 return mixed; 1782 }, 1783 1784 unique: _unique 1785 }; 1786 1787 1788 1789 /** 1790 * Create a mapping object that allows camel case parameters to be looked up 1791 * for their Hungarian counterparts. The mapping is stored in a private 1792 * parameter called `_hungarianMap` which can be accessed on the source object. 1793 * @param {object} o 1794 * @memberof DataTable#oApi 1795 */ 1796 function _fnHungarianMap ( o ) 1797 { 1798 var 1799 hungarian = 'a aa ai ao as b fn i m o s ', 1800 match, 1801 newKey, 1802 map = {}; 1803 1804 $.each( o, function (key) { 1805 match = key.match(/^([^A-Z]+?)([A-Z])/); 1806 1807 if ( match && hungarian.indexOf(match[1]+' ') !== -1 ) 1808 { 1809 newKey = key.replace( match[0], match[2].toLowerCase() ); 1810 map[ newKey ] = key; 1811 1812 if ( match[1] === 'o' ) 1813 { 1814 _fnHungarianMap( o[key] ); 1815 } 1816 } 1817 } ); 1818 1819 o._hungarianMap = map; 1820 } 1821 1822 1823 /** 1824 * Convert from camel case parameters to Hungarian, based on a Hungarian map 1825 * created by _fnHungarianMap. 1826 * @param {object} src The model object which holds all parameters that can be 1827 * mapped. 1828 * @param {object} user The object to convert from camel case to Hungarian. 1829 * @param {boolean} force When set to `true`, properties which already have a 1830 * Hungarian value in the `user` object will be overwritten. Otherwise they 1831 * won't be. 1832 * @memberof DataTable#oApi 1833 */ 1834 function _fnCamelToHungarian ( src, user, force ) 1835 { 1836 if ( ! src._hungarianMap ) { 1837 _fnHungarianMap( src ); 1838 } 1839 1840 var hungarianKey; 1841 1842 $.each( user, function (key) { 1843 hungarianKey = src._hungarianMap[ key ]; 1844 1845 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) ) 1846 { 1847 // For objects, we need to buzz down into the object to copy parameters 1848 if ( hungarianKey.charAt(0) === 'o' ) 1849 { 1850 // Copy the camelCase options over to the hungarian 1851 if ( ! user[ hungarianKey ] ) { 1852 user[ hungarianKey ] = {}; 1853 } 1854 $.extend( true, user[hungarianKey], user[key] ); 1855 1856 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force ); 1857 } 1858 else { 1859 user[hungarianKey] = user[ key ]; 1860 } 1861 } 1862 } ); 1863 } 1864 1865 /** 1866 * Map one parameter onto another 1867 * @param {object} o Object to map 1868 * @param {*} knew The new parameter name 1869 * @param {*} old The old parameter name 1870 */ 1871 var _fnCompatMap = function ( o, knew, old ) { 1872 if ( o[ knew ] !== undefined ) { 1873 o[ old ] = o[ knew ]; 1874 } 1875 }; 1876 1877 1878 /** 1879 * Provide backwards compatibility for the main DT options. Note that the new 1880 * options are mapped onto the old parameters, so this is an external interface 1881 * change only. 1882 * @param {object} init Object to map 1883 */ 1884 function _fnCompatOpts ( init ) 1885 { 1886 _fnCompatMap( init, 'ordering', 'bSort' ); 1887 _fnCompatMap( init, 'orderMulti', 'bSortMulti' ); 1888 _fnCompatMap( init, 'orderClasses', 'bSortClasses' ); 1889 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' ); 1890 _fnCompatMap( init, 'order', 'aaSorting' ); 1891 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' ); 1892 _fnCompatMap( init, 'paging', 'bPaginate' ); 1893 _fnCompatMap( init, 'pagingType', 'sPaginationType' ); 1894 _fnCompatMap( init, 'pageLength', 'iDisplayLength' ); 1895 _fnCompatMap( init, 'searching', 'bFilter' ); 1896 1897 // Boolean initialisation of x-scrolling 1898 if ( typeof init.sScrollX === 'boolean' ) { 1899 init.sScrollX = init.sScrollX ? '100%' : ''; 1900 } 1901 if ( typeof init.scrollX === 'boolean' ) { 1902 init.scrollX = init.scrollX ? '100%' : ''; 1903 } 1904 1905 // Column search objects are in an array, so it needs to be converted 1906 // element by element 1907 var searchCols = init.aoSearchCols; 1908 1909 if ( searchCols ) { 1910 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) { 1911 if ( searchCols[i] ) { 1912 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] ); 1913 } 1914 } 1915 } 1916 1917 // Enable search delay if server-side processing is enabled 1918 if (init.serverSide && ! init.searchDelay) { 1919 init.searchDelay = 400; 1920 } 1921 } 1922 1923 1924 /** 1925 * Provide backwards compatibility for column options. Note that the new options 1926 * are mapped onto the old parameters, so this is an external interface change 1927 * only. 1928 * @param {object} init Object to map 1929 */ 1930 function _fnCompatCols ( init ) 1931 { 1932 _fnCompatMap( init, 'orderable', 'bSortable' ); 1933 _fnCompatMap( init, 'orderData', 'aDataSort' ); 1934 _fnCompatMap( init, 'orderSequence', 'asSorting' ); 1935 _fnCompatMap( init, 'orderDataType', 'sortDataType' ); 1936 1937 // orderData can be given as an integer 1938 var dataSort = init.aDataSort; 1939 if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) { 1940 init.aDataSort = [ dataSort ]; 1941 } 1942 } 1943 1944 1945 /** 1946 * Browser feature detection for capabilities, quirks 1947 * @param {object} settings dataTables settings object 1948 * @memberof DataTable#oApi 1949 */ 1950 function _fnBrowserDetect( settings ) 1951 { 1952 // We don't need to do this every time DataTables is constructed, the values 1953 // calculated are specific to the browser and OS configuration which we 1954 // don't expect to change between initialisations 1955 if ( ! DataTable.__browser ) { 1956 var browser = {}; 1957 DataTable.__browser = browser; 1958 1959 // Scrolling feature / quirks detection 1960 var n = $('<div/>') 1961 .css( { 1962 position: 'fixed', 1963 top: 0, 1964 left: -1 * window.pageXOffset, // allow for scrolling 1965 height: 1, 1966 width: 1, 1967 overflow: 'hidden' 1968 } ) 1969 .append( 1970 $('<div/>') 1971 .css( { 1972 position: 'absolute', 1973 top: 1, 1974 left: 1, 1975 width: 100, 1976 overflow: 'scroll' 1977 } ) 1978 .append( 1979 $('<div/>') 1980 .css( { 1981 width: '100%', 1982 height: 10 1983 } ) 1984 ) 1985 ) 1986 .appendTo( 'body' ); 1987 1988 var outer = n.children();
1989 var inner = outer.children(); 1990 1991 // Get scrollbar width 1992 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; 1993 1994 // In rtl text layout, some browsers (most, but not all) will place the 1995 // scrollbar on the left, rather than the right. 1996 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; 1997 1998 n.remove(); 1999 } 2000 2001 $.extend( settings.oBrowser, DataTable.__browser ); 2002 settings.oScroll.iBarWidth = DataTable.__browser.barWidth; 2003 } 2004 2005 /** 2006 * Add a column to the list used for the table with default values 2007 * @param {object} oSettings dataTables settings object 2008 * @memberof DataTable#oApi 2009 */ 2010 function _fnAddColumn( oSettings ) 2011 { 2012 // Add column to aoColumns array 2013 var oDefaults = DataTable.defaults.column; 2014 var iCol = oSettings.aoColumns.length; 2015 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { 2016 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], 2017 "mData": oDefaults.mData ? oDefaults.mData : iCol, 2018 idx: iCol, 2019 searchFixed: {}, 2020 colEl: $('<col>').attr('data-dt-column', iCol) 2021 } ); 2022 oSettings.aoColumns.push( oCol ); 2023 2024 // Add search object for column specific search. Note that the `searchCols[ iCol ]` 2025 // passed into extend can be undefined. This allows the user to give a default 2026 // with only some of the parameters defined, and also not give a default 2027 var searchCols = oSettings.aoPreSearchCols; 2028 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); 2029 } 2030 2031 2032 /** 2033 * Apply options for a column 2034 * @param {object} oSettings dataTables settings object 2035 * @param {int} iCol column index to consider 2036 * @param {object} oOptions object with sType, bVisible and bSearchable etc 2037 * @memberof DataTable#oApi 2038 */ 2039 function _fnColumnOptions( oSettings, iCol, oOptions ) 2040 { 2041 var oCol = oSettings.aoColumns[ iCol ]; 2042 2043 /* User specified column options */ 2044 if ( oOptions !== undefined && oOptions !== null ) 2045 { 2046 // Backwards compatibility 2047 _fnCompatCols( oOptions ); 2048 2049 // Map camel case parameters to their Hungarian counterparts 2050 _fnCamelToHungarian( DataTable.defaults.column, oOptions, true ); 2051 2052 /* Backwards compatibility for mDataProp */ 2053 if ( oOptions.mDataProp !== undefined && !oOptions.mData ) 2054 { 2055 oOptions.mData = oOptions.mDataProp; 2056 } 2057 2058 if ( oOptions.sType ) 2059 { 2060 oCol._sManualType = oOptions.sType; 2061 } 2062 2063 // `class` is a reserved word in Javascript, so we need to provide 2064 // the ability to use a valid name for the camel case input 2065 if ( oOptions.className && ! oOptions.sClass ) 2066 { 2067 oOptions.sClass = oOptions.className; 2068 } 2069 2070 var origClass = oCol.sClass; 2071 2072 $.extend( oCol, oOptions ); 2073 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); 2074 2075 // Merge class from previously defined classes with this one, rather than just 2076 // overwriting it in the extend above 2077 if (origClass !== oCol.sClass) { 2078 oCol.sClass = origClass + ' ' + oCol.sClass; 2079 } 2080 2081 /* iDataSort to be applied (backwards compatibility), but aDataSort will take 2082 * priority if defined 2083 */ 2084 if ( oOptions.iDataSort !== undefined ) 2085 { 2086 oCol.aDataSort = [ oOptions.iDataSort ]; 2087 } 2088 _fnMap( oCol, oOptions, "aDataSort" ); 2089 } 2090 2091 /* Cache the data get and set functions for speed */ 2092 var mDataSrc = oCol.mData; 2093 var mData = _fnGetObjectDataFn( mDataSrc ); 2094 2095 // The `render` option can be given as an array to access the helper rendering methods. 2096 // The first element is the rendering method to use, the rest are the parameters to pass 2097 if ( oCol.mRender && Array.isArray( oCol.mRender ) ) { 2098 var copy = oCol.mRender.slice(); 2099 var name = copy.shift(); 2100 2101 oCol.mRender = DataTable.render[name].apply(window, copy); 2102 } 2103 2104 oCol._render = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; 2105 2106 var attrTest = function( src ) { 2107 return typeof src === 'string' && src.indexOf('@') !== -1; 2108 }; 2109 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( 2110 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) 2111 ); 2112 oCol._setter = null; 2113 2114 oCol.fnGetData = function (rowData, type, meta) { 2115 var innerData = mData( rowData, type, undefined, meta ); 2116 2117 return oCol._render && type ? 2118 oCol._render( innerData, type, rowData, meta ) : 2119 innerData; 2120 }; 2121 oCol.fnSetData = function ( rowData, val, meta ) { 2122 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); 2123 }; 2124 2125 // Indicate if DataTables should read DOM data as an object or array 2126 // Used in _fnGetRowElements 2127 if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) { 2128 oSettings._rowReadObject = true; 2129 } 2130 2131 /* Feature sorting overrides column specific when off */ 2132 if ( !oSettings.oFeatures.bSort ) 2133 { 2134 oCol.bSortable = false; 2135 } 2136 } 2137 2138 2139 /** 2140 * Adjust the table column widths for new data. Note: you would probably want to 2141 * do a redraw after calling this function! 2142 * @param {object} settings dataTables settings object 2143 * @memberof DataTable#oApi 2144 */ 2145 function _fnAdjustColumnSizing ( settings ) 2146 { 2147 _fnCalculateColumnWidths( settings ); 2148 _fnColumnSizes( settings ); 2149 2150 var scroll = settings.oScroll;
2151 if ( scroll.sY !== '' || scroll.sX !== '') { 2152 _fnScrollDraw( settings ); 2153 } 2154 2155 _fnCallbackFire( settings, null, 'column-sizing', [settings] ); 2156 } 2157 2158 /** 2159 * Apply column sizes 2160 * 2161 * @param {*} settings DataTables settings object 2162 */ 2163 function _fnColumnSizes ( settings ) 2164 { 2165 var cols = settings.aoColumns; 2166 2167 for (var i=0 ; i<cols.length ; i++) { 2168 var width = _fnColumnsSumWidth(settings, [i], false, false); 2169 2170 cols[i].colEl.css('width', width); 2171 } 2172 } 2173 2174 2175 /** 2176 * Convert the index of a visible column to the index in the data array (take account 2177 * of hidden columns) 2178 * @param {object} oSettings dataTables settings object 2179 * @param {int} iMatch Visible column index to lookup 2180 * @returns {int} i the data index 2181 * @memberof DataTable#oApi 2182 */ 2183 function _fnVisibleToColumnIndex( oSettings, iMatch ) 2184 { 2185 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2186 2187 return typeof aiVis[iMatch] === 'number' ? 2188 aiVis[iMatch] : 2189 null; 2190 } 2191 2192 2193 /** 2194 * Convert the index of an index in the data array and convert it to the visible 2195 * column index (take account of hidden columns) 2196 * @param {int} iMatch Column index to lookup 2197 * @param {object} oSettings dataTables settings object 2198 * @returns {int} i the data index 2199 * @memberof DataTable#oApi 2200 */ 2201 function _fnColumnIndexToVisible( oSettings, iMatch ) 2202 { 2203 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2204 var iPos = aiVis.indexOf(iMatch); 2205 2206 return iPos !== -1 ? iPos : null; 2207 } 2208 2209 2210 /** 2211 * Get the number of visible columns 2212 * @param {object} oSettings dataTables settings object 2213 * @returns {int} i the number of visible columns 2214 * @memberof DataTable#oApi 2215 */ 2216 function _fnVisbleColumns( settings ) 2217 { 2218 var layout = settings.aoHeader; 2219 var columns = settings.aoColumns; 2220 var vis = 0; 2221 2222 if ( layout.length ) { 2223 for ( var i=0, ien=layout[0].length ; i<ien ; i++ ) { 2224 if ( columns[i].bVisible && $(layout[0][i].cell).css('display') !== 'none' ) { 2225 vis++; 2226 } 2227 } 2228 } 2229 2230 return vis; 2231 } 2232 2233 2234 /** 2235 * Get an array of column indexes that match a given property 2236 * @param {object} oSettings dataTables settings object 2237 * @param {string} sParam Parameter in aoColumns to look for - typically 2238 * bVisible or bSearchable 2239 * @returns {array} Array of indexes with matched properties 2240 * @memberof DataTable#oApi 2241 */ 2242 function _fnGetColumns( oSettings, sParam ) 2243 { 2244 var a = []; 2245 2246 oSettings.aoColumns.map( function(val, i) { 2247 if ( val[sParam] ) { 2248 a.push( i ); 2249 } 2250 } ); 2251 2252 return a; 2253 } 2254 2255 2256 /** 2257 * Calculate the 'type' of a column 2258 * @param {object} settings dataTables settings object 2259 * @memberof DataTable#oApi 2260 */ 2261 function _fnColumnTypes ( settings ) 2262 { 2263 var columns = settings.aoColumns; 2264 var data = settings.aoData; 2265 var types = DataTable.ext.type.detect; 2266 var i, ien, j, jen, k, ken; 2267 var col, detectedType, cache; 2268 2269 // For each column, spin over the 2270 for ( i=0, ien=columns.length ; i<ien ; i++ ) { 2271 col = columns[i]; 2272 cache = []; 2273 2274 if ( ! col.sType && col._sManualType ) { 2275 col.sType = col._sManualType; 2276 } 2277 else if ( ! col.sType ) { 2278 for ( j=0, jen=types.length ; j<jen ; j++ ) { 2279 for ( k=0, ken=data.length ; k<ken ; k++ ) { 2280 2281 if (! data[k]) { 2282 continue; 2283 } 2284 2285 // Use a cache array so we only need to get the type data 2286 // from the formatter once (when using multiple detectors) 2287 if ( cache[k] === undefined ) { 2288 cache[k] = _fnGetCellData( settings, k, i, 'type' ); 2289 } 2290 2291 detectedType = types[j]( cache[k], settings ); 2292 2293 // If null, then this type can't apply to this column, so 2294 // rather than testing all cells, break out. There is an 2295 // exception for the last type which is `html`. We need to 2296 // scan all rows since it is possible to mix string and HTML 2297 // types 2298 if ( ! detectedType && j !== types.length-2 ) { 2299 break; 2300 } 2301 2302 // Only a single match is needed for html type since it is 2303 // bottom of the pile and very similar to string - but it 2304 // must not be empty 2305 if ( detectedType === 'html' && ! _empty(cache[k]) ) { 2306 break; 2307 } 2308 } 2309 2310 // Type is valid for all data points in the column - use this 2311 // type 2312 if ( detectedType ) {
2313 col.sType = detectedType; 2314 break; 2315 } 2316 } 2317 2318 // Fall back - if no type was detected, always use string 2319 if ( ! col.sType ) { 2320 col.sType = 'string'; 2321 } 2322 } 2323 2324 // Set class names for header / footer for auto type classes 2325 var autoClass = _ext.type.className[col.sType]; 2326 2327 if (autoClass) { 2328 _columnAutoClass(settings.aoHeader, i, autoClass); 2329 _columnAutoClass(settings.aoFooter, i, autoClass); 2330 } 2331 2332 var renderer = _ext.type.render[col.sType]; 2333 2334 // This can only happen once! There is no way to remover 2335 // a renderer. After the first time the renderer has 2336 // already been set so createTr will run the renderer itself. 2337 if (renderer && ! col._render) { 2338 col._render = DataTable.util.get(renderer); 2339 2340 _columnAutoRender(settings, i); 2341 } 2342 } 2343 } 2344 2345 /** 2346 * Apply an auto detected renderer to data which doesn't yet have 2347 * a renderer 2348 */ 2349 function _columnAutoRender(settings, colIdx) { 2350 var data = settings.aoData; 2351 2352 for (var i=0 ; i<data.length ; i++) { 2353 if (data[i].nTr) { 2354 // We have to update the display here since there is no 2355 // invalidation check for the data 2356 var display = _fnGetCellData( settings, i, colIdx, 'display' ); 2357 2358 data[i].displayData[colIdx] = display; 2359 _fnWriteCell(data[i].anCells[colIdx], display); 2360 2361 // No need to update sort / filter data since it has 2362 // been invalidated and will be re-read with the 2363 // renderer now applied 2364 } 2365 } 2366 } 2367 2368 /** 2369 * Apply a class name to a column's header cells 2370 */ 2371 function _columnAutoClass(container, colIdx, className) { 2372 container.forEach(function (row) { 2373 if (row[colIdx] && row[colIdx].unique) { 2374 _addClass(row[colIdx].cell, className); 2375 } 2376 }); 2377 } 2378 2379 /** 2380 * Take the column definitions and static columns arrays and calculate how 2381 * they relate to column indexes. The callback function will then apply the 2382 * definition found for a column to a suitable configuration object. 2383 * @param {object} oSettings dataTables settings object 2384 * @param {array} aoColDefs The aoColumnDefs array that is to be applied 2385 * @param {array} aoCols The aoColumns array that defines columns individually 2386 * @param {array} headerLayout Layout for header as it was loaded 2387 * @param {function} fn Callback function - takes two parameters, the calculated 2388 * column index and the definition for that column. 2389 * @memberof DataTable#oApi 2390 */ 2391 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, headerLayout, fn ) 2392 { 2393 var i, iLen, j, jLen, k, kLen, def; 2394 var columns = oSettings.aoColumns; 2395 2396 if ( aoCols ) { 2397 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) { 2398 if (aoCols[i] && aoCols[i].name) { 2399 columns[i].sName = aoCols[i].name; 2400 } 2401 } 2402 } 2403 2404 // Column definitions with aTargets 2405 if ( aoColDefs ) 2406 { 2407 /* Loop over the definitions array - loop in reverse so first instance has priority */ 2408 for ( i=aoColDefs.length-1 ; i>=0 ; i-- ) 2409 { 2410 def = aoColDefs[i]; 2411 2412 /* Each definition can target multiple columns, as it is an array */ 2413 var aTargets = def.target !== undefined 2414 ? def.target 2415 : def.targets !== undefined 2416 ? def.targets 2417 : def.aTargets; 2418 2419 if ( ! Array.isArray( aTargets ) ) 2420 { 2421 aTargets = [ aTargets ]; 2422 } 2423 2424 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ ) 2425 { 2426 var target = aTargets[j]; 2427 2428 if ( typeof target === 'number' && target >= 0 ) 2429 { 2430 /* Add columns that we don't yet know about */ 2431 while( columns.length <= target ) 2432 { 2433 _fnAddColumn( oSettings ); 2434 } 2435 2436 /* Integer, basic index */ 2437 fn( target, def ); 2438 } 2439 else if ( typeof target === 'number' && target < 0 ) 2440 { 2441 /* Negative integer, right to left column counting */ 2442 fn( columns.length+target, def ); 2443 } 2444 else if ( typeof target === 'string' ) 2445 { 2446 for ( k=0, kLen=columns.length ; k<kLen ; k++ ) { 2447 if (target === '_all') { 2448 // Apply to all columns 2449 fn( k, def ); 2450 } 2451 else if (target.indexOf(':name') !== -1) { 2452 // Column selector 2453 if (columns[k].sName === target.replace(':name', '')) { 2454 fn( k, def ); 2455 } 2456 } 2457 else { 2458 // Cell selector 2459 headerLayout.forEach(function (row) { 2460 if (row[k]) { 2461 var cell = $(row[k].cell); 2462 2463 // Legacy support. Note that it means that we don't support 2464 // an element name selector only, since they are treated as
2465 // class names for 1.x compat. 2466 if (target.match(/^[a-z][\w-]*$/i)) { 2467 target = '.' + target; 2468 } 2469 2470 if (cell.is( target )) { 2471 fn( k, def ); 2472 } 2473 } 2474 }); 2475 } 2476 } 2477 } 2478 } 2479 } 2480 } 2481 2482 // Statically defined columns array 2483 if ( aoCols ) { 2484 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) { 2485 fn( i, aoCols[i] ); 2486 } 2487 } 2488 } 2489 2490 2491 /** 2492 * Get the width for a given set of columns 2493 * 2494 * @param {*} settings DataTables settings object 2495 * @param {*} targets Columns - comma separated string or array of numbers 2496 * @param {*} original Use the original width (true) or calculated (false) 2497 * @param {*} incVisible Include visible columns (true) or not (false) 2498 * @returns Combined CSS value 2499 */ 2500 function _fnColumnsSumWidth( settings, targets, original, incVisible ) { 2501 if ( ! Array.isArray( targets ) ) { 2502 targets = _fnColumnsFromHeader( targets ); 2503 } 2504 2505 var sum = 0; 2506 var unit; 2507 var columns = settings.aoColumns; 2508 2509 for ( var i=0, ien=targets.length ; i<ien ; i++ ) { 2510 var column = columns[ targets[i] ]; 2511 var definedWidth = original ? 2512 column.sWidthOrig : 2513 column.sWidth; 2514 2515 if ( ! incVisible && column.bVisible === false ) { 2516 continue; 2517 } 2518 2519 if ( definedWidth === null || definedWidth === undefined ) { 2520 return null; // can't determine a defined width - browser defined 2521 } 2522 else if ( typeof definedWidth === 'number' ) { 2523 unit = 'px'; 2524 sum += definedWidth; 2525 } 2526 else { 2527 var matched = definedWidth.match(/([\d\.]+)([^\d]*)/); 2528 2529 if ( matched ) { 2530 sum += matched[1] * 1; 2531 unit = matched.length === 3 ? 2532 matched[2] : 2533 'px'; 2534 } 2535 } 2536 } 2537 2538 return sum + unit; 2539 } 2540 2541 function _fnColumnsFromHeader( cell ) 2542 { 2543 var attr = $(cell).closest('[data-dt-column]').attr('data-dt-column'); 2544 2545 if ( ! attr ) { 2546 return []; 2547 } 2548 2549 return attr.split(',').map( function (val) { 2550 return val * 1; 2551 } ); 2552 } 2553 /** 2554 * Add a data array to the table, creating DOM node etc. This is the parallel to 2555 * _fnGatherData, but for adding rows from a Javascript source, rather than a 2556 * DOM source. 2557 * @param {object} settings dataTables settings object 2558 * @param {array} data data array to be added 2559 * @param {node} [tr] TR element to add to the table - optional. If not given, 2560 * DataTables will create a row automatically 2561 * @param {array} [tds] Array of TD|TH elements for the row - must be given 2562 * if nTr is. 2563 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed 2564 * @memberof DataTable#oApi 2565 */ 2566 function _fnAddData ( settings, dataIn, tr, tds ) 2567 { 2568 /* Create the object for storing information about this new row */ 2569 var rowIdx = settings.aoData.length; 2570 var rowModel = $.extend( true, {}, DataTable.models.oRow, { 2571 src: tr ? 'dom' : 'data', 2572 idx: rowIdx 2573 } ); 2574 2575 rowModel._aData = dataIn; 2576 settings.aoData.push( rowModel ); 2577 2578 var columns = settings.aoColumns; 2579 2580 for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) 2581 { 2582 // Invalidate the column types as the new data needs to be revalidated 2583 columns[i].sType = null; 2584 } 2585 2586 /* Add to the display array */ 2587 settings.aiDisplayMaster.push( rowIdx ); 2588 2589 var id = settings.rowIdFn( dataIn ); 2590 if ( id !== undefined ) { 2591 settings.aIds[ id ] = rowModel; 2592 } 2593 2594 /* Create the DOM information, or register it if already present */ 2595 if ( tr || ! settings.oFeatures.bDeferRender ) 2596 { 2597 _fnCreateTr( settings, rowIdx, tr, tds ); 2598 } 2599 2600 return rowIdx; 2601 } 2602 2603 2604 /** 2605 * Add one or more TR elements to the table. Generally we'd expect to 2606 * use this for reading data from a DOM sourced table, but it could be 2607 * used for an TR element. Note that if a TR is given, it is used (i.e. 2608 * it is not cloned). 2609 * @param {object} settings dataTables settings object 2610 * @param {array|node|jQuery} trs The TR element(s) to add to the table 2611 * @returns {array} Array of indexes for the added rows 2612 * @memberof DataTable#oApi 2613 */ 2614 function _fnAddTr( settings, trs ) 2615 { 2616 var row; 2617 2618 // Allow an individual node to be passed in 2619 if ( ! (trs instanceof $) ) { 2620 trs = $(trs); 2621 } 2622 2623 return trs.map( function (i, el) { 2624 row = _fnGetRowElements( settings, el ); 2625 return _fnAddData( settings, row.data, el, row.cells ); 2626 } ); 2627 } 2628 2629 2630 /**
2631 * Get the data for a given cell from the internal cache, taking into account data mapping 2632 * @param {object} settings dataTables settings object 2633 * @param {int} rowIdx aoData row id 2634 * @param {int} colIdx Column index 2635 * @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order') 2636 * @returns {*} Cell data 2637 * @memberof DataTable#oApi 2638 */ 2639 function _fnGetCellData( settings, rowIdx, colIdx, type ) 2640 { 2641 if (type === 'search') { 2642 type = 'filter'; 2643 } 2644 else if (type === 'order') { 2645 type = 'sort'; 2646 } 2647 2648 var row = settings.aoData[rowIdx]; 2649 2650 if (! row) { 2651 return undefined; 2652 } 2653 2654 var draw = settings.iDraw; 2655 var col = settings.aoColumns[colIdx]; 2656 var rowData = row._aData; 2657 var defaultContent = col.sDefaultContent; 2658 var cellData = col.fnGetData( rowData, type, { 2659 settings: settings, 2660 row: rowIdx, 2661 col: colIdx 2662 } ); 2663 2664 // Allow for a node being returned for non-display types 2665 if (type !== 'display' && cellData && typeof cellData === 'object' && cellData.nodeName) { 2666 cellData = cellData.innerHTML; 2667 } 2668 2669 if ( cellData === undefined ) { 2670 if ( settings.iDrawError != draw && defaultContent === null ) { 2671 _fnLog( settings, 0, "Requested unknown parameter "+ 2672 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+ 2673 " for row "+rowIdx+", column "+colIdx, 4 ); 2674 settings.iDrawError = draw; 2675 } 2676 return defaultContent; 2677 } 2678 2679 // When the data source is null and a specific data type is requested (i.e. 2680 // not the original data), we can use default column data 2681 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) { 2682 cellData = defaultContent; 2683 } 2684 else if ( typeof cellData === 'function' ) { 2685 // If the data source is a function, then we run it and use the return, 2686 // executing in the scope of the data object (for instances) 2687 return cellData.call( rowData ); 2688 } 2689 2690 if ( cellData === null && type === 'display' ) { 2691 return ''; 2692 } 2693 2694 if ( type === 'filter' ) { 2695 var fomatters = DataTable.ext.type.search; 2696 2697 if ( fomatters[ col.sType ] ) { 2698 cellData = fomatters[ col.sType ]( cellData ); 2699 } 2700 } 2701 2702 return cellData; 2703 } 2704 2705 2706 /** 2707 * Set the value for a specific cell, into the internal data cache 2708 * @param {object} settings dataTables settings object 2709 * @param {int} rowIdx aoData row id 2710 * @param {int} colIdx Column index 2711 * @param {*} val Value to set 2712 * @memberof DataTable#oApi 2713 */ 2714 function _fnSetCellData( settings, rowIdx, colIdx, val ) 2715 { 2716 var col = settings.aoColumns[colIdx]; 2717 var rowData = settings.aoData[rowIdx]._aData; 2718 2719 col.fnSetData( rowData, val, { 2720 settings: settings, 2721 row: rowIdx, 2722 col: colIdx 2723 } ); 2724 } 2725 2726 /** 2727 * Write a value to a cell 2728 * @param {*} td Cell 2729 * @param {*} val Value 2730 */ 2731 function _fnWriteCell(td, val) 2732 { 2733 if (val && typeof val === 'object' && val.nodeName) { 2734 $(td) 2735 .empty() 2736 .append(val); 2737 } 2738 else { 2739 td.innerHTML = val; 2740 } 2741 } 2742 2743 2744 // Private variable that is used to match action syntax in the data property object 2745 var __reArray = /\[.*?\]$/; 2746 var __reFn = /\(\)$/; 2747 2748 /** 2749 * Split string on periods, taking into account escaped periods 2750 * @param {string} str String to split 2751 * @return {array} Split string 2752 */ 2753 function _fnSplitObjNotation( str ) 2754 { 2755 var parts = str.match(/(\\.|[^.])+/g) || ['']; 2756 2757 return parts.map( function ( s ) { 2758 return s.replace(/\\\./g, '.'); 2759 } ); 2760 } 2761 2762 2763 /** 2764 * Return a function that can be used to get data from a source object, taking 2765 * into account the ability to use nested objects as a source 2766 * @param {string|int|function} mSource The data source for the object 2767 * @returns {function} Data get function 2768 * @memberof DataTable#oApi 2769 */ 2770 var _fnGetObjectDataFn = DataTable.util.get; 2771 2772 2773 /** 2774 * Return a function that can be used to set data from a source object, taking 2775 * into account the ability to use nested objects as a source 2776 * @param {string|int|function} mSource The data source for the object 2777 * @returns {function} Data set function 2778 * @memberof DataTable#oApi 2779 */ 2780 var _fnSetObjectDataFn = DataTable.util.set; 2781 2782 2783 /** 2784 * Return an array with the full table data 2785 * @param {object} oSettings dataTables settings object 2786 * @returns array {array} aData Master data array 2787 * @memberof DataTable#oApi 2788 */ 2789 function _fnGetDataMaster ( settings ) 2790 { 2791 return _pluck( settings.aoData, '_aData' ); 2792 } 2793 2794 2795 /** 2796 * Nuke the table 2797 * @param {object} oSettings dataTables settings object 2798 * @memberof DataTable#oApi 2799 */ 2800 function _fnClearTable( settings ) 2801 { 2802 settings.aoData.length = 0; 2803 settings.aiDisplayMaster.length = 0; 2804 settings.aiDisplay.length = 0; 2805 settings.aIds = {}; 2806 } 2807 2808 2809 /**
2810 * Mark cached data as invalid such that a re-read of the data will occur when 2811 * the cached data is next requested. Also update from the data source object. 2812 * 2813 * @param {object} settings DataTables settings object 2814 * @param {int} rowIdx Row index to invalidate 2815 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom' 2816 * or 'data' 2817 * @param {int} [colIdx] Column index to invalidate. If undefined the whole 2818 * row will be invalidated 2819 * @memberof DataTable#oApi 2820 * 2821 * @todo For the modularisation of v1.11 this will need to become a callback, so 2822 * the sort and filter methods can subscribe to it. That will required 2823 * initialisation options for sorting, which is why it is not already baked in 2824 */ 2825 function _fnInvalidate( settings, rowIdx, src, colIdx ) 2826 { 2827 var row = settings.aoData[ rowIdx ]; 2828 var i, ien; 2829 2830 // Remove the cached data for the row 2831 row._aSortData = null; 2832 row._aFilterData = null; 2833 row.displayData = null; 2834 2835 // Are we reading last data from DOM or the data object? 2836 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) { 2837 // Read the data from the DOM 2838 row._aData = _fnGetRowElements( 2839 settings, row, colIdx, colIdx === undefined ? undefined : row._aData 2840 ) 2841 .data; 2842 } 2843 else { 2844 // Reading from data object, update the DOM 2845 var cells = row.anCells; 2846 var display = _fnGetRowDisplay(settings, rowIdx); 2847 2848 if ( cells ) { 2849 if ( colIdx !== undefined ) { 2850 _fnWriteCell(cells[colIdx], display[colIdx]); 2851 } 2852 else { 2853 for ( i=0, ien=cells.length ; i<ien ; i++ ) { 2854 _fnWriteCell(cells[i], display[i]); 2855 } 2856 } 2857 } 2858 } 2859 2860 // Column specific invalidation 2861 var cols = settings.aoColumns; 2862 if ( colIdx !== undefined ) { 2863 // Type - the data might have changed 2864 cols[ colIdx ].sType = null; 2865 2866 // Max length string. Its a fairly cheep recalculation, so not worth 2867 // something more complicated 2868 cols[ colIdx ].maxLenString = null; 2869 } 2870 else { 2871 for ( i=0, ien=cols.length ; i<ien ; i++ ) { 2872 cols[i].sType = null; 2873 cols[i].maxLenString = null; 2874 } 2875 2876 // Update DataTables special `DT_*` attributes for the row 2877 _fnRowAttributes( settings, row ); 2878 } 2879 } 2880 2881 2882 /** 2883 * Build a data source object from an HTML row, reading the contents of the 2884 * cells that are in the row. 2885 * 2886 * @param {object} settings DataTables settings object 2887 * @param {node|object} TR element from which to read data or existing row 2888 * object from which to re-read the data from the cells 2889 * @param {int} [colIdx] Optional column index 2890 * @param {array|object} [d] Data source object. If `colIdx` is given then this 2891 * parameter should also be given and will be used to write the data into. 2892 * Only the column in question will be written 2893 * @returns {object} Object with two parameters: `data` the data read, in 2894 * document order, and `cells` and array of nodes (they can be useful to the 2895 * caller, so rather than needing a second traversal to get them, just return 2896 * them from here). 2897 * @memberof DataTable#oApi 2898 */ 2899 function _fnGetRowElements( settings, row, colIdx, d ) 2900 { 2901 var 2902 tds = [], 2903 td = row.firstChild, 2904 name, col, i=0, contents, 2905 columns = settings.aoColumns, 2906 objectRead = settings._rowReadObject; 2907 2908 // Allow the data object to be passed in, or construct 2909 d = d !== undefined ? 2910 d : 2911 objectRead ? 2912 {} : 2913 []; 2914 2915 var attr = function ( str, td ) { 2916 if ( typeof str === 'string' ) { 2917 var idx = str.indexOf('@'); 2918 2919 if ( idx !== -1 ) { 2920 var attr = str.substring( idx+1 ); 2921 var setter = _fnSetObjectDataFn( str ); 2922 setter( d, td.getAttribute( attr ) ); 2923 } 2924 } 2925 }; 2926 2927 // Read data from a cell and store into the data object 2928 var cellProcess = function ( cell ) { 2929 if ( colIdx === undefined || colIdx === i ) { 2930 col = columns[i]; 2931 contents = (cell.innerHTML).trim(); 2932 2933 if ( col && col._bAttrSrc ) { 2934 var setter = _fnSetObjectDataFn( col.mData._ ); 2935 setter( d, contents ); 2936 2937 attr( col.mData.sort, cell ); 2938 attr( col.mData.type, cell ); 2939 attr( col.mData.filter, cell ); 2940 } 2941 else { 2942 // Depending on the `data` option for the columns the data can 2943 // be read to either an object or an array. 2944 if ( objectRead ) { 2945 if ( ! col._setter ) { 2946 // Cache the setter function 2947 col._setter = _fnSetObjectDataFn( col.mData ); 2948 } 2949 col._setter( d, contents ); 2950 } 2951 else { 2952 d[i] = contents; 2953 } 2954 } 2955 } 2956 2957 i++; 2958 }; 2959 2960 if ( td ) { 2961 // `tr` element was passed in 2962 while ( td ) { 2963 name = td.nodeName.toUpperCase(); 2964
2965 if ( name == "TD" || name == "TH" ) { 2966 cellProcess( td ); 2967 tds.push( td ); 2968 } 2969 2970 td = td.nextSibling; 2971 } 2972 } 2973 else { 2974 // Existing row object passed in 2975 tds = row.anCells; 2976 2977 for ( var j=0, jen=tds.length ; j<jen ; j++ ) { 2978 cellProcess( tds[j] ); 2979 } 2980 } 2981 2982 // Read the ID from the DOM if present 2983 var rowNode = row.firstChild ? row : row.nTr; 2984 2985 if ( rowNode ) { 2986 var id = rowNode.getAttribute( 'id' ); 2987 2988 if ( id ) { 2989 _fnSetObjectDataFn( settings.rowId )( d, id ); 2990 } 2991 } 2992 2993 return { 2994 data: d, 2995 cells: tds 2996 }; 2997 } 2998 2999 /** 3000 * Render and cache a row's display data for the columns, if required 3001 * @returns 3002 */ 3003 function _fnGetRowDisplay (settings, rowIdx) { 3004 let rowModal = settings.aoData[rowIdx]; 3005 let columns = settings.aoColumns; 3006 3007 if (! rowModal.displayData) { 3008 // Need to render and cache 3009 rowModal.displayData = []; 3010 3011 for ( var colIdx=0, len=columns.length ; colIdx<len ; colIdx++ ) { 3012 rowModal.displayData.push( 3013 _fnGetCellData( settings, rowIdx, colIdx, 'display' ) 3014 ); 3015 } 3016 } 3017 3018 return rowModal.displayData; 3019 } 3020 3021 /** 3022 * Create a new TR element (and it's TD children) for a row 3023 * @param {object} oSettings dataTables settings object 3024 * @param {int} iRow Row to consider 3025 * @param {node} [nTrIn] TR element to add to the table - optional. If not given, 3026 * DataTables will create a row automatically 3027 * @param {array} [anTds] Array of TD|TH elements for the row - must be given 3028 * if nTr is. 3029 * @memberof DataTable#oApi 3030 */ 3031 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds ) 3032 { 3033 var 3034 row = oSettings.aoData[iRow], 3035 rowData = row._aData, 3036 cells = [], 3037 nTr, nTd, oCol, 3038 i, iLen, create, 3039 trClass = oSettings.oClasses.tbody.row; 3040 3041 if ( row.nTr === null ) 3042 { 3043 nTr = nTrIn || document.createElement('tr'); 3044 3045 row.nTr = nTr; 3046 row.anCells = cells; 3047 3048 _addClass(nTr, trClass); 3049 3050 /* Use a private property on the node to allow reserve mapping from the node 3051 * to the aoData array for fast look up 3052 */ 3053 nTr._DT_RowIndex = iRow; 3054 3055 /* Special parameters can be given by the data source to be used on the row */ 3056 _fnRowAttributes( oSettings, row ); 3057 3058 /* Process each column */ 3059 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 3060 { 3061 oCol = oSettings.aoColumns[i]; 3062 create = nTrIn && anTds[i] ? false : true; 3063 3064 nTd = create ? document.createElement( oCol.sCellType ) : anTds[i]; 3065 3066 if (! nTd) { 3067 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 3068 } 3069 3070 nTd._DT_CellIndex = { 3071 row: iRow, 3072 column: i 3073 }; 3074 3075 cells.push( nTd ); 3076 3077 var display = _fnGetRowDisplay(oSettings, iRow); 3078 3079 // Need to create the HTML if new, or if a rendering function is defined 3080 if ( 3081 create || 3082 ( 3083 (oCol.mRender || oCol.mData !== i) && 3084 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display') 3085 ) 3086 ) { 3087 _fnWriteCell(nTd, display[i]); 3088 } 3089 3090 // Visibility - add or remove as required 3091 if ( oCol.bVisible && create ) 3092 { 3093 nTr.appendChild( nTd ); 3094 } 3095 else if ( ! oCol.bVisible && ! create ) 3096 { 3097 nTd.parentNode.removeChild( nTd ); 3098 } 3099 3100 if ( oCol.fnCreatedCell ) 3101 { 3102 oCol.fnCreatedCell.call( oSettings.oInstance, 3103 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i 3104 ); 3105 } 3106 } 3107 3108 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', 'row-created', [nTr, rowData, iRow, cells] ); 3109 } 3110 else { 3111 _addClass(row.nTr, trClass); 3112 } 3113 } 3114 3115 3116 /** 3117 * Add attributes to a row based on the special `DT_*` parameters in a data 3118 * source object. 3119 * @param {object} settings DataTables settings object 3120 * @param {object} DataTables row object for the row to be modified 3121 * @memberof DataTable#oApi 3122 */ 3123 function _fnRowAttributes( settings, row ) 3124 { 3125 var tr = row.nTr; 3126 var data = row._aData; 3127 3128 if ( tr ) { 3129 var id = settings.rowIdFn( data ); 3130 3131 if ( id ) { 3132 tr.id = id; 3133 } 3134 3135 if ( data.DT_RowClass ) { 3136 // Remove any classes added by DT_RowClass before 3137 var a = data.DT_RowClass.split(' '); 3138 row.__rowc = row.__rowc ? 3139 _unique( row.__rowc.concat( a ) ) : 3140 a; 3141 3142 $(tr) 3143 .removeClass( row.__rowc.join(' ') ) 3144 .addClass( data.DT_RowClass ); 3145 } 3146 3147 if ( data.DT_RowAttr ) { 3148 $(tr).attr( data.DT_RowAttr ); 3149 } 3150 3151 if ( data.DT_RowData ) { 3152 $(tr).data( data.DT_RowData ); 3153 } 3154 } 3155 } 3156 3157 3158 /** 3159 * Create the HTML header for the table 3160 * @param {object} oSettings dataTables settings object 3161 * @memberof DataTable#oApi 3162 */ 3163 function _fnBuildHead( settings, side ) 3164 { 3165 var classes = settings.oClasses; 3166 var columns = settings.aoColumns; 3167 var i, ien, row; 3168 var target = side === 'header' 3169 ? settings.nTHead 3170 : settings.nTFoot; 3171 var titleProp = side === 'header' ? 'sTitle' : side; 3172 3173 // Footer might be defined 3174 if (! target) { 3175 return; 3176 } 3177 3178 // If no cells yet and we have content for them, then create 3179 if (side === 'header' || _pluck(settings.aoColumns, titleProp).join('')) { 3180 row = $('tr', target); 3181 3182 // Add a row if needed 3183 if (! row.length) { 3184 row = $('<tr/>').appendTo(target) 3185 } 3186 3187 // Add the number of cells needed to make up to the number of columns 3188 if (row.length === 1) { 3189 var cells = $('td, th', row); 3190 3191 for ( i=cells.length, ien=columns.length ; i<ien ; i++ ) { 3192 $('<th/>') 3193 .html( columns[i][titleProp] || '' ) 3194 .appendTo( row ); 3195 } 3196 } 3197 } 3198 3199 var detected = _fnDetectHeader( settings, target, true ); 3200 3201 if (side === 'header') { 3202 settings.aoHeader = detected; 3203 } 3204 else { 3205 settings.aoFooter = detected; 3206 } 3207 3208 // ARIA role for the rows 3209 $(target).children('tr').attr('role', 'row'); 3210 3211 // Every cell needs to be passed through the renderer 3212 $(target).children('tr').children('th, td') 3213 .each( function () { 3214 _fnRenderer( settings, side )( 3215 settings, $(this), classes 3216 ); 3217 } ); 3218 } 3219 3220 /** 3221 * Build a layout structure for a header or footer 3222 * 3223 * @param {*} settings DataTables settings 3224 * @param {*} source Source layout array 3225 * @param {*} incColumns What columns should be included 3226 * @returns Layout array 3227 */ 3228 function _fnHeaderLayout( settings, source, incColumns ) 3229 {
3230 var row, column, cell; 3231 var local = []; 3232 var structure = []; 3233 var columns = settings.aoColumns; 3234 var columnCount = columns.length; 3235 var rowspan, colspan; 3236 3237 if ( ! source ) { 3238 return; 3239 } 3240 3241 // Default is to work on only visible columns 3242 if ( ! incColumns ) { 3243 incColumns = _range(columnCount) 3244 .filter(function (idx) { 3245 return columns[idx].bVisible; 3246 }); 3247 } 3248 3249 // Make a copy of the master layout array, but with only the columns we want 3250 for ( row=0 ; row<source.length ; row++ ) { 3251 // Remove any columns we haven't selected 3252 local[row] = source[row].slice().filter(function (cell, i) { 3253 return incColumns.includes(i); 3254 }); 3255 3256 // Prep the structure array - it needs an element for each row 3257 structure.push( [] ); 3258 } 3259 3260 for ( row=0 ; row<local.length ; row++ ) { 3261 for ( column=0 ; column<local[row].length ; column++ ) { 3262 rowspan = 1; 3263 colspan = 1; 3264 3265 // Check to see if there is already a cell (row/colspan) covering our target 3266 // insert point. If there is, then there is nothing to do. 3267 if ( structure[row][column] === undefined ) { 3268 cell = local[row][column].cell; 3269 3270 // Expand for rowspan 3271 while ( 3272 local[row+rowspan] !== undefined && 3273 local[row][column].cell == local[row+rowspan][column].cell 3274 ) { 3275 structure[row+rowspan][column] = null; 3276 rowspan++; 3277 } 3278 3279 // And for colspan 3280 while ( 3281 local[row][column+colspan] !== undefined && 3282 local[row][column].cell == local[row][column+colspan].cell 3283 ) { 3284 // Which also needs to go over rows 3285 for ( var k=0 ; k<rowspan ; k++ ) { 3286 structure[row+k][column+colspan] = null; 3287 } 3288 3289 colspan++; 3290 } 3291 3292 var titleSpan = $('span.dt-column-title', cell); 3293 3294 structure[row][column] = { 3295 cell: cell, 3296 colspan: colspan, 3297 rowspan: rowspan, 3298 title: titleSpan.length 3299 ? titleSpan.html() 3300 : $(cell).html() 3301 }; 3302 } 3303 } 3304 } 3305 3306 return structure; 3307 } 3308 3309 3310 /** 3311 * Draw the header (or footer) element based on the column visibility states. 3312 * 3313 * @param object oSettings dataTables settings object 3314 * @param array aoSource Layout array from _fnDetectHeader 3315 * @memberof DataTable#oApi 3316 */ 3317 function _fnDrawHead( settings, source ) 3318 { 3319 var layout = _fnHeaderLayout(settings, source); 3320 var tr, n; 3321 3322 for ( var row=0 ; row<source.length ; row++ ) { 3323 tr = source[row].row; 3324 3325 // All cells are going to be replaced, so empty out the row 3326 // Can't use $().empty() as that kills event handlers 3327 if (tr) { 3328 while( (n = tr.firstChild) ) { 3329 tr.removeChild( n ); 3330 } 3331 } 3332 3333 for ( var column=0 ; column<layout[row].length ; column++ ) { 3334 var point = layout[row][column]; 3335 3336 if (point) { 3337 $(point.cell) 3338 .appendTo(tr) 3339 .attr('rowspan', point.rowspan) 3340 .attr('colspan', point.colspan); 3341 } 3342 } 3343 } 3344 } 3345 3346 3347 /** 3348 * Insert the required TR nodes into the table for display 3349 * @param {object} oSettings dataTables settings object 3350 * @param ajaxComplete true after ajax call to complete rendering 3351 * @memberof DataTable#oApi 3352 */ 3353 function _fnDraw( oSettings, ajaxComplete ) 3354 { 3355 // Allow for state saving and a custom start position 3356 _fnStart( oSettings ); 3357 3358 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */ 3359 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] ); 3360 if ( aPreDraw.indexOf(false) !== -1 ) 3361 { 3362 _fnProcessingDisplay( oSettings, false ); 3363 return; 3364 } 3365 3366 var anRows = []; 3367 var iRowCount = 0; 3368 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3369 var aiDisplay = oSettings.aiDisplay; 3370 var iDisplayStart = oSettings._iDisplayStart; 3371 var iDisplayEnd = oSettings.fnDisplayEnd(); 3372 var columns = oSettings.aoColumns; 3373 var body = $(oSettings.nTBody); 3374 3375 oSettings.bDrawing = true; 3376 3377 /* Server-side processing draw intercept */ 3378 if ( !bServerSide ) 3379 { 3380 oSettings.iDraw++; 3381 } 3382 else if ( !oSettings.bDestroying && !ajaxComplete) 3383 { 3384 // Show loading message for server-side processing 3385 if (oSettings.iDraw === 0) {
3386 body.empty().append(_emptyRow(oSettings)); 3387 } 3388 3389 _fnAjaxUpdate( oSettings ); 3390 return; 3391 } 3392 3393 if ( aiDisplay.length !== 0 ) 3394 { 3395 var iStart = bServerSide ? 0 : iDisplayStart; 3396 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd; 3397 3398 for ( var j=iStart ; j<iEnd ; j++ ) 3399 { 3400 var iDataIndex = aiDisplay[j]; 3401 var aoData = oSettings.aoData[ iDataIndex ]; 3402 if ( aoData.nTr === null ) 3403 { 3404 _fnCreateTr( oSettings, iDataIndex ); 3405 } 3406 3407 var nRow = aoData.nTr; 3408 3409 // Add various classes as needed 3410 for (var i=0 ; i<columns.length ; i++) { 3411 var col = columns[i]; 3412 var td = aoData.anCells[i]; 3413 3414 _addClass(td, _ext.type.className[col.sType]); // auto class 3415 _addClass(td, col.sClass); // column class 3416 _addClass(td, oSettings.oClasses.tbody.cell); // all cells 3417 } 3418 3419 // Row callback functions - might want to manipulate the row 3420 // iRowCount and j are not currently documented. Are they at all 3421 // useful? 3422 _fnCallbackFire( oSettings, 'aoRowCallback', null, 3423 [nRow, aoData._aData, iRowCount, j, iDataIndex] ); 3424 3425 anRows.push( nRow ); 3426 iRowCount++; 3427 } 3428 } 3429 else 3430 { 3431 anRows[ 0 ] = _emptyRow(oSettings); 3432 } 3433 3434 /* Header and footer callbacks */ 3435 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], 3436 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3437 3438 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], 3439 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3440 3441 // replaceChildren is faster, but only became widespread in 2020, 3442 // so a fall back in jQuery is provided for older browsers. 3443 if (body[0].replaceChildren) { 3444 body[0].replaceChildren.apply(body[0], anRows); 3445 } 3446 else { 3447 body.children().detach(); 3448 body.append( $(anRows) ); 3449 } 3450 3451 // Empty table needs a specific class 3452 $(oSettings.nTableWrapper).toggleClass('dt-empty-footer', $('tr', oSettings.nTFoot).length === 0); 3453 3454 /* Call all required callback functions for the end of a draw */ 3455 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings], true ); 3456 3457 /* Draw is complete, sorting and filtering must be as well */ 3458 oSettings.bSorted = false; 3459 oSettings.bFiltered = false; 3460 oSettings.bDrawing = false; 3461 } 3462 3463 3464 /** 3465 * Redraw the table - taking account of the various features which are enabled 3466 * @param {object} oSettings dataTables settings object 3467 * @param {boolean} [holdPosition] Keep the current paging position. By default 3468 * the paging is reset to the first page 3469 * @memberof DataTable#oApi 3470 */ 3471 function _fnReDraw( settings, holdPosition, recompute ) 3472 { 3473 var 3474 features = settings.oFeatures, 3475 sort = features.bSort, 3476 filter = features.bFilter; 3477 3478 if (recompute === undefined || recompute === true) { 3479 if ( sort ) { 3480 _fnSort( settings ); 3481 } 3482 3483 if ( filter ) { 3484 _fnFilterComplete( settings, settings.oPreviousSearch ); 3485 } 3486 else { 3487 // No filtering, so we want to just use the display master 3488 settings.aiDisplay = settings.aiDisplayMaster.slice(); 3489 } 3490 } 3491 3492 if ( holdPosition !== true ) { 3493 settings._iDisplayStart = 0; 3494 } 3495 3496 // Let any modules know about the draw hold position state (used by 3497 // scrolling internally) 3498 settings._drawHold = holdPosition; 3499 3500 _fnDraw( settings ); 3501 3502 settings._drawHold = false; 3503 } 3504 3505 3506 /* 3507 * Table is empty - create a row with an empty message in it 3508 */ 3509 function _emptyRow ( settings ) { 3510 var oLang = settings.oLanguage; 3511 var zero = oLang.sZeroRecords; 3512 var dataSrc = _fnDataSource( settings ); 3513 3514 if ( 3515 (settings.iDraw < 1 && dataSrc === 'ssp') || 3516 (settings.iDraw <= 1 && dataSrc === 'ajax') 3517 ) { 3518 zero = oLang.sLoadingRecords; 3519 } 3520 else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 ) 3521 { 3522 zero = oLang.sEmptyTable; 3523 } 3524 3525 return $( '<tr/>' ) 3526 .append( $('<td />', { 3527 'colSpan': _fnVisbleColumns( settings ), 3528 'class': settings.oClasses.empty.row 3529 } ).html( zero ) )[0]; 3530 } 3531 3532 3533 /** 3534 * Convert a `layout` object given by a user to the object structure needed 3535 * for the renderer. This is done twice, once for above and once for below
3536 * the table. Ordering must also be considered. 3537 * 3538 * @param {*} settings DataTables settings object 3539 * @param {*} layout Layout object to convert 3540 * @param {string} side `top` or `bottom` 3541 * @returns Converted array structure - one item for each row. 3542 */ 3543 function _layoutArray ( settings, layout, side ) 3544 { 3545 var groups = {}; 3546 3547 // Combine into like groups (e.g. `top`, `top2`, etc) 3548 $.each( layout, function ( pos, val ) { 3549 if (val === null) { 3550 return; 3551 } 3552 3553 var splitPos = pos.replace(/([A-Z])/g, ' $1').split(' '); 3554 3555 if ( ! groups[ splitPos[0] ] ) { 3556 groups[ splitPos[0] ] = {}; 3557 } 3558 3559 var align = splitPos.length === 1 ? 3560 'full' : 3561 splitPos[1].toLowerCase(); 3562 var group = groups[ splitPos[0] ]; 3563 var groupRun = function (contents, innerVal) { 3564 // If it is an object, then there can be multiple features contained in it 3565 if ( $.isPlainObject( innerVal ) ) { 3566 Object.keys(innerVal).map(function (key) { 3567 contents.push( { 3568 feature: key, 3569 opts: innerVal[key] 3570 }); 3571 }); 3572 } 3573 else { 3574 contents.push(innerVal); 3575 } 3576 } 3577 3578 // Transform to an object with a contents property 3579 if (! group[align] || ! group[align].contents) { 3580 group[align] = { contents: [] }; 3581 } 3582 3583 // Allow for an array or just a single object 3584 if ( Array.isArray(val)) { 3585 for (var i=0 ; i<val.length ; i++) { 3586 groupRun(group[align].contents, val[i]); 3587 } 3588 } 3589 else { 3590 groupRun(group[ align ].contents, val); 3591 } 3592 3593 // And make contents an array 3594 if ( ! Array.isArray( group[ align ].contents ) ) { 3595 group[ align ].contents = [ group[ align ].contents ]; 3596 } 3597 } ); 3598 3599 var filtered = Object.keys(groups) 3600 .map( function ( pos ) { 3601 // Filter to only the side we need 3602 if ( pos.indexOf(side) !== 0 ) { 3603 return null; 3604 } 3605 3606 return { 3607 name: pos, 3608 val: groups[pos] 3609 }; 3610 } ) 3611 .filter( function (item) { 3612 return item !== null; 3613 }); 3614 3615 // Order by item identifier 3616 filtered.sort( function ( a, b ) { 3617 var order1 = a.name.replace(/[^0-9]/g, '') * 1; 3618 var order2 = b.name.replace(/[^0-9]/g, '') * 1; 3619 3620 return order2 - order1; 3621 } ); 3622 3623 if ( side === 'bottom' ) { 3624 filtered.reverse(); 3625 } 3626 3627 // Split into rows 3628 var rows = []; 3629 for ( var i=0, ien=filtered.length ; i<ien ; i++ ) { 3630 if ( filtered[i].val.full ) { 3631 rows.push( { full: filtered[i].val.full } ); 3632 _layoutResolve( settings, rows[ rows.length - 1 ] ); 3633 3634 delete filtered[i].val.full; 3635 } 3636 3637 if ( Object.keys(filtered[i].val).length ) { 3638 rows.push( filtered[i].val ); 3639 _layoutResolve( settings, rows[ rows.length - 1 ] ); 3640 } 3641 } 3642 3643 return rows; 3644 } 3645 3646 3647 /** 3648 * Convert the contents of a row's layout object to nodes that can be inserted 3649 * into the document by a renderer. Execute functions, look up plug-ins, etc. 3650 * 3651 * @param {*} settings DataTables settings object 3652 * @param {*} row Layout object for this row 3653 */ 3654 function _layoutResolve( settings, row ) { 3655 var getFeature = function (feature, opts) { 3656 if ( ! _ext.features[ feature ] ) { 3657 _fnLog( settings, 0, 'Unknown feature: '+ feature ); 3658 } 3659 3660 return _ext.features[ feature ].apply( this, [settings, opts] ); 3661 }; 3662 3663 var resolve = function ( item ) { 3664 var line = row[ item ].contents; 3665 3666 for ( var i=0, ien=line.length ; i<ien ; i++ ) { 3667 if ( ! line[i] ) { 3668 continue; 3669 } 3670 else if ( typeof line[i] === 'string' ) { 3671 line[i] = getFeature( line[i], null ); 3672 } 3673 else if ( $.isPlainObject(line[i]) ) { 3674 // If it's an object, it just has feature and opts properties from 3675 // the transform in _layoutArray 3676 line[i] = getFeature(line[i].feature, line[i].opts); 3677 } 3678 else if ( typeof line[i].node === 'function' ) { 3679 line[i] = line[i].node( settings ); 3680 } 3681 else if ( typeof line[i] === 'function' ) { 3682 var inst = line[i]( settings ); 3683 3684 line[i] = typeof inst.node === 'function' ? 3685 inst.node() : 3686 inst; 3687 } 3688 } 3689 }; 3690 3691 $.each( row, function ( key ) { 3692 resolve( key ); 3693 } ); 3694 } 3695 3696 3697 /** 3698 * Add the options to the page HTML for the table 3699 * @param {object} settings DataTables settings object 3700 * @memberof DataTable#oApi 3701 */ 3702 function _fnAddOptionsHtml ( settings ) 3703 { 3704 var classes = settings.oClasses; 3705 var table = $(settings.nTable); 3706 3707 // Wrapper div around everything DataTables controls
3708 var insert = $('<div/>') 3709 .attr({ 3710 id: settings.sTableId+'_wrapper', 3711 'class': classes.container 3712 }) 3713 .insertBefore(table); 3714 3715 settings.nTableWrapper = insert[0]; 3716 3717 if (settings.sDom) { 3718 // Legacy 3719 _fnLayoutDom(settings, settings.sDom, insert); 3720 } 3721 else { 3722 var top = _layoutArray( settings, settings.layout, 'top' ); 3723 var bottom = _layoutArray( settings, settings.layout, 'bottom' ); 3724 var renderer = _fnRenderer( settings, 'layout' ); 3725 3726 // Everything above - the renderer will actually insert the contents into the document 3727 top.forEach(function (item) { 3728 renderer( settings, insert, item ); 3729 }); 3730 3731 // The table - always the center of attention 3732 renderer( settings, insert, { 3733 full: { 3734 table: true, 3735 contents: [ _fnFeatureHtmlTable(settings) ] 3736 } 3737 } ); 3738 3739 // Everything below 3740 bottom.forEach(function (item) { 3741 renderer( settings, insert, item ); 3742 }); 3743 } 3744 3745 // Processing floats on top, so it isn't an inserted feature 3746 _processingHtml( settings ); 3747 } 3748 3749 /** 3750 * Draw the table with the legacy DOM property 3751 * @param {*} settings DT settings object 3752 * @param {*} dom DOM string 3753 * @param {*} insert Insert point 3754 */ 3755 function _fnLayoutDom( settings, dom, insert ) 3756 { 3757 var parts = dom.match(/(".*?")|('.*?')|./g); 3758 var featureNode, option, newNode, next, attr; 3759 3760 for ( var i=0 ; i<parts.length ; i++ ) { 3761 featureNode = null; 3762 option = parts[i]; 3763 3764 if ( option == '<' ) { 3765 // New container div 3766 newNode = $('<div/>'); 3767 3768 // Check to see if we should append an id and/or a class name to the container 3769 next = parts[i+1]; 3770 3771 if ( next[0] == "'" || next[0] == '"' ) { 3772 attr = next.replace(/['"]/g, ''); 3773 3774 var id = '', className; 3775 3776 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic 3777 * breaks the string into parts and applies them as needed 3778 */ 3779 if ( attr.indexOf('.') != -1 ) { 3780 var split = attr.split('.'); 3781 3782 id = split[0]; 3783 className = split[1]; 3784 } 3785 else if ( attr[0] == "#" ) { 3786 id = attr; 3787 } 3788 else { 3789 className = attr; 3790 } 3791 3792 newNode 3793 .attr('id', id.substring(1)) 3794 .addClass(className); 3795 3796 i++; // Move along the position array 3797 } 3798 3799 insert.append( newNode ); 3800 insert = newNode; 3801 } 3802 else if ( option == '>' ) { 3803 // End container div 3804 insert = insert.parent(); 3805 } 3806 else if ( option == 't' ) { 3807 // Table 3808 featureNode = _fnFeatureHtmlTable( settings ); 3809 } 3810 else 3811 { 3812 DataTable.ext.feature.forEach(function(feature) { 3813 if ( option == feature.cFeature ) { 3814 featureNode = feature.fnInit( settings ); 3815 } 3816 }); 3817 } 3818 3819 // Add to the display 3820 if ( featureNode ) { 3821 insert.append( featureNode ); 3822 } 3823 } 3824 } 3825 3826 3827 /** 3828 * Use the DOM source to create up an array of header cells. The idea here is to 3829 * create a layout grid (array) of rows x columns, which contains a reference 3830 * to the cell that that point in the grid (regardless of col/rowspan), such that 3831 * any column / row could be removed and the new grid constructed 3832 * @param {node} thead The header/footer element for the table 3833 * @returns {array} Calculated layout array 3834 * @memberof DataTable#oApi 3835 */ 3836 function _fnDetectHeader ( settings, thead, write ) 3837 { 3838 var columns = settings.aoColumns; 3839 var rows = $(thead).children('tr'); 3840 var row, cell; 3841 var i, k, l, iLen, shifted, column, colspan, rowspan; 3842 var isHeader = thead && thead.nodeName.toLowerCase() === 'thead'; 3843 var layout = []; 3844 var unique; 3845 var shift = function ( a, i, j ) { 3846 var k = a[i]; 3847 while ( k[j] ) { 3848 j++; 3849 } 3850 return j; 3851 }; 3852 3853 // We know how many rows there are in the layout - so prep it 3854 for ( i=0, iLen=rows.length ; i<iLen ; i++ ) { 3855 layout.push( [] ); 3856 } 3857 3858 for ( i=0, iLen=rows.length ; i<iLen ; i++ ) { 3859 row = rows[i]; 3860 column = 0; 3861 3862 // For every cell in the row.. 3863 cell = row.firstChild; 3864 while ( cell ) { 3865 if ( 3866 cell.nodeName.toUpperCase() == 'TD' || 3867 cell.nodeName.toUpperCase() == 'TH' 3868 ) { 3869 var cols = []; 3870 3871 // Get the col and rowspan attributes from the DOM and sanitise them 3872 colspan = cell.getAttribute('colspan') * 1; 3873 rowspan = cell.getAttribute('rowspan') * 1; 3874 colspan = (!colspan || colspan===0 || colspan===1) ? 1 : colspan; 3875 rowspan = (!rowspan || rowspan===0 || rowspan===1) ? 1 : rows
3875pan; 3876 3877 // There might be colspan cells already in this row, so shift our target 3878 // accordingly 3879 shifted = shift( layout, i, column ); 3880 3881 // Cache calculation for unique columns 3882 unique = colspan === 1 ? 3883 true : 3884 false; 3885 3886 // Perform header setup 3887 if ( write ) { 3888 if (unique) { 3889 // Allow column options to be set from HTML attributes 3890 _fnColumnOptions( settings, shifted, $(cell).data() ); 3891 3892 // Get the width for the column. This can be defined from the 3893 // width attribute, style attribute or `columns.width` option 3894 var columnDef = columns[shifted]; 3895 var width = cell.getAttribute('width') || null; 3896 var t = cell.style.width.match(/width:\s*(\d+[pxem%]+)/); 3897 if ( t ) { 3898 width = t[1]; 3899 } 3900 3901 columnDef.sWidthOrig = columnDef.sWidth || width; 3902 3903 if (isHeader) { 3904 // Column title handling - can be user set, or read from the DOM 3905 // This happens before the render, so the original is still in place 3906 if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) { 3907 cell.innerHTML = columnDef.sTitle; 3908 } 3909 3910 if (! columnDef.sTitle && unique) { 3911 columnDef.sTitle = _stripHtml(cell.innerHTML); 3912 columnDef.autoTitle = true; 3913 } 3914 } 3915 else { 3916 // Footer specific operations 3917 if (columnDef.footer) { 3918 cell.innerHTML = columnDef.footer; 3919 } 3920 } 3921 3922 // Fall back to the aria-label attribute on the table header if no ariaTitle is 3923 // provided. 3924 if (! columnDef.ariaTitle) { 3925 columnDef.ariaTitle = $(cell).attr("aria-label") || columnDef.sTitle; 3926 } 3927 3928 // Column specific class names 3929 if ( columnDef.className ) { 3930 $(cell).addClass( columnDef.className ); 3931 } 3932 } 3933 3934 // Wrap the column title so we can write to it in future 3935 if ( $('span.dt-column-title', cell).length === 0) { 3936 $('<span>') 3937 .addClass('dt-column-title') 3938 .append(cell.childNodes) 3939 .appendTo(cell); 3940 } 3941 3942 if ( isHeader && $('span.dt-column-order', cell).length === 0) { 3943 $('<span>') 3944 .addClass('dt-column-order') 3945 .appendTo(cell); 3946 } 3947 } 3948 3949 // If there is col / rowspan, copy the information into the layout grid 3950 for ( l=0 ; l<colspan ; l++ ) { 3951 for ( k=0 ; k<rowspan ; k++ ) { 3952 layout[i+k][shifted+l] = { 3953 cell: cell, 3954 unique: unique 3955 }; 3956 3957 layout[i+k].row = row; 3958 } 3959 3960 cols.push( shifted+l ); 3961 } 3962 3963 // Assign an attribute so spanning cells can still be identified 3964 // as belonging to a column 3965 cell.setAttribute('data-dt-column', _unique(cols).join(',')); 3966 } 3967 3968 cell = cell.nextSibling; 3969 } 3970 } 3971 3972 return layout; 3973 } 3974 3975 /** 3976 * Set the start position for draw 3977 * @param {object} oSettings dataTables settings object 3978 */ 3979 function _fnStart( oSettings ) 3980 { 3981 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3982 var iInitDisplayStart = oSettings.iInitDisplayStart; 3983 3984 // Check and see if we have an initial draw position from state saving 3985 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 ) 3986 { 3987 oSettings._iDisplayStart = bServerSide ? 3988 iInitDisplayStart : 3989 iInitDisplayStart >= oSettings.fnRecordsDisplay() ? 3990 0 : 3991 iInitDisplayStart; 3992 3993 oSettings.iInitDisplayStart = -1; 3994 } 3995 } 3996 3997 /** 3998 * Create an Ajax call based on the table's settings, taking into account that 3999 * parameters can have multiple forms, and backwards compatibility. 4000 * 4001 * @param {object} oSettings dataTables settings object 4002 * @param {array} data Data to send to the server, required by 4003 * DataTables - may be augmented by developer callbacks 4004 * @param {function} fn Callback function to run when data is obtained 4005 */ 4006 function _fnBuildAjax( oSettings, data, fn ) 4007 { 4008 var ajaxData; 4009 var ajax = oSettings.ajax; 4010 var instance = oSettings.oInstance; 4011 var callback = function ( json ) { 4012 var status = oSettings.jqXHR 4013 ? oSettings.jqXHR.status 4014 : null; 4015 4016 if ( json === null || (typeof status === 'number' && status == 204 ) ) { 4017 json = {}; 4018 _fnAjaxDataSrc( oSettings, json, [] ); 4019 } 4020 4021 var error = json.error || json.sError; 4022 if ( error ) { 4023 _fnLog( oSettings, 0, error ); 4024 } 4025 4026 oSettings.json = json; 4027 4028 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true ); 4029 fn( json ); 4030 }; 4031 4032 if ( $.isPlainObject( ajax ) && ajax.data ) 4033 { 4034 ajaxData = ajax.data; 4035 4036 var newData = typeof ajaxData === 'function' ? 4037 ajaxData( data, oSettings ) : // fn can manipulate data or return 4038 ajaxData; // an object object or array to merge 4039 4040 // If the function returned something, use that alone 4041 data = typeof ajaxData === 'function' && newData ? 4042 newData : 4043 $.extend( true, data, newData ); 4044
4045 // Remove the data property as we've resolved it already and don't want 4046 // jQuery to do it again (it is restored at the end of the function) 4047 delete ajax.data; 4048 } 4049 4050 var baseAjax = { 4051 "url": typeof ajax === 'string' ? 4052 ajax : 4053 '', 4054 "data": data, 4055 "success": callback, 4056 "dataType": "json", 4057 "cache": false, 4058 "type": oSettings.sServerMethod, 4059 "error": function (xhr, error) { 4060 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR], true ); 4061 4062 if ( ret.indexOf(true) === -1 ) { 4063 if ( error == "parsererror" ) { 4064 _fnLog( oSettings, 0, 'Invalid JSON response', 1 ); 4065 } 4066 else if ( xhr.readyState === 4 ) { 4067 _fnLog( oSettings, 0, 'Ajax error', 7 ); 4068 } 4069 } 4070 4071 _fnProcessingDisplay( oSettings, false ); 4072 } 4073 }; 4074 4075 // If `ajax` option is an object, extend and override our default base 4076 if ( $.isPlainObject( ajax ) ) { 4077 $.extend( baseAjax, ajax ) 4078 } 4079 4080 // Store the data submitted for the API 4081 oSettings.oAjaxData = data; 4082 4083 // Allow plug-ins and external processes to modify the data 4084 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data, baseAjax], true ); 4085 4086 if ( typeof ajax === 'function' ) 4087 { 4088 // Is a function - let the caller define what needs to be done 4089 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings ); 4090 } 4091 else if (ajax.url === '') { 4092 // No url, so don't load any data. Just apply an empty data array 4093 // to the object for the callback. 4094 var empty = {}; 4095 4096 DataTable.util.set(ajax.dataSrc)(empty, []); 4097 callback(empty); 4098 } 4099 else { 4100 // Object to extend the base settings 4101 oSettings.jqXHR = $.ajax( baseAjax ); 4102 4103 // Restore for next time around 4104 if ( ajaxData ) { 4105 ajax.data = ajaxData; 4106 } 4107 } 4108 } 4109 4110 4111 /** 4112 * Update the table using an Ajax call 4113 * @param {object} settings dataTables settings object 4114 * @returns {boolean} Block the table drawing or not 4115 * @memberof DataTable#oApi 4116 */ 4117 function _fnAjaxUpdate( settings ) 4118 { 4119 settings.iDraw++; 4120 _fnProcessingDisplay( settings, true ); 4121 4122 _fnBuildAjax( 4123 settings, 4124 _fnAjaxParameters( settings ), 4125 function(json) { 4126 _fnAjaxUpdateDraw( settings, json ); 4127 } 4128 ); 4129 } 4130 4131 4132 /** 4133 * Build up the parameters in an object needed for a server-side processing 4134 * request. 4135 * @param {object} oSettings dataTables settings object 4136 * @returns {bool} block the table drawing or not 4137 * @memberof DataTable#oApi 4138 */ 4139 function _fnAjaxParameters( settings ) 4140 { 4141 var 4142 columns = settings.aoColumns, 4143 features = settings.oFeatures, 4144 preSearch = settings.oPreviousSearch, 4145 preColSearch = settings.aoPreSearchCols, 4146 colData = function ( idx, prop ) { 4147 return typeof columns[idx][prop] === 'function' ? 4148 'function' : 4149 columns[idx][prop]; 4150 }; 4151 4152 return { 4153 draw: settings.iDraw, 4154 columns: columns.map( function ( column, i ) { 4155 return { 4156 data: colData(i, 'mData'), 4157 name: column.sName, 4158 searchable: column.bSearchable, 4159 orderable: column.bSortable, 4160 search: { 4161 value: preColSearch[i].search, 4162 regex: preColSearch[i].regex, 4163 fixed: Object.keys(column.searchFixed).map( function(name) { 4164 return { 4165 name: name, 4166 term: column.searchFixed[name].toString() 4167 } 4168 }) 4169 } 4170 }; 4171 } ), 4172 order: _fnSortFlatten( settings ).map( function ( val ) { 4173 return { 4174 column: val.col, 4175 dir: val.dir, 4176 name: colData(val.col, 'sName') 4177 }; 4178 } ), 4179 start: settings._iDisplayStart, 4180 length: features.bPaginate ? 4181 settings._iDisplayLength : 4182 -1, 4183 search: { 4184 value: preSearch.search, 4185 regex: preSearch.regex, 4186 fixed: Object.keys(settings.searchFixed).map( function(name) { 4187 return { 4188 name: name, 4189 term: settings.searchFixed[name].toString() 4190 } 4191 }) 4192 } 4193 }; 4194 } 4195 4196 4197 /** 4198 * Data the data from the server (nuking the old) and redraw the table 4199 * @param {object} oSettings dataTables settings object 4200 * @param {object} json json data return from the server.
4201 * @param {string} json.sEcho Tracking flag for DataTables to match requests 4202 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering 4203 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering 4204 * @param {array} json.aaData The data to display on this page 4205 * @param {string} [json.sColumns] Column ordering (sName, comma separated) 4206 * @memberof DataTable#oApi 4207 */ 4208 function _fnAjaxUpdateDraw ( settings, json ) 4209 { 4210 var data = _fnAjaxDataSrc(settings, json); 4211 var draw = _fnAjaxDataSrcParam(settings, 'draw', json); 4212 var recordsTotal = _fnAjaxDataSrcParam(settings, 'recordsTotal', json); 4213 var recordsFiltered = _fnAjaxDataSrcParam(settings, 'recordsFiltered', json); 4214 4215 if ( draw !== undefined ) { 4216 // Protect against out of sequence returns 4217 if ( draw*1 < settings.iDraw ) { 4218 return; 4219 } 4220 settings.iDraw = draw * 1; 4221 } 4222 4223 // No data in returned object, so rather than an array, we show an empty table 4224 if ( ! data ) { 4225 data = []; 4226 } 4227 4228 _fnClearTable( settings ); 4229 settings._iRecordsTotal = parseInt(recordsTotal, 10); 4230 settings._iRecordsDisplay = parseInt(recordsFiltered, 10); 4231 4232 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 4233 _fnAddData( settings, data[i] ); 4234 } 4235 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4236 4237 _fnDraw( settings, true ); 4238 _fnInitComplete( settings ); 4239 _fnProcessingDisplay( settings, false ); 4240 } 4241 4242 4243 /** 4244 * Get the data from the JSON data source to use for drawing a table. Using 4245 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the 4246 * source object, or from a processing function. 4247 * @param {object} settings dataTables settings object 4248 * @param {object} json Data source object / array from the server 4249 * @return {array} Array of data to use 4250 */ 4251 function _fnAjaxDataSrc ( settings, json, write ) 4252 { 4253 var dataProp = 'data'; 4254 4255 if ($.isPlainObject( settings.ajax ) && settings.ajax.dataSrc !== undefined) { 4256 // Could in inside a `dataSrc` object, or not! 4257 var dataSrc = settings.ajax.dataSrc; 4258 4259 // string, function and object are valid types 4260 if (typeof dataSrc === 'string' || typeof dataSrc === 'function') { 4261 dataProp = dataSrc; 4262 } 4263 else if (dataSrc.data !== undefined) { 4264 dataProp = dataSrc.data; 4265 } 4266 } 4267 4268 if ( ! write ) { 4269 if ( dataProp === 'data' ) { 4270 // If the default, then we still want to support the old style, and safely ignore 4271 // it if possible 4272 return json.aaData || json[dataProp]; 4273 } 4274 4275 return dataProp !== "" ? 4276 _fnGetObjectDataFn( dataProp )( json ) : 4277 json; 4278 } 4279 4280 // set 4281 _fnSetObjectDataFn( dataProp )( json, write ); 4282 } 4283 4284 /** 4285 * Very similar to _fnAjaxDataSrc, but for the other SSP properties 4286 * @param {*} settings DataTables settings object 4287 * @param {*} param Target parameter 4288 * @param {*} json JSON data 4289 * @returns Resolved value 4290 */ 4291 function _fnAjaxDataSrcParam (settings, param, json) { 4292 var dataSrc = $.isPlainObject( settings.ajax ) 4293 ? settings.ajax.dataSrc 4294 : null; 4295 4296 if (dataSrc && dataSrc[param]) { 4297 // Get from custom location 4298 return _fnGetObjectDataFn( dataSrc[param] )( json ); 4299 } 4300 4301 // else - Default behaviour 4302 var old = ''; 4303 4304 // Legacy support 4305 if (param === 'draw') { 4306 old = 'sEcho'; 4307 } 4308 else if (param === 'recordsTotal') { 4309 old = 'iTotalRecords'; 4310 } 4311 else if (param === 'recordsFiltered') { 4312 old = 'iTotalDisplayRecords'; 4313 } 4314 4315 return json[old] !== undefined 4316 ? json[old] 4317 : json[param]; 4318 } 4319 4320 4321 /** 4322 * Filter the table using both the global filter and column based filtering 4323 * @param {object} settings dataTables settings object 4324 * @param {object} input search information 4325 * @memberof DataTable#oApi 4326 */ 4327 function _fnFilterComplete ( settings, input ) 4328 { 4329 var columnsSearch = settings.aoPreSearchCols; 4330 4331 // Resolve any column types that are unknown due to addition or invalidation 4332 // @todo As per sort - can this be moved into an event handler?
4333 _fnColumnTypes( settings ); 4334 4335 // In server-side processing all filtering is done by the server, so no point hanging around here 4336 if ( _fnDataSource( settings ) != 'ssp' ) 4337 { 4338 // Check if any of the rows were invalidated 4339 _fnFilterData( settings ); 4340 4341 // Start from the full data set 4342 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4343 4344 // Global filter first 4345 _fnFilter( settings.aiDisplay, settings, input.search, input ); 4346 4347 $.each(settings.searchFixed, function (name, term) { 4348 _fnFilter(settings.aiDisplay, settings, term, {}); 4349 }); 4350 4351 // Then individual column filters 4352 for ( var i=0 ; i<columnsSearch.length ; i++ ) 4353 { 4354 var col = columnsSearch[i]; 4355 4356 _fnFilter( 4357 settings.aiDisplay, 4358 settings, 4359 col.search, 4360 col, 4361 i 4362 ); 4363 4364 $.each(settings.aoColumns[i].searchFixed, function (name, term) { 4365 _fnFilter(settings.aiDisplay, settings, term, {}, i); 4366 }); 4367 } 4368 4369 // And finally global filtering 4370 _fnFilterCustom( settings ); 4371 } 4372 4373 // Tell the draw function we have been filtering 4374 settings.bFiltered = true; 4375 4376 _fnCallbackFire( settings, null, 'search', [settings] ); 4377 } 4378 4379 4380 /** 4381 * Apply custom filtering functions 4382 * 4383 * This is legacy now that we have named functions, but it is widely used 4384 * from 1.x, so it is not yet deprecated. 4385 * @param {object} oSettings dataTables settings object 4386 * @memberof DataTable#oApi 4387 */ 4388 function _fnFilterCustom( settings ) 4389 { 4390 var filters = DataTable.ext.search; 4391 var displayRows = settings.aiDisplay; 4392 var row, rowIdx; 4393 4394 for ( var i=0, ien=filters.length ; i<ien ; i++ ) { 4395 var rows = []; 4396 4397 // Loop over each row and see if it should be included 4398 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) { 4399 rowIdx = displayRows[ j ]; 4400 row = settings.aoData[ rowIdx ]; 4401 4402 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) { 4403 rows.push( rowIdx ); 4404 } 4405 } 4406 4407 // So the array reference doesn't break set the results into the 4408 // existing array 4409 displayRows.length = 0; 4410 displayRows.push.apply(displayRows, rows); 4411 } 4412 } 4413 4414 4415 /** 4416 * Filter the data table based on user input and draw the table 4417 */ 4418 function _fnFilter( searchRows, settings, input, options, column ) 4419 { 4420 if ( input === '' ) { 4421 return; 4422 } 4423 4424 var i = 0; 4425 4426 // Search term can be a function, regex or string - if a string we apply our 4427 // smart filtering regex (assuming the options require that) 4428 var searchFunc = typeof input === 'function' ? input : null; 4429 var rpSearch = input instanceof RegExp 4430 ? input 4431 : searchFunc 4432 ? null 4433 : _fnFilterCreateSearch( input, options ); 4434 4435 // Then for each row, does the test pass. If not, lop the row from the array 4436 while (i < searchRows.length) { 4437 var row = settings.aoData[ searchRows[i] ]; 4438 var data = column === undefined 4439 ? row._sFilterRow 4440 : row._aFilterData[ column ]; 4441 4442 if ( (searchFunc && ! searchFunc(data, row._aData, searchRows[i], column)) || (rpSearch && ! rpSearch.test(data)) ) { 4443 searchRows.splice(i, 1); 4444 i--; 4445 } 4446 4447 i++; 4448 } 4449 } 4450 4451 4452 /** 4453 * Build a regular expression object suitable for searching a table 4454 * @param {string} sSearch string to search for 4455 * @param {bool} bRegex treat as a regular expression or not 4456 * @param {bool} bSmart perform smart filtering or not 4457 * @param {bool} bCaseInsensitive Do case insensitive matching or not 4458 * @returns {RegExp} constructed object 4459 * @memberof DataTable#oApi 4460 */ 4461 function _fnFilterCreateSearch( search, inOpts ) 4462 { 4463 var not = []; 4464 var options = $.extend({}, { 4465 boundary: false, 4466 caseInsensitive: true, 4467 exact: false, 4468 regex: false, 4469 smart: true 4470 }, inOpts); 4471 4472 if (typeof search !== 'string') { 4473 search = search.toString(); 4474 } 4475 4476 // Remove diacritics if normalize is set up to do so 4477 search = _normalize(search); 4478 4479 if (options.exact) { 4480 return new RegExp( 4481 '^'+_fnEscapeRegex(search)+'$', 4482 options.caseInsensitive ? 'i' : '' 4483 ); 4484 } 4485 4486 search = options.regex ? 4487 search : 4488 _fnEscapeRegex( search ); 4489 4490 if ( options.smart ) { 4491 /* For smart filtering we want to allow the search to work regardless of 4492 * word order. We also want double quoted text to be preserved, so word 4493 * order is important - a la google. And a negative look around for 4494 * finding rows which don't contain a given string. 4495 * 4496 * So this is the sort of thing we want to generate: 4497 * 4498 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$ 4499 */ 4500 var parts = search.match( /!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || ['']; 4501 var a = parts.map( function ( word ) { 4502 var negative = false; 4503 var m; 4504 4505 // Determine if it is a "does not include" 4506 if ( word.charAt(0) === '!' ) { 4507 negative = true; 4508 word = word.substring(1); 4509 } 4510 4511 // Strip the quotes from around matched phrases 4512 if ( word.charAt(0) === '"' ) { 4513 m = word.match( /^"(.*)"$/ ); 4514 word = m ? m[1] : word; 4515 } 4516 else if ( word.charAt(0) === '\u201C' ) { 4517 // Smart quote match (iPhone users) 4518 m = word.match( /^\u201C(.*)\u201D$/ ); 4519 word = m ? m[1] : word; 4520 } 4521 4522 // For our "not" case, we need to modify the string that is 4523 // allowed to match at the end of the expression. 4524 if (negative) { 4525 if (word.length > 1) { 4526 not.push('(?!'+word+')'); 4527 } 4528 4529 word = ''; 4530 } 4531 4532 return word.replace(/"/g, ''); 4533 } ); 4534 4535 var match = not.length 4536 ? not.join('') 4537 : ''; 4538 4539 var boundary = options.boundary 4540 ? '\\b' 4541 : ''; 4542 4543 search = '^(?=.*?'+boundary+a.join( ')(?=.*?'+boundary )+')('+match+'.)*$'; 4544 } 4545 4546 return new RegExp( search, options.caseInsensitive ? 'i' : '' ); 4547 } 4548 4549 4550 /** 4551 * Escape a string such that it can be used in a regular expression 4552 * @param {string} sVal string to escape 4553 * @returns {string} escaped string 4554 * @memberof DataTable#oApi 4555 */ 4556 var _fnEscapeRegex = DataTable.util.escapeRegex; 4557
4558 var __filter_div = $('<div>')[0]; 4559 var __filter_div_textContent = __filter_div.textContent !== undefined; 4560 4561 // Update the filtering data for each row if needed (by invalidation or first run) 4562 function _fnFilterData ( settings ) 4563 { 4564 var columns = settings.aoColumns; 4565 var data = settings.aoData; 4566 var column; 4567 var j, jen, filterData, cellData, row; 4568 var wasInvalidated = false; 4569 4570 for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) { 4571 if (! data[rowIdx]) { 4572 continue; 4573 } 4574 4575 row = data[rowIdx]; 4576 4577 if ( ! row._aFilterData ) { 4578 filterData = []; 4579 4580 for ( j=0, jen=columns.length ; j<jen ; j++ ) { 4581 column = columns[j]; 4582 4583 if ( column.bSearchable ) { 4584 cellData = _fnGetCellData( settings, rowIdx, j, 'filter' ); 4585 4586 // Search in DataTables is string based 4587 if ( cellData === null ) { 4588 cellData = ''; 4589 } 4590 4591 if ( typeof cellData !== 'string' && cellData.toString ) { 4592 cellData = cellData.toString(); 4593 } 4594 } 4595 else { 4596 cellData = ''; 4597 } 4598 4599 // If it looks like there is an HTML entity in the string, 4600 // attempt to decode it so sorting works as expected. Note that 4601 // we could use a single line of jQuery to do this, but the DOM 4602 // method used here is much faster https://jsperf.com/html-decode 4603 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) { 4604 __filter_div.innerHTML = cellData; 4605 cellData = __filter_div_textContent ? 4606 __filter_div.textContent : 4607 __filter_div.innerText; 4608 } 4609 4610 if ( cellData.replace ) { 4611 cellData = cellData.replace(/[\r\n\u2028]/g, ''); 4612 } 4613 4614 filterData.push( cellData ); 4615 } 4616 4617 row._aFilterData = filterData; 4618 row._sFilterRow = filterData.join(' '); 4619 wasInvalidated = true; 4620 } 4621 } 4622 4623 return wasInvalidated; 4624 } 4625 4626 4627 /** 4628 * Draw the table for the first time, adding all required features 4629 * @param {object} settings dataTables settings object 4630 * @memberof DataTable#oApi 4631 */ 4632 function _fnInitialise ( settings ) 4633 { 4634 var i, iAjaxStart=settings.iInitDisplayStart; 4635 4636 /* Ensure that the table data is fully initialised */ 4637 if ( ! settings.bInitialised ) { 4638 setTimeout( function(){ _fnInitialise( settings ); }, 200 ); 4639 return; 4640 } 4641 4642 /* Build and draw the header / footer for the table */ 4643 _fnBuildHead( settings, 'header' ); 4644 _fnBuildHead( settings, 'footer' ); 4645 _fnDrawHead( settings, settings.aoHeader ); 4646 _fnDrawHead( settings, settings.aoFooter ); 4647 4648 // Enable features 4649 _fnAddOptionsHtml( settings ); 4650 _fnSortInit( settings ); 4651 4652 _colGroup( settings ); 4653 4654 /* Okay to show that something is going on now */ 4655 _fnProcessingDisplay( settings, true ); 4656 4657 _fnCallbackFire( settings, null, 'preInit', [settings], true ); 4658 4659 // If there is default sorting required - let's do it. The sort function 4660 // will do the drawing for us. Otherwise we draw the table regardless of the 4661 // Ajax source - this allows the table to look initialised for Ajax sourcing 4662 // data (show 'loading' message possibly) 4663 _fnReDraw( settings ); 4664 4665 var dataSrc = _fnDataSource( settings ); 4666 4667 // Server-side processing init complete is done by _fnAjaxUpdateDraw 4668 if ( dataSrc != 'ssp' ) { 4669 // if there is an ajax source load the data 4670 if ( dataSrc == 'ajax' ) { 4671 _fnBuildAjax( settings, {}, function(json) { 4672 var aData = _fnAjaxDataSrc( settings, json ); 4673 4674 // Got the data - add it to the table 4675 for ( i=0 ; i<aData.length ; i++ ) { 4676 _fnAddData( settings, aData[i] ); 4677 } 4678 4679 // Reset the init display for cookie saving. We've already done 4680 // a filter, and therefore cleared it before. So we need to make 4681 // it appear 'fresh' 4682 settings.iInitDisplayStart = iAjaxStart; 4683 4684 _fnReDraw( settings ); 4685 _fnProcessingDisplay( settings, false ); 4686 _fnInitComplete( settings ); 4687 }, settings ); 4688 } 4689 else { 4690 _fnInitComplete( settings ); 4691 _fnProcessingDisplay( settings, false ); 4692 } 4693 } 4694 } 4695 4696 4697 /** 4698 * Draw the table for the first time, adding all required features 4699 * @param {object} settings dataTables settings object 4700 * @memberof DataTable#oApi 4701 */ 4702 function _fnInitComplete ( settings ) 4703 { 4704 if (settings._bInitComplete) { 4705 return; 4706 } 4707 4708 var args = [settings, settings.json]; 4709 4710 settings._bInitComplete = true; 4711 4712 // Table is fully set up and we have data, so calculate the 4713 // column widths 4714 _fnAdjustColumnSizing( settings ); 4715 4716 _fnCallbackFire( settings, null, 'plugin-init', args, true ); 4717 _fnCallbackFire( settings, 'aoInitComplete', 'init', args, true ); 4718 } 4719 4720 function _fnLengthChange ( settings, val ) 4721 { 4722 var len = parseInt( val, 10 ); 4723 settings._iDisplayLength = len; 4724 4725 _fnLengthOverflow( settings ); 4726 4727 // Fire length change event 4728 _fnCallbackFire( settings, null, 'length', [settings, len] ); 4729 } 4730 4731 /** 4732 * Alter the display settings to change the page 4733 * @param {object} settings DataTables settings object 4734 * @param {string|int} action Paging action to take: "first", "previous", 4735 * "next" or "last" or page number to jump to (integer) 4736 * @param [bool] redraw Automatically draw the update or not 4737 * @returns {bool} true page has changed, false - no change 4738 * @memberof DataTable#oApi 4739 */ 4740 function _fnPageChange ( settings, action, redraw ) 4741 { 4742 var 4743 start = settings._iDisplayStart, 4744 len = settings._iDisplayLength, 4745 records = settings.fnRecordsDisplay(); 4746 4747 if ( records === 0 || len === -1 ) 4748 { 4749 start = 0; 4750 } 4751 else if ( typeof action === "number" ) 4752 { 4753 start = action * len; 4754 4755 if ( start > records ) 4756 { 4757 start = 0; 4758 } 4759 } 4760 else if ( action == "first" ) 4761 { 4762 start = 0; 4763 } 4764 else if ( action == "previous" ) 4765 { 4766 start = len >= 0 ? 4767 start - len : 4768 0; 4769 4770 if ( start < 0 ) 4771 { 4772 start = 0; 4773 } 4774 } 4775 else if ( action == "next" ) 4776 { 4777 if ( start + len < records ) 4778 { 4779 start += len; 4780 } 4781 } 4782 else if ( action == "last" ) 4783 { 4784 start = Math.floor( (records-1) / len) * len; 4785 } 4786 else if ( action === 'ellipsis' ) 4787 { 4788 return; 4789 } 4790 else 4791 { 4792 _fnLog( settings, 0, "Unknown paging action: "+action, 5 ); 4793 } 4794 4795 var changed = settings._iDisplayStart !== start; 4796 settings._iDisplayStart = start; 4797 4798 _fnCallbackFire( settings, null, changed ? 'page' : 'page-nc', [settings] ); 4799 4800 if ( changed && redraw ) { 4801 _fnDraw( settings ); 4802 } 4803 4804 return changed; 4805 } 4806 4807 4808 /** 4809 * Generate the node required for the processing node 4810 * @param {object} settings DataTables settings object 4811 */ 4812 function _processingHtml ( settings ) 4813 { 4814 var table = settings.nTable; 4815 4816 if ( settings.oFeatures.bProcessing ) { 4817 var n = $('<div/>', { 4818 'id': settings.sTableId + '_processing', 4819 'class': settings.oClasses.processing.container, 4820 'role': 'status' 4821 } ) 4822 .html( settings.oLanguage.sProcessing ) 4823 .append('<div><div></div><div></div><div></div><div></div></div>') 4824 .insertBefore( table ); 4825 4826 $(table).on( 'processing.dt.DT', function (e, s, show) { 4827 n.css( 'display', show ? 'block' : 'none' ); 4828 } ); 4829 } 4830 } 4831 4832 4833 /** 4834 * Display or hide the processing indicator 4835 * @param {object} settings DataTables settings object 4836 * @param {bool} show Show the processing indicator (true) or not (false) 4837 */ 4838 function _fnProcessingDisplay ( settings, show ) 4839 { 4840 _fnCallbackFire( settings, null, 'processing', [settings, show] ); 4841 } 4842 /** 4843 * Add any control elements for the table - specifically scrolling 4844 * @param {object} settings dataTables settings object 4845 * @returns {node} Node to add to the DOM 4846 * @memberof DataTable#oApi 4847 */ 4848 function _fnFeatureHtmlTable ( settings ) 4849 { 4850 var table = $(settings.nTable); 4851 4852 // Scrolling from here on in 4853 var scroll = settings.oScroll; 4854
4855 if ( scroll.sX === '' && scroll.sY === '' ) { 4856 return settings.nTable; 4857 } 4858 4859 var scrollX = scroll.sX; 4860 var scrollY = scroll.sY; 4861 var classes = settings.oClasses.scrolling; 4862 var caption = settings.captionNode; 4863 var captionSide = caption ? caption._captionSide : null; 4864 var headerClone = $( table[0].cloneNode(false) ); 4865 var footerClone = $( table[0].cloneNode(false) ); 4866 var footer = table.children('tfoot'); 4867 var _div = '<div/>'; 4868 var size = function ( s ) { 4869 return !s ? null : _fnStringToCss( s ); 4870 }; 4871 4872 if ( ! footer.length ) { 4873 footer = null; 4874 } 4875 4876 /* 4877 * The HTML structure that we want to generate in this function is: 4878 * div - scroller 4879 * div - scroll head 4880 * div - scroll head inner 4881 * table - scroll head table 4882 * thead - thead 4883 * div - scroll body 4884 * table - table (master table) 4885 * thead - thead clone for sizing 4886 * tbody - tbody 4887 * div - scroll foot 4888 * div - scroll foot inner 4889 * table - scroll foot table 4890 * tfoot - tfoot 4891 */ 4892 var scroller = $( _div, { 'class': classes.container } ) 4893 .append( 4894 $(_div, { 'class': classes.header.self } ) 4895 .css( { 4896 overflow: 'hidden', 4897 position: 'relative', 4898 border: 0, 4899 width: scrollX ? size(scrollX) : '100%' 4900 } ) 4901 .append( 4902 $(_div, { 'class': classes.header.inner } ) 4903 .css( { 4904 'box-sizing': 'content-box', 4905 width: scroll.sXInner || '100%' 4906 } ) 4907 .append( 4908 headerClone 4909 .removeAttr('id') 4910 .css( 'margin-left', 0 ) 4911 .append( captionSide === 'top' ? caption : null ) 4912 .append( 4913 table.children('thead') 4914 ) 4915 ) 4916 ) 4917 ) 4918 .append( 4919 $(_div, { 'class': classes.body } ) 4920 .css( { 4921 position: 'relative', 4922 overflow: 'auto', 4923 width: size( scrollX ) 4924 } ) 4925 .append( table ) 4926 ); 4927 4928 if ( footer ) { 4929 scroller.append( 4930 $(_div, { 'class': classes.footer.self } ) 4931 .css( { 4932 overflow: 'hidden', 4933 border: 0, 4934 width: scrollX ? size(scrollX) : '100%' 4935 } ) 4936 .append( 4937 $(_div, { 'class': classes.footer.inner } ) 4938 .append( 4939 footerClone 4940 .removeAttr('id') 4941 .css( 'margin-left', 0 ) 4942 .append( captionSide === 'bottom' ? caption : null ) 4943 .append( 4944 table.children('tfoot') 4945 ) 4946 ) 4947 ) 4948 ); 4949 } 4950 4951 var children = scroller.children(); 4952 var scrollHead = children[0]; 4953 var scrollBody = children[1]; 4954 var scrollFoot = footer ? children[2] : null; 4955 4956 // When the body is scrolled, then we also want to scroll the headers 4957 $(scrollBody).on( 'scroll.DT', function () { 4958 var scrollLeft = this.scrollLeft; 4959 4960 scrollHead.scrollLeft = scrollLeft; 4961 4962 if ( footer ) { 4963 scrollFoot.scrollLeft = scrollLeft; 4964 } 4965 } ); 4966 4967 // When focus is put on the header cells, we might need to scroll the body 4968 $('th, td', scrollHead).on('focus', function () { 4969 var scrollLeft = scrollHead.scrollLeft; 4970 4971 scrollBody.scrollLeft = scrollLeft; 4972 4973 if ( footer ) { 4974 scrollBody.scrollLeft = scrollLeft; 4975 } 4976 }); 4977 4978 $(scrollBody).css('max-height', scrollY); 4979 if (! scroll.bCollapse) { 4980 $(scrollBody).css('height', scrollY); 4981 } 4982 4983 settings.nScrollHead = scrollHead; 4984 settings.nScrollBody = scrollBody; 4985 settings.nScrollFoot = scrollFoot; 4986 4987 // On redraw - align columns 4988 settings.aoDrawCallback.push(_fnScrollDraw); 4989 4990 return scroller[0]; 4991 } 4992 4993 4994 4995 /** 4996 * Update the header, footer and body tables for resizing - i.e. column 4997 * alignment. 4998 * 4999 * Welcome to the most horrible function DataTables. The process that this 5000 * function follows is basically: 5001 * 1. Re-create the table inside the scrolling div 5002 * 2. Correct colgroup > col values if needed 5003 * 3. Copy colgroup > col over to header and footer 5004 * 4. Clean up 5005 * 5006 * @param {object} settings dataTables settings object 5007 * @memberof DataTable#oApi 5008 */ 5009 function _fnScrollDraw ( settings ) 5010 { 5011 // Given that this is such a monster function, a lot of variables are use 5012 // to try and keep the minimised size as small as possible 5013 var 5014 scroll = settings.oScroll, 5015 barWidth = scroll.iBarWidth, 5016 divHeader = $(settings.nScrollHead), 5017 divHeaderInner = divHeader.children('div'), 5018 divHeaderTable = divHeaderInner.children('table'), 5019 divBodyEl = settings.nScrollBody, 5020 divBody = $(divBodyEl), 5021 divFooter = $(settings.nScrollFoot), 5022 divFooterInner = divFooter.children('div'), 5023 divFooterTable = divFooterInner.children('table'), 5024 header = $(settings.nTHead), 5025 table = $(settings.nTable), 5026 footer = settings.nTFoot && $('th, td', settings.nTFoot).length ? $(settings.nTFoot) : null, 5027 browser = settings.oBrowser, 5028 headerCopy, footerCopy; 5029 5030 // If the scrollbar visibility has changed from the last draw, we need to 5031 // adjust the column sizes as the table width will have changed to account 5032 // for the scrollbar 5033 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight; 5034 5035 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) { 5036 settings.scrollBarVis = scrollBarVis; 5037 _fnAdjustColumnSizing( settings ); 5038 return; // adjust column sizing will call this function again 5039 } 5040 else { 5041 settings.scrollBarVis = scrollBarVis; 5042 } 5043 5044 // 1. Re-create the table inside the scrolling div 5045 // Remove the old minimised thead and tfoot elements in the inner table 5046 table.children('thead, tfoot').remove(); 5047 5048 // Clone the current header and footer elements and then place it into the inner table 5049 headerCopy = header.clone().prependTo( table ); 5050 headerCopy.find('th, td').removeAttr('tabindex'); 5051 headerCopy.find('[id]').removeAttr('id'); 5052 5053 if ( footer ) { 5054 footerCopy = footer.clone().prependTo( table ); 5055 footerCopy.find('[id]').removeAttr('id'); 5056 } 5057 5058 // 2. Correct colgroup > col values if needed 5059 // It is possible that the cell sizes are smaller than the content, so we need to 5060 // correct colgroup>col for such cases. This can happen if the auto width detection 5061 // uses a cell which has a longer string, but isn't the widest! For example 5062 // "Chief Executive Officer (CEO)" is the longest string in the demo, but 5063 // "Systems Administrator" is actually the widest string since it doesn't collapse. 5064 // Note the use of translating into a column index to get the `col` element. This 5065 // is because of Responsive which might remove `col` elements, knocking the alignment 5066 // of the indexes out. 5067 if (settings.aiDisplay.length) { 5068 // Get the column sizes from the first row in the table 5069 var colSizes = table.find('tbody tr').eq(0).find('th, td').map(function (vis) { 5070 return { 5071 idx: _fnVisibleToColumnIndex(settings, vis), 5072 width: $(this).outerWidth() 5073 } 5074 }); 5075 5076 // Check against what the colgroup > col is set to and correct if needed 5077 for (var i=0 ; i<colSizes.length ; i++) { 5078 var colEl = settings.aoColumns[ colSizes[i].idx ].colEl[0]; 5079 var colWidth = colEl.style.width.replace('px', ''); 5080 5081 if (colWidth !== colSizes[i].width) { 5082 colEl.style.width = colSizes[i].width + 'px'; 5083 } 5084 } 5085 } 5086 5087 // 3. Copy the colgroup over to the header and footer 5088 divHeaderTable 5089 .find('colgroup') 5090 .remove(); 5091 5092 divHeaderTable.append(settings.colgroup.clone()); 5093 5094 if ( footer ) { 5095 divFooterTable 5096 .find('colgroup') 5097 .remove(); 5098 5099 divFooterTable.append(settings.colgroup.clone()); 5100 } 5101 5102 // "Hide" the header and footer that we used for the sizing. We need to keep 5103 // the content of the cell so that the width applied to the header and body 5104 // both match, but we want to hide it completely. 5105 $('th, td', headerCopy).each(function () { 5106 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5107 }); 5108 5109 if ( footer ) { 5110 $('th, td', footerCopy).each(function () { 5111 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5112 }); 5113 } 5114 5115 // 4. Clean up 5116 // Figure out if there are scrollbar present - if so then we need a the header and footer to 5117 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) 5118 var isScrolling = Math.floor(table.height()) > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll"; 5119 var paddingSide = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' ); 5120 5121 // Set the width's of the header and footer tables 5122 var outerWidth = table.outerWidth(); 5123 5124 divHeaderTable.css('width', _fnStringToCss( outerWidth )); 5125 divHeaderInner 5126 .css('width', _fnStringToCss( outerWidth )) 5127 .css(paddingSide, isScrolling ? barWidth+"px" : "0px"); 5128 5129 if ( footer ) { 5130 divFooterTable.css('width', _fnStringToCss( outerWidth )); 5131 divFooterInner 5132 .css('width', _fnStringToCss( outerWidth )) 5133 .css(paddingSide, isScrolling ? barWidth+"px" : "0px"); 5134 } 5135 5136 // Correct DOM ordering for colgroup - comes before the thead 5137 table.children('colgroup').prependTo(table); 5138 5139 // Adjust the position of the header in case we loose the y-scrollbar 5140 divBody.trigger('scroll'); 5141 5142 // If sorting or filtering has occurred, jump the scrolling back to the top 5143 // only if we aren't holding the position 5144 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) { 5145 divBodyEl.scrollTop = 0; 5146 } 5147 } 5148 5149 /** 5150 * Calculate the width of columns for the table 5151 * @param {object} settings dataTables settings object 5152 * @memberof DataTable#oApi 5153 */ 5154 function _fnCalculateColumnWidths ( settings ) 5155 { 5156 // Not interested in doing column width calculation if auto-width is disabled 5157 if (! settings.oFeatures.bAutoWidth) { 5158 return; 5159 } 5160 5161 var 5162 table = settings.nTable,
5163 columns = settings.aoColumns, 5164 scroll = settings.oScroll, 5165 scrollY = scroll.sY, 5166 scrollX = scroll.sX, 5167 scrollXInner = scroll.sXInner, 5168 visibleColumns = _fnGetColumns( settings, 'bVisible' ), 5169 tableWidthAttr = table.getAttribute('width'), // from DOM element 5170 tableContainer = table.parentNode, 5171 i, column, columnIdx; 5172 5173 var styleWidth = table.style.width; 5174 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) { 5175 tableWidthAttr = styleWidth; 5176 } 5177 5178 // Let plug-ins know that we are doing a recalc, in case they have changed any of the 5179 // visible columns their own way (e.g. Responsive uses display:none). 5180 _fnCallbackFire( 5181 settings, 5182 null, 5183 'column-calc', 5184 {visible: visibleColumns}, 5185 false 5186 ); 5187 5188 // Construct a single row, worst case, table with the widest 5189 // node in the data, assign any user defined widths, then insert it into 5190 // the DOM and allow the browser to do all the hard work of calculating 5191 // table widths 5192 var tmpTable = $(table.cloneNode()) 5193 .css( 'visibility', 'hidden' ) 5194 .removeAttr( 'id' ); 5195 5196 // Clean up the table body 5197 tmpTable.append('<tbody>') 5198 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') ); 5199 5200 // Clone the table header and footer - we can't use the header / footer 5201 // from the cloned table, since if scrolling is active, the table's 5202 // real header and footer are contained in different table tags 5203 tmpTable 5204 .append( $(settings.nTHead).clone() ) 5205 .append( $(settings.nTFoot).clone() ); 5206 5207 // Remove any assigned widths from the footer (from scrolling) 5208 tmpTable.find('tfoot th, tfoot td').css('width', ''); 5209 5210 // Apply custom sizing to the cloned header 5211 tmpTable.find('thead th, thead td').each( function () { 5212 // Get the `width` from the header layout 5213 var width = _fnColumnsSumWidth( settings, this, true, false ); 5214 5215 if ( width ) { 5216 this.style.width = width; 5217 5218 // For scrollX we need to force the column width otherwise the 5219 // browser will collapse it. If this width is smaller than the 5220 // width the column requires, then it will have no effect 5221 if ( scrollX ) { 5222 $( this ).append( $('<div/>').css( { 5223 width: width, 5224 margin: 0, 5225 padding: 0, 5226 border: 0, 5227 height: 1 5228 } ) ); 5229 } 5230 } 5231 else { 5232 this.style.width = ''; 5233 } 5234 } ); 5235 5236 // Find the widest piece of data for each column and put it into the table 5237 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5238 columnIdx = visibleColumns[i]; 5239 column = columns[ columnIdx ]; 5240 5241 var longest = _fnGetMaxLenString(settings, columnIdx); 5242 var autoClass = _ext.type.className[column.sType]; 5243 var text = longest + column.sContentPadding; 5244 var insert = longest.indexOf('<') === -1 5245 ? document.createTextNode(text) 5246 : text 5247 5248 $('<td/>') 5249 .addClass(autoClass) 5250 .addClass(column.sClass) 5251 .append(insert) 5252 .appendTo(tr); 5253 } 5254 5255 // Tidy the temporary table - remove name attributes so there aren't 5256 // duplicated in the dom (radio elements for example) 5257 $('[name]', tmpTable).removeAttr('name'); 5258 5259 // Table has been built, attach to the document so we can work with it. 5260 // A holding element is used, positioned at the top of the container 5261 // with minimal height, so it has no effect on if the container scrolls 5262 // or not. Otherwise it might trigger scrolling when it actually isn't 5263 // needed 5264 var holder = $('<div/>').css( scrollX || scrollY ? 5265 { 5266 position: 'absolute', 5267 top: 0, 5268 left: 0, 5269 height: 1, 5270 right: 0, 5271 overflow: 'hidden' 5272 } : 5273 {} 5274 ) 5275 .append( tmpTable ) 5276 .appendTo( tableContainer ); 5277 5278 // When scrolling (X or Y) we want to set the width of the table as 5279 // appropriate. However, when not scrolling leave the table width as it 5280 // is. This results in slightly different, but I think correct behaviour 5281 if ( scrollX && scrollXInner ) { 5282 tmpTable.width( scrollXInner ); 5283 } 5284 else if ( scrollX ) { 5285 tmpTable.css( 'width', 'auto' ); 5286 tmpTable.removeAttr('width'); 5287 5288 // If there is no width attribute or style, then allow the table to 5289 // collapse
5290 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) { 5291 tmpTable.width( tableContainer.clientWidth ); 5292 } 5293 } 5294 else if ( scrollY ) { 5295 tmpTable.width( tableContainer.clientWidth ); 5296 } 5297 else if ( tableWidthAttr ) { 5298 tmpTable.width( tableWidthAttr ); 5299 } 5300 5301 // Get the width of each column in the constructed table 5302 var total = 0; 5303 var bodyCells = tmpTable.find('tbody tr').eq(0).children(); 5304 5305 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5306 // Use getBounding for sub-pixel accuracy, which we then want to round up! 5307 var bounding = bodyCells[i].getBoundingClientRect().width; 5308 5309 // Total is tracked to remove any sub-pixel errors as the outerWidth 5310 // of the table might not equal the total given here 5311 total += bounding; 5312 5313 // Width for each column to use 5314 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding ); 5315 } 5316 5317 table.style.width = _fnStringToCss( total ); 5318 5319 // Finished with the table - ditch it 5320 holder.remove(); 5321 5322 // If there is a width attr, we want to attach an event listener which 5323 // allows the table sizing to automatically adjust when the window is 5324 // resized. Use the width attr rather than CSS, since we can't know if the 5325 // CSS is a relative value or absolute - DOM read is always px. 5326 if ( tableWidthAttr ) { 5327 table.style.width = _fnStringToCss( tableWidthAttr ); 5328 } 5329 5330 if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) { 5331 var bindResize = function () { 5332 $(window).on('resize.DT-'+settings.sInstance, DataTable.util.throttle( function () {
5333 if (! settings.bDestroying) { 5334 _fnAdjustColumnSizing( settings ); 5335 } 5336 } ) ); 5337 }; 5338 5339 bindResize(); 5340 5341 settings._reszEvt = true; 5342 } 5343 } 5344 5345 5346 /** 5347 * Get the maximum strlen for each data column 5348 * @param {object} settings dataTables settings object 5349 * @param {int} colIdx column of interest 5350 * @returns {string} string of the max length 5351 * @memberof DataTable#oApi 5352 */ 5353 function _fnGetMaxLenString( settings, colIdx ) 5354 { 5355 var column = settings.aoColumns[colIdx]; 5356 5357 if (! column.maxLenString) { 5358 var s, max='', maxLen = -1; 5359 5360 for ( var i=0, ien=settings.aiDisplayMaster.length ; i<ien ; i++ ) { 5361 var rowIdx = settings.aiDisplayMaster[i]; 5362 var data = _fnGetRowDisplay(settings, rowIdx)[colIdx]; 5363 5364 var cellString = data && typeof data === 'object' && data.nodeType 5365 ? data.innerHTML 5366 : data+''; 5367 5368 // Remove id / name attributes from elements so they 5369 // don't interfere with existing elements 5370 cellString = cellString 5371 .replace(/id=".*?"/g, '') 5372 .replace(/name=".*?"/g, ''); 5373 5374 s = _stripHtml(cellString) 5375 .replace( / /g, ' ' ); 5376 5377 if ( s.length > maxLen ) { 5378 // We want the HTML in the string, but the length that 5379 // is important is the stripped string 5380 max = cellString; 5381 maxLen = s.length; 5382 } 5383 } 5384 5385 column.maxLenString = max; 5386 } 5387 5388 return column.maxLenString; 5389 } 5390 5391 5392 /** 5393 * Append a CSS unit (only if required) to a string 5394 * @param {string} value to css-ify 5395 * @returns {string} value with css unit 5396 * @memberof DataTable#oApi 5397 */ 5398 function _fnStringToCss( s ) 5399 { 5400 if ( s === null ) { 5401 return '0px'; 5402 } 5403 5404 if ( typeof s == 'number' ) { 5405 return s < 0 ? 5406 '0px' : 5407 s+'px'; 5408 } 5409 5410 // Check it has a unit character already 5411 return s.match(/\d$/) ? 5412 s+'px' : 5413 s; 5414 } 5415 5416 /** 5417 * Re-insert the `col` elements for current visibility 5418 * 5419 * @param {*} settings DT settings 5420 */ 5421 function _colGroup( settings ) { 5422 var cols = settings.aoColumns; 5423 5424 settings.colgroup.empty(); 5425 5426 for (i=0 ; i<cols.length ; i++) { 5427 if (cols[i].bVisible) { 5428 settings.colgroup.append(cols[i].colEl); 5429 } 5430 } 5431 } 5432 5433 5434 function _fnSortInit( settings ) { 5435 var target = settings.nTHead; 5436 var headerRows = target.querySelectorAll('tr'); 5437 var legacyTop = settings.bSortCellsTop; 5438 var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])'; 5439 5440 // Legacy support for `orderCellsTop` 5441 if (legacyTop === true) { 5442 target = headerRows[0]; 5443 } 5444 else if (legacyTop === false) { 5445 target = headerRows[ headerRows.length - 1 ]; 5446 } 5447 5448 _fnSortAttachListener( 5449 settings, 5450 target, 5451 target === settings.nTHead 5452 ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector 5453 : 'th'+notSelector+', td'+notSelector 5454 ); 5455 5456 // Need to resolve the user input array into our internal structure 5457 var order = []; 5458 _fnSortResolve( settings, order, settings.aaSorting ); 5459 5460 settings.aaSorting = order; 5461 } 5462 5463 5464 function _fnSortAttachListener(settings, node, selector, column, callback) { 5465 _fnBindAction( node, selector, function (e) { 5466 var run = false; 5467 var columns = column === undefined 5468 ? _fnColumnsFromHeader( e.target ) 5469 : [column]; 5470 5471 if ( columns.length ) { 5472 for ( var i=0, ien=columns.length ; i<ien ; i++ ) { 5473 var ret = _fnSortAdd( settings, columns[i], i, e.shiftKey ); 5474 5475 if (ret !== false) { 5476 run = true; 5477 } 5478 5479 // If the first entry is no sort, then subsequent 5480 // sort columns are ignored 5481 if (settings.aaSorting.length === 1 && settings.aaSorting[0][1] === '') { 5482 break; 5483 } 5484 } 5485 5486 if (run) { 5487 _fnProcessingDisplay( settings, true ); 5488 5489 // Allow the processing display to show 5490 setTimeout( function () { 5491 _fnSort( settings ); 5492 _fnSortDisplay( settings, settings.aiDisplay ); 5493 5494 // Sort processing done - redraw has its own processing display 5495 _fnProcessingDisplay( settings, false ); 5496 5497 _fnReDraw( settings, false, false ); 5498 5499 if (callback) { 5500 callback(); 5501 } 5502 }, 0); 5503 } 5504 } 5505 } ); 5506 } 5507 5508 /** 5509 * Sort the display array to match the master's order 5510 * @param {*} settings 5511 */ 5512 function _fnSortDisplay(settings, display) { 5513 var master = settings.aiDisplayMaster; 5514 var masterMap = {}; 5515 var map = {}; 5516 var i; 5517 5518 // Rather than needing an `indexOf` on master array, we can create a map 5519 for (i=0 ; i<master.length ; i++) { 5520 masterMap[master[i]] = i; 5521 } 5522 5523 // And then cache what would be the indexOf fom the display 5524 for (i=0 ; i<display.length ; i++) { 5525 map[display[i]] = masterMap[display[i]]; 5526 } 5527 5528 display.sort(function(a, b){ 5529 // Short version of this function is simply `master.indexOf(a) - master.indexOf(b);` 5530 return map[a] - map[b]; 5531 }); 5532 } 5533 5534
5535 function _fnSortResolve (settings, nestedSort, sort) { 5536 var push = function ( a ) { 5537 if ($.isPlainObject(a)) { 5538 if (a.idx !== undefined) { 5539 // Index based ordering 5540 nestedSort.push([a.idx, a.dir]); 5541 } 5542 else if (a.name) { 5543 // Name based ordering 5544 var cols = _pluck( settings.aoColumns, 'sName'); 5545 var idx = cols.indexOf(a.name); 5546 5547 if (idx !== -1) { 5548 nestedSort.push([idx, a.dir]); 5549 } 5550 } 5551 } 5552 else { 5553 // Plain column index and direction pair 5554 nestedSort.push(a); 5555 } 5556 }; 5557 5558 if ( $.isPlainObject(sort) ) { 5559 // Object 5560 push(sort); 5561 } 5562 else if ( sort.length && typeof sort[0] === 'number' ) { 5563 // 1D array 5564 push(sort); 5565 } 5566 else if ( sort.length ) { 5567 // 2D array 5568 for (var z=0; z<sort.length; z++) { 5569 push(sort[z]); // Object or array 5570 } 5571 } 5572 } 5573 5574 5575 function _fnSortFlatten ( settings ) 5576 { 5577 var 5578 i, k, kLen, 5579 aSort = [], 5580 extSort = DataTable.ext.type.order, 5581 aoColumns = settings.aoColumns, 5582 aDataSort, iCol, sType, srcCol, 5583 fixed = settings.aaSortingFixed, 5584 fixedObj = $.isPlainObject( fixed ), 5585 nestedSort = []; 5586 5587 if ( ! settings.oFeatures.bSort ) { 5588 return aSort; 5589 } 5590 5591 // Build the sort array, with pre-fix and post-fix options if they have been 5592 // specified 5593 if ( Array.isArray( fixed ) ) { 5594 _fnSortResolve( settings, nestedSort, fixed ); 5595 } 5596 5597 if ( fixedObj && fixed.pre ) { 5598 _fnSortResolve( settings, nestedSort, fixed.pre ); 5599 } 5600 5601 _fnSortResolve( settings, nestedSort, settings.aaSorting ); 5602 5603 if (fixedObj && fixed.post ) { 5604 _fnSortResolve( settings, nestedSort, fixed.post ); 5605 } 5606 5607 for ( i=0 ; i<nestedSort.length ; i++ ) 5608 { 5609 srcCol = nestedSort[i][0]; 5610 5611 if ( aoColumns[ srcCol ] ) { 5612 aDataSort = aoColumns[ srcCol ].aDataSort; 5613 5614 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ ) 5615 { 5616 iCol = aDataSort[k]; 5617 sType = aoColumns[ iCol ].sType || 'string'; 5618 5619 if ( nestedSort[i]._idx === undefined ) { 5620 nestedSort[i]._idx = aoColumns[iCol].asSorting.indexOf(nestedSort[i][1]); 5621 } 5622 5623 if ( nestedSort[i][1] ) { 5624 aSort.push( { 5625 src: srcCol, 5626 col: iCol, 5627 dir: nestedSort[i][1], 5628 index: nestedSort[i]._idx, 5629 type: sType, 5630 formatter: extSort[ sType+"-pre" ], 5631 sorter: extSort[ sType+"-"+nestedSort[i][1] ] 5632 } ); 5633 } 5634 } 5635 } 5636 } 5637 5638 return aSort; 5639 } 5640 5641 /** 5642 * Change the order of the table 5643 * @param {object} oSettings dataTables settings object 5644 * @memberof DataTable#oApi 5645 */ 5646 function _fnSort ( oSettings, col, dir ) 5647 { 5648 var 5649 i, ien, iLen, 5650 aiOrig = [], 5651 extSort = DataTable.ext.type.order, 5652 aoData = oSettings.aoData, 5653 sortCol, 5654 displayMaster = oSettings.aiDisplayMaster, 5655 aSort; 5656 5657 // Resolve any column types that are unknown due to addition or invalidation 5658 // @todo Can this be moved into a 'data-ready' handler which is called when 5659 // data is going to be used in the table? 5660 _fnColumnTypes( oSettings ); 5661 5662 // Allow a specific column to be sorted, which will _not_ alter the display 5663 // master 5664 if (col !== undefined) { 5665 var srcCol = oSettings.aoColumns[col]; 5666 aSort = [{ 5667 src: col, 5668 col: col, 5669 dir: dir, 5670 index: 0, 5671 type: srcCol.sType, 5672 formatter: extSort[ srcCol.sType+"-pre" ], 5673 sorter: extSort[ srcCol.sType+"-"+dir ] 5674 }]; 5675 displayMaster = displayMaster.slice(); 5676 } 5677 else { 5678 aSort = _fnSortFlatten( oSettings ); 5679 } 5680 5681 for ( i=0, ien=aSort.length ; i<ien ; i++ ) { 5682 sortCol = aSort[i]; 5683 5684 // Load the data needed for the sort, for each cell 5685 _fnSortData( oSettings, sortCol.col ); 5686 } 5687 5688 /* No sorting required if server-side or no sorting array */ 5689 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 ) 5690 { 5691 // Reset the initial positions on each pass so we get a stable sort 5692 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) { 5693 aiOrig[ i ] = i; 5694 } 5695 5696 // If the first sort is desc, then reverse the array to preserve original 5697 // order, just in reverse 5698 if (aSort.length && aSort[0].dir === 'desc') { 5699 aiOrig.reverse(); 5700 } 5701
5702 /* Do the sort - here we want multi-column sorting based on a given data source (column) 5703 * and sorting function (from oSort) in a certain direction. It's reasonably complex to 5704 * follow on it's own, but this is what we want (example two column sorting): 5705 * fnLocalSorting = function(a,b){ 5706 * var test; 5707 * test = oSort['string-asc']('data11', 'data12'); 5708 * if (test !== 0) 5709 * return test; 5710 * test = oSort['numeric-desc']('data21', 'data22'); 5711 * if (test !== 0) 5712 * return test; 5713 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); 5714 * } 5715 * Basically we have a test for each sorting column, if the data in that column is equal, 5716 * test the next column. If all columns match, then we use a numeric sort on the row 5717 * positions in the original data array to provide a stable sort. 5718 */ 5719 displayMaster.sort( function ( a, b ) { 5720 var 5721 x, y, k, test, sort, 5722 len=aSort.length, 5723 dataA = aoData[a]._aSortData, 5724 dataB = aoData[b]._aSortData; 5725 5726 for ( k=0 ; k<len ; k++ ) { 5727 sort = aSort[k]; 5728 5729 // Data, which may have already been through a `-pre` function 5730 x = dataA[ sort.col ]; 5731 y = dataB[ sort.col ]; 5732 5733 if (sort.sorter) { 5734 // If there is a custom sorter (`-asc` or `-desc`) for this 5735 // data type, use it 5736 test = sort.sorter(x, y); 5737 5738 if ( test !== 0 ) { 5739 return test; 5740 } 5741 } 5742 else { 5743 // Otherwise, use generic sorting 5744 test = x<y ? -1 : x>y ? 1 : 0; 5745 5746 if ( test !== 0 ) { 5747 return sort.dir === 'asc' ? test : -test; 5748 } 5749 } 5750 } 5751 5752 x = aiOrig[a]; 5753 y = aiOrig[b]; 5754 5755 return x<y ? -1 : x>y ? 1 : 0; 5756 } ); 5757 } 5758 else if ( aSort.length === 0 ) { 5759 // Apply index order 5760 displayMaster.sort(function (x, y) { 5761 return x<y ? -1 : x>y ? 1 : 0; 5762 }); 5763 } 5764 5765 if (col === undefined) { 5766 // Tell the draw function that we have sorted the data 5767 oSettings.bSorted = true; 5768 5769 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] ); 5770 } 5771
5772 return displayMaster; 5773 } 5774 5775 5776 /** 5777 * Function to run on user sort request 5778 * @param {object} settings dataTables settings object 5779 * @param {node} attachTo node to attach the handler to 5780 * @param {int} colIdx column sorting index 5781 * @param {int} addIndex Counter 5782 * @param {boolean} [shift=false] Shift click add 5783 * @param {function} [callback] callback function 5784 * @memberof DataTable#oApi 5785 */ 5786 function _fnSortAdd ( settings, colIdx, addIndex, shift ) 5787 { 5788 var col = settings.aoColumns[ colIdx ]; 5789 var sorting = settings.aaSorting; 5790 var asSorting = col.asSorting; 5791 var nextSortIdx; 5792 var next = function ( a, overflow ) { 5793 var idx = a._idx; 5794 if ( idx === undefined ) { 5795 idx = asSorting.indexOf(a[1]); 5796 } 5797 5798 return idx+1 < asSorting.length ? 5799 idx+1 : 5800 overflow ? 5801 null : 5802 0; 5803 }; 5804 5805 if ( ! col.bSortable ) { 5806 return false; 5807 } 5808 5809 // Convert to 2D array if needed 5810 if ( typeof sorting[0] === 'number' ) { 5811 sorting = settings.aaSorting = [ sorting ]; 5812 } 5813 5814 // If appending the sort then we are multi-column sorting 5815 if ( (shift || addIndex) && settings.oFeatures.bSortMulti ) { 5816 // Are we already doing some kind of sort on this column? 5817 var sortIdx = _pluck(sorting, '0').indexOf(colIdx); 5818 5819 if ( sortIdx !== -1 ) { 5820 // Yes, modify the sort 5821 nextSortIdx = next( sorting[sortIdx], true ); 5822 5823 if ( nextSortIdx === null && sorting.length === 1 ) { 5824 nextSortIdx = 0; // can't remove sorting completely 5825 } 5826 5827 if ( nextSortIdx === null ) { 5828 sorting.splice( sortIdx, 1 ); 5829 } 5830 else { 5831 sorting[sortIdx][1] = asSorting[ nextSortIdx ]; 5832 sorting[sortIdx]._idx = nextSortIdx; 5833 } 5834 } 5835 else if (shift) { 5836 // No sort on this column yet, being added by shift click 5837 // add it as itself 5838 sorting.push( [ colIdx, asSorting[0], 0 ] ); 5839 sorting[sorting.length-1]._idx = 0; 5840 } 5841 else { 5842 // No sort on this column yet, being added from a colspan 5843 // so add with same direction as first column 5844 sorting.push( [ colIdx, sorting[0][1], 0 ] ); 5845 sorting[sorting.length-1]._idx = 0; 5846 } 5847 } 5848 else if ( sorting.length && sorting[0][0] == colIdx ) { 5849 // Single column - already sorting on this column, modify the sort 5850 nextSortIdx = next( sorting[0] ); 5851 5852 sorting.length = 1; 5853 sorting[0][1] = asSorting[ nextSortIdx ]; 5854 sorting[0]._idx = nextSortIdx; 5855 } 5856 else { 5857 // Single column - sort only on this column 5858 sorting.length = 0; 5859 sorting.push( [ colIdx, asSorting[0] ] ); 5860 sorting[0]._idx = 0; 5861 } 5862 } 5863 5864 5865 /** 5866 * Set the sorting classes on table's body, Note: it is safe to call this function 5867 * when bSort and bSortClasses are false 5868 * @param {object} oSettings dataTables settings object 5869 * @memberof DataTable#oApi 5870 */ 5871 function _fnSortingClasses( settings ) 5872 { 5873 var oldSort = settings.aLastSort; 5874 var sortClass = settings.oClasses.order.position; 5875 var sort = _fnSortFlatten( settings ); 5876 var features = settings.oFeatures; 5877 var i, ien, colIdx; 5878 5879 if ( features.bSort && features.bSortClasses ) { 5880 // Remove old sorting classes 5881 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) { 5882 colIdx = oldSort[i].src; 5883 5884 // Remove column sorting 5885 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 5886 .removeClass( sortClass + (i<2 ? i+1 : 3) ); 5887 } 5888 5889 // Add new column sorting 5890 for ( i=0, ien=sort.length ; i<ien ; i++ ) { 5891 colIdx = sort[i].src; 5892 5893 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 5894 .addClass( sortClass + (i<2 ? i+1 : 3) ); 5895 } 5896 } 5897 5898 settings.aLastSort = sort; 5899 } 5900 5901 5902 // Get the data to sort a column, be it from cache, fresh (populating the 5903 // cache), or from a sort formatter 5904 function _fnSortData( settings, colIdx ) 5905 { 5906 // Custom sorting function - provided by the sort data type 5907 var column = settings.aoColumns[ colIdx ]; 5908 var customSort = DataTable.ext.order[ column.sSortDataType ]; 5909 var customData; 5910 5911 if ( customSort ) { 5912 customData = customSort.call( settings.oInstance, settings, colIdx, 5913 _fnColumnIndexToVisible( settings, colIdx ) 5914 ); 5915 } 5916 5917 // Use / populate cache 5918 var row, cellData; 5919 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ]; 5920 var data = settings.aoData; 5921 5922 for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) { 5923 // Sparse array 5924 if (! data[rowIdx]) { 5925 continue; 5926 } 5927 5928 row = data[rowIdx]; 5929 5930 if ( ! row._aSortData ) { 5931 row._aSortData = []; 5932 } 5933 5934 if ( ! row._aSortData[colIdx] || customSort ) { 5935 cellData = customSort ? 5936 customData[rowIdx] : // If there was a custom sort function, use data from there 5937 _fnGetCellData( settings, rowIdx, colIdx, 'sort' ); 5938 5939 row._aSortData[ colIdx ] = formatter ? 5940 formatter( cellData, settings ) : 5941 cellData; 5942 } 5943 } 5944 } 5945 5946 5947 /** 5948 * State information for a table 5949 * 5950 * @param {*} settings 5951 * @returns State object 5952 */ 5953 function _fnSaveState ( settings ) 5954 { 5955 if (settings._bLoadingState) { 5956 return; 5957 } 5958 5959 /* Store the interesting variables */ 5960 var state = { 5961 time: +new Date(), 5962 start: settings._iDisplayStart, 5963 length: settings._iDisplayLength, 5964 order: $.extend( true, [], settings.aaSorting ), 5965 search: $.extend({}, settings.oPreviousSearch), 5966 columns: settings.aoColumns.map( function ( col, i ) { 5967 return { 5968 visible: col.bVisible, 5969 search: $.extend({}, settings.aoPreSearchCols[i]) 5970 }; 5971 } ) 5972 }; 5973 5974 settings.oSavedState = state; 5975 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] ); 5976
vendor: 17,891 bytes, lines 5977-6561
5977 if ( settings.oFeatures.bStateSave && !settings.bDestroying ) 5978 { 5979 settings.fnStateSaveCallback.call( settings.oInstance, settings, state ); 5980 } 5981 } 5982 5983 5984 /** 5985 * Attempt to load a saved table state 5986 * @param {object} oSettings dataTables settings object 5987 * @param {object} oInit DataTables init object so we can override settings 5988 * @param {function} callback Callback to execute when the state has been loaded 5989 * @memberof DataTable#oApi 5990 */ 5991 function _fnLoadState ( settings, init, callback ) 5992 { 5993 if ( ! settings.oFeatures.bStateSave ) { 5994 callback(); 5995 return; 5996 } 5997 5998 var loaded = function(state) { 5999 _fnImplementState(settings, state, callback); 6000 } 6001 6002 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded ); 6003 6004 if ( state !== undefined ) { 6005 _fnImplementState( settings, state, callback ); 6006 } 6007 // otherwise, wait for the loaded callback to be executed 6008 6009 return true; 6010 } 6011 6012 function _fnImplementState ( settings, s, callback) { 6013 var i, ien; 6014 var columns = settings.aoColumns; 6015 settings._bLoadingState = true; 6016 6017 // When StateRestore was introduced the state could now be implemented at any time 6018 // Not just initialisation. To do this an api instance is required in some places 6019 var api = settings._bInitComplete ? new DataTable.Api(settings) : null; 6020 6021 if ( ! s || ! s.time ) { 6022 settings._bLoadingState = false; 6023 callback(); 6024 return; 6025 } 6026 6027 // Reject old data 6028 var duration = settings.iStateDuration; 6029 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) { 6030 settings._bLoadingState = false; 6031 callback(); 6032 return; 6033 } 6034 6035 // Allow custom and plug-in manipulation functions to alter the saved data set and 6036 // cancelling of loading by returning false 6037 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] ); 6038 if ( abStateLoad.indexOf(false) !== -1 ) { 6039 settings._bLoadingState = false; 6040 callback(); 6041 return; 6042 } 6043 6044 // Number of columns have changed - all bets are off, no restore of settings 6045 if ( s.columns && columns.length !== s.columns.length ) { 6046 settings._bLoadingState = false; 6047 callback(); 6048 return; 6049 } 6050 6051 // Store the saved state so it might be accessed at any time 6052 settings.oLoadedState = $.extend( true, {}, s ); 6053 6054 // This is needed for ColReorder, which has to happen first to allow all 6055 // the stored indexes to be usable. It is not publicly documented. 6056 _fnCallbackFire( settings, null, 'stateLoadInit', [settings, s], true ); 6057 6058 // Page Length 6059 if ( s.length !== undefined ) { 6060 // If already initialised just set the value directly so that the select element is also updated 6061 if (api) { 6062 api.page.len(s.length) 6063 } 6064 else { 6065 settings._iDisplayLength = s.length; 6066 } 6067 } 6068 6069 // Restore key features - todo - for 1.11 this needs to be done by 6070 // subscribed events 6071 if ( s.start !== undefined ) { 6072 if(api === null) { 6073 settings._iDisplayStart = s.start; 6074 settings.iInitDisplayStart = s.start; 6075 } 6076 else { 6077 _fnPageChange(settings, s.start/settings._iDisplayLength); 6078 } 6079 } 6080 6081 // Order 6082 if ( s.order !== undefined ) { 6083 settings.aaSorting = []; 6084 $.each( s.order, function ( i, col ) { 6085 settings.aaSorting.push( col[0] >= columns.length ? 6086 [ 0, col[1] ] : 6087 col 6088 ); 6089 } ); 6090 } 6091 6092 // Search 6093 if ( s.search !== undefined ) { 6094 $.extend( settings.oPreviousSearch, s.search ); 6095 } 6096 6097 // Columns 6098 if ( s.columns ) { 6099 for ( i=0, ien=s.columns.length ; i<ien ; i++ ) { 6100 var col = s.columns[i]; 6101 6102 // Visibility 6103 if ( col.visible !== undefined ) { 6104 // If the api is defined, the table has been initialised so we need to use it rather than internal settings 6105 if (api) { 6106 // Don't redraw the columns on every iteration of this loop, we will do this at the end instead 6107 api.column(i).visible(col.visible, false); 6108 } 6109 else { 6110 columns[i].bVisible = col.visible; 6111 } 6112 } 6113 6114 // Search 6115 if ( col.search !== undefined ) { 6116 $.extend( settings.aoPreSearchCols[i], col.search ); 6117 } 6118 } 6119 6120 // If the api is defined then we need to adjust the columns once the visibility has been changed 6121 if (api) { 6122 api.columns.adjust(); 6123 } 6124 } 6125 6126 settings._bLoadingState = false; 6127 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] ); 6128 callback(); 6129 } 6130 6131 /** 6132 * Log an error message 6133 * @param {object} settings dataTables settings object 6134 * @param {int} level log error messages, or display them to the user 6135 * @param {string} msg error message 6136 * @param {int} tn Technical note id to get more information about the error. 6137 * @memberof DataTable#oApi 6138 */ 6139 function _fnLog( settings, level, msg, tn ) 6140 { 6141 msg = 'DataTables warning: '+ 6142 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg; 6143 6144 if ( tn ) { 6145 msg += '. For more information about this error, please see '+ 6146 'https://datatables.net/tn/'+tn; 6147 } 6148 6149 if ( ! level ) { 6150 // Backwards compatibility pre 1.10 6151 var ext = DataTable.ext; 6152 var type = ext.sErrMode || ext.errMode; 6153 6154 if ( settings ) { 6155 _fnCallbackFire( settings, null, 'dt-error', [ settings, tn, msg ], true ); 6156 } 6157 6158 if ( type == 'alert' ) { 6159 alert( msg ); 6160 } 6161 else if ( type == 'throw' ) { 6162 throw new Error(msg); 6163 } 6164 else if ( typeof type == 'function' ) { 6165 type( settings, tn, msg ); 6166 } 6167 } 6168 else if ( window.console && console.log ) { 6169 console.log( msg ); 6170 } 6171 } 6172 6173 6174 /** 6175 * See if a property is defined on one object, if so assign it to the other object 6176 * @param {object} ret target object 6177 * @param {object} src source object 6178 * @param {string} name property 6179 * @param {string} [mappedName] name to map too - optional, name used if not given 6180 * @memberof DataTable#oApi 6181 */ 6182 function _fnMap( ret, src, name, mappedName ) 6183 { 6184 if ( Array.isArray( name ) ) { 6185 $.each( name, function (i, val) { 6186 if ( Array.isArray( val ) ) { 6187 _fnMap( ret, src, val[0], val[1] ); 6188 } 6189 else { 6190 _fnMap( ret, src, val ); 6191 } 6192 } ); 6193 6194 return; 6195 } 6196 6197 if ( mappedName === undefined ) { 6198 mappedName = name; 6199 } 6200 6201 if ( src[name] !== undefined ) { 6202 ret[mappedName] = src[name]; 6203 } 6204 } 6205 6206 6207 /** 6208 * Extend objects - very similar to jQuery.extend, but deep copy objects, and 6209 * shallow copy arrays. The reason we need to do this, is that we don't want to 6210 * deep copy array init values (such as aaSorting) since the dev wouldn't be 6211 * able to override them, but we do want to deep copy arrays. 6212 * @param {object} out Object to extend 6213 * @param {object} extender Object from which the properties will be applied to 6214 * out 6215 * @param {boolean} breakRefs If true, then arrays will be sliced to take an 6216 * independent copy with the exception of the `data` or `aaData` parameters 6217 * if they are present. This is so you can pass in a collection to 6218 * DataTables and have that used as your data source without breaking the 6219 * references 6220 * @returns {object} out Reference, just for convenience - out === the return. 6221 * @memberof DataTable#oApi 6222 * @todo This doesn't take account of arrays inside the deep copied objects. 6223 */ 6224 function _fnExtend( out, extender, breakRefs ) 6225 { 6226 var val; 6227 6228 for ( var prop in extender ) { 6229 if ( Object.prototype.hasOwnProperty.call(extender, prop) ) { 6230 val = extender[prop]; 6231 6232 if ( $.isPlainObject( val ) ) { 6233 if ( ! $.isPlainObject( out[prop] ) ) { 6234 out[prop] = {}; 6235 } 6236 $.extend( true, out[prop], val ); 6237 } 6238 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) { 6239 out[prop] = val.slice(); 6240 } 6241 else { 6242 out[prop] = val; 6243 } 6244 } 6245 } 6246 6247 return out; 6248 } 6249 6250 6251 /** 6252 * Bind an event handers to allow a click or return key to activate the callback. 6253 * This is good for accessibility since a return on the keyboard will have the 6254 * same effect as a click, if the element has focus. 6255 * @param {element} n Element to bind the action to 6256 * @param {object|string} selector Selector (for delegated events) or data object 6257 * to pass to the triggered function 6258 * @param {function} fn Callback function for when the event is triggered 6259 * @memberof DataTable#oApi 6260 */ 6261 function _fnBindAction( n, selector, fn ) 6262 { 6263 $(n) 6264 .on( 'click.DT', selector, function (e) { 6265 fn(e); 6266 } ) 6267 .on( 'keypress.DT', selector, function (e){ 6268 if ( e.which === 13 ) { 6269 e.preventDefault(); 6270 fn(e); 6271 } 6272 } ) 6273 .on( 'selectstart.DT', selector, function () { 6274 // Don't want a double click resulting in text selection 6275 return false; 6276 } ); 6277 } 6278 6279 6280 /** 6281 * Register a callback function. Easily allows a callback function to be added to 6282 * an array store of callback functions that can then all be called together. 6283 * @param {object} settings dataTables settings object 6284 * @param {string} store Name of the array storage for the callbacks in oSettings 6285 * @param {function} fn Function to be called back 6286 * @memberof DataTable#oApi 6287 */ 6288 function _fnCallbackReg( settings, store, fn ) 6289 { 6290 if ( fn ) { 6291 settings[store].push(fn); 6292 } 6293 } 6294 6295 6296 /** 6297 * Fire callback functions and trigger events. Note that the loop over the 6298 * callback array store is done backwards! Further note that you do not want to 6299 * fire off triggers in time sensitive applications (for example cell creation) 6300 * as its slow. 6301 * @param {object} settings dataTables settings object 6302 * @param {string} callbackArr Name of the array storage for the callbacks in 6303 * oSettings 6304 * @param {string} eventName Name of the jQuery custom event to trigger. If 6305 * null no trigger is fired 6306 * @param {array} args Array of arguments to pass to the callback function / 6307 * trigger 6308 * @param {boolean} [bubbles] True if the event should bubble 6309 * @memberof DataTable#oApi 6310 */ 6311 function _fnCallbackFire( settings, callbackArr, eventName, args, bubbles ) 6312 { 6313 var ret = []; 6314 6315 if ( callbackArr ) { 6316 ret = settings[callbackArr].slice().reverse().map( function (val) { 6317 return val.apply( settings.oInstance, args ); 6318 } ); 6319 } 6320 6321 if ( eventName !== null) { 6322 var e = $.Event( eventName+'.dt' ); 6323 var table = $(settings.nTable); 6324 6325 // Expose the DataTables API on the event object for easy access 6326 e.dt = settings.api; 6327 6328 table[bubbles ? 'trigger' : 'triggerHandler']( e, args ); 6329 6330 // If not yet attached to the document, trigger the event 6331 // on the body directly to sort of simulate the bubble 6332 if (bubbles && table.parents('body').length === 0) { 6333 $('body').trigger( e, args ); 6334 } 6335 6336 ret.push( e.result ); 6337 } 6338 6339 return ret; 6340 } 6341 6342 6343 function _fnLengthOverflow ( settings ) 6344 { 6345 var 6346 start = settings._iDisplayStart, 6347 end = settings.fnDisplayEnd(), 6348 len = settings._iDisplayLength; 6349 6350 /* If we have space to show extra rows (backing up from the end point - then do so */ 6351 if ( start >= end ) 6352 { 6353 start = end - len; 6354 } 6355 6356 // Keep the start record on the current page 6357 start -= (start % len); 6358 6359 if ( len === -1 || start < 0 ) 6360 { 6361 start = 0; 6362 } 6363 6364 settings._iDisplayStart = start; 6365 } 6366 6367 6368 function _fnRenderer( settings, type ) 6369 { 6370 var renderer = settings.renderer; 6371 var host = DataTable.ext.renderer[type]; 6372 6373 if ( $.isPlainObject( renderer ) && renderer[type] ) { 6374 // Specific renderer for this type. If available use it, otherwise use 6375 // the default. 6376 return host[renderer[type]] || host._; 6377 } 6378 else if ( typeof renderer === 'string' ) { 6379 // Common renderer - if there is one available for this type use it, 6380 // otherwise use the default 6381 return host[renderer] || host._; 6382 } 6383 6384 // Use the default 6385 return host._; 6386 } 6387 6388 6389 /** 6390 * Detect the data source being used for the table. Used to simplify the code 6391 * a little (ajax) and to make it compress a little smaller. 6392 * 6393 * @param {object} settings dataTables settings object 6394 * @returns {string} Data source 6395 * @memberof DataTable#oApi 6396 */ 6397 function _fnDataSource ( settings ) 6398 { 6399 if ( settings.oFeatures.bServerSide ) { 6400 return 'ssp'; 6401 } 6402 else if ( settings.ajax ) { 6403 return 'ajax'; 6404 } 6405 return 'dom'; 6406 } 6407 6408 /** 6409 * Common replacement for language strings 6410 * 6411 * @param {*} settings DT settings object 6412 * @param {*} str String with values to replace 6413 * @param {*} entries Plural number for _ENTRIES_ - can be undefined 6414 * @returns String 6415 */ 6416 function _fnMacros ( settings, str, entries ) 6417 { 6418 // When infinite scrolling, we are always starting at 1. _iDisplayStart is 6419 // used only internally 6420 var 6421 formatter = settings.fnFormatNumber, 6422 start = settings._iDisplayStart+1, 6423 len = settings._iDisplayLength, 6424 vis = settings.fnRecordsDisplay(), 6425 max = settings.fnRecordsTotal(), 6426 all = len === -1; 6427 6428 return str. 6429 replace(/_START_/g, formatter.call( settings, start ) ). 6430 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ). 6431 replace(/_MAX_/g, formatter.call( settings, max ) ). 6432 replace(/_TOTAL_/g, formatter.call( settings, vis ) ). 6433 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ). 6434 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ). 6435 replace(/_ENTRIES_/g, settings.api.i18n('entries', '', entries) ). 6436 replace(/_ENTRIES-MAX_/g, settings.api.i18n('entries', '', max) ). 6437 replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) ); 6438 } 6439 6440 6441 6442 /** 6443 * Computed structure of the DataTables API, defined by the options passed to 6444 * `DataTable.Api.register()` when building the API. 6445 * 6446 * The structure is built in order to speed creation and extension of the Api 6447 * objects since the extensions are effectively pre-parsed. 6448 * 6449 * The array is an array of objects with the following structure, where this 6450 * base array represents the Api prototype base: 6451 * 6452 * [ 6453 * { 6454 * name: 'data' -- string - Property name 6455 * val: function () {}, -- function - Api method (or undefined if just an object 6456 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6457 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6458 * }, 6459 * { 6460 * name: 'row' 6461 * val: {}, 6462 * methodExt: [ ... ], 6463 * propExt: [ 6464 * { 6465 * name: 'data' 6466 * val: function () {}, 6467 * methodExt: [ ... ], 6468 * propExt: [ ... ] 6469 * }, 6470 * ... 6471 * ] 6472 * } 6473 * ] 6474 * 6475 * @type {Array} 6476 * @ignore 6477 */ 6478 var __apiStruct = []; 6479 6480 6481 /** 6482 * `Array.prototype` reference. 6483 * 6484 * @type object 6485 * @ignore 6486 */ 6487 var __arrayProto = Array.prototype; 6488 6489 6490 /** 6491 * Abstraction for `context` parameter of the `Api` constructor to allow it to 6492 * take several different forms for ease of use. 6493 * 6494 * Each of the input parameter types will be converted to a DataTables settings 6495 * object where possible. 6496 * 6497 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one 6498 * of: 6499 * 6500 * * `string` - jQuery selector. Any DataTables' matching the given selector 6501 * with be found and used. 6502 * * `node` - `TABLE` node which has already been formed into a DataTable. 6503 * * `jQuery` - A jQuery object of `TABLE` nodes. 6504 * * `object` - DataTables settings object 6505 * * `DataTables.Api` - API instance 6506 * @return {array|null} Matching DataTables settings objects. `null` or 6507 * `undefined` is returned if no matching DataTable is found. 6508 * @ignore 6509 */ 6510 var _toSettings = function ( mixed ) 6511 { 6512 var idx, jq; 6513 var settings = DataTable.settings; 6514 var tables = _pluck(settings, 'nTable'); 6515 6516 if ( ! mixed ) { 6517 return []; 6518 } 6519 else if ( mixed.nTable && mixed.oFeatures ) { 6520 // DataTables settings object 6521 return [ mixed ]; 6522 } 6523 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) { 6524 // Table node 6525 idx = tables.indexOf(mixed); 6526 return idx !== -1 ? [ settings[idx] ] : null; 6527 } 6528 else if ( mixed && typeof mixed.settings === 'function' ) { 6529 return mixed.settings().toArray(); 6530 } 6531 else if ( typeof mixed === 'string' ) { 6532 // jQuery selector 6533 jq = $(mixed).get(); 6534 } 6535 else if ( mixed instanceof $ ) { 6536 // jQuery object (also DataTables instance) 6537 jq = mixed.get(); 6538 } 6539 6540 if ( jq ) { 6541 return settings.filter(function (v, idx) { 6542 return jq.includes(tables[idx]); 6543 }); 6544 } 6545 }; 6546 6547 6548 /** 6549 * DataTables API class - used to control and interface with one or more 6550 * DataTables enhanced tables. 6551 * 6552 * The API class is heavily based on jQuery, presenting a chainable interface 6553 * that you can use to interact with tables. Each instance of the API class has 6554 * a "context" - i.e. the tables that it will operate on. This could be a single 6555 * table, all tables on a page or a sub-set thereof. 6556 * 6557 * Additionally the API is designed to allow you to easily work with the data in 6558 * the tables, retrieving and manipulating it as required. This is done by 6559 * presenting the API class as an array like interface. The contents of the 6560 * array depend upon the actions requested by each method (for example 6561 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6562 * return an array of objects or arrays depending upon your table's 6563 * configuration). The API object has a number of array like methods (`push`, 6564 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`, 6565 * `unique` etc) to assist your working with the data held in a table. 6566 * 6567 * Most methods (those which return an Api instance) are chainable, which means 6568 * the return from a method call also has all of the methods available that the 6569 * top level object had. For example, these two calls are equivalent: 6570 * 6571 * // Not chained 6572 * api.row.add( {...} ); 6573 * api.draw(); 6574 * 6575 * // Chained 6576 * api.row.add( {...} ).draw(); 6577 * 6578 * @class DataTable.Api 6579 * @param {array|object|string|jQuery} context DataTable identifier. This is 6580 * used to define which DataTables enhanced tables this API will operate on. 6581 * Can be one of: 6582 * 6583 * * `string` - jQuery selector. Any DataTables' matching the given selector 6584 * with be found and used. 6585 * * `node` - `TABLE` node which has already been formed into a DataTable. 6586 * * `jQuery` - A jQuery object of `TABLE` nodes. 6587 * * `object` - DataTables settings object 6588 * @param {array} [data] Data to initialise the Api instance with. 6589 * 6590 * @example 6591 * // Direct initialisation during DataTables construction 6592 * var api = $('#example').DataTable(); 6593 * 6594 * @example 6595 * // Initialisation using a DataTables jQuery object 6596 * var api = $('#example').dataTable().api(); 6597 * 6598 * @example 6599 * // Initialisation as a constructor 6600 * var api = new DataTable.Api( 'table.dataTable' ); 6601 */ 6602 _Api = function ( context, data ) 6603 { 6604 if ( ! (this instanceof _Api) ) { 6605 return new _Api( context, data ); 6606 } 6607 6608 var settings = []; 6609 var ctxSettings = function ( o ) { 6610 var a = _toSettings( o ); 6611 if ( a ) { 6612 settings.push.apply( settings, a ); 6613 } 6614 }; 6615 6616 if ( Array.isArray( context ) ) { 6617 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 6618 ctxSettings( context[i] ); 6619 } 6620 } 6621 else { 6622 ctxSettings( context ); 6623 } 6624 6625 // Remove duplicates 6626 this.context = settings.length > 1 6627 ? _unique( settings ) 6628 : settings; 6629 6630 // Initial data 6631 if ( data ) { 6632 this.push.apply(this, data); 6633 } 6634 6635 // selector 6636 this.selector = { 6637 rows: null, 6638 cols: null, 6639 opts: null 6640 }; 6641 6642 _Api.extend( this, this, __apiStruct ); 6643 }; 6644 6645 DataTable.Api = _Api; 6646 6647 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's 6648 // isPlainObject. 6649 $.extend( _Api.prototype, { 6650 any: function () 6651 { 6652 return this.count() !== 0; 6653 }, 6654 6655 context: [], // array of table settings objects 6656 6657 count: function () 6658 { 6659 return this.flatten().length; 6660 }, 6661 6662 each: function ( fn ) 6663 { 6664 for ( var i=0, ien=this.length ; i<ien; i++ ) { 6665 fn.call( this, this[i], i, this ); 6666 } 6667 6668 return this; 6669 }, 6670 6671 eq: function ( idx ) 6672 { 6673 var ctx = this.context; 6674 6675 return ctx.length > idx ? 6676 new _Api( ctx[idx], this[idx] ) : 6677 null; 6678 }, 6679 6680 filter: function ( fn ) 6681 { 6682 var a = __arrayProto.filter.call( this, fn, this ); 6683 6684 return new _Api( this.context, a ); 6685 }, 6686 6687 flatten: function () 6688 { 6689 var a = []; 6690 6691 return new _Api( this.context, a.concat.apply( a, this.toArray() ) ); 6692 }, 6693 6694 get: function ( idx ) 6695 { 6696 return this[ idx ]; 6697 }, 6698 6699 join: __arrayProto.join, 6700 6701 includes: function ( find ) { 6702 return this.indexOf( find ) === -1 ? false : true; 6703 }, 6704 6705 indexOf: __arrayProto.indexOf, 6706 6707 iterator: function ( flatten, type, fn, alwaysNew ) { 6708 var 6709 a = [], ret, 6710 i, ien, j, jen, 6711 context = this.context, 6712 rows, items, item, 6713 selector = this.selector; 6714 6715 // Argument shifting 6716 if ( typeof flatten === 'string' ) { 6717 alwaysNew = fn; 6718 fn = type; 6719 type = flatten; 6720 flatten = false; 6721 } 6722 6723 for ( i=0, ien=context.length ; i<ien ; i++ ) { 6724 var apiInst = new _Api( context[i] ); 6725 6726 if ( type === 'table' ) { 6727 ret = fn.call( apiInst, context[i], i ); 6728 6729 if ( ret !== undefined ) { 6730 a.push( ret ); 6731 } 6732 } 6733 else if ( type === 'columns' || type === 'rows' ) { 6734 // this has same length as context - one entry for each table 6735 ret = fn.call( apiInst, context[i], this[i], i ); 6736 6737 if ( ret !== undefined ) { 6738 a.push( ret ); 6739 } 6740 } 6741 else if ( type === 'every' || type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) { 6742 // columns and rows share the same structure. 6743 // 'this' is an array of column indexes for each context 6744 items = this[i]; 6745 6746 if ( type === 'column-rows' ) { 6747 rows = _selector_row_indexes( context[i], selector.opts ); 6748 } 6749 6750 for ( j=0, jen=items.length ; j<jen ; j++ ) { 6751 item = items[j];
vendor: 4,127 bytes, lines 6752-6939
6752 6753 if ( type === 'cell' ) { 6754 ret = fn.call( apiInst, context[i], item.row, item.column, i, j ); 6755 } 6756 else { 6757 ret = fn.call( apiInst, context[i], item, i, j, rows ); 6758 } 6759 6760 if ( ret !== undefined ) { 6761 a.push( ret ); 6762 } 6763 } 6764 } 6765 } 6766 6767 if ( a.length || alwaysNew ) { 6768 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a ); 6769 var apiSelector = api.selector; 6770 apiSelector.rows = selector.rows; 6771 apiSelector.cols = selector.cols; 6772 apiSelector.opts = selector.opts; 6773 return api; 6774 } 6775 return this; 6776 }, 6777 6778 lastIndexOf: __arrayProto.lastIndexOf, 6779 6780 length: 0, 6781 6782 map: function ( fn ) 6783 { 6784 var a = __arrayProto.map.call( this, fn, this ); 6785 6786 return new _Api( this.context, a ); 6787 }, 6788 6789 pluck: function ( prop ) 6790 { 6791 var fn = DataTable.util.get(prop); 6792 6793 return this.map( function ( el ) { 6794 return fn(el); 6795 } ); 6796 }, 6797 6798 pop: __arrayProto.pop, 6799 6800 push: __arrayProto.push, 6801 6802 reduce: __arrayProto.reduce, 6803 6804 reduceRight: __arrayProto.reduceRight, 6805 6806 reverse: __arrayProto.reverse, 6807 6808 // Object with rows, columns and opts 6809 selector: null, 6810 6811 shift: __arrayProto.shift, 6812 6813 slice: function () { 6814 return new _Api( this.context, this ); 6815 }, 6816 6817 sort: __arrayProto.sort, 6818 6819 splice: __arrayProto.splice, 6820 6821 toArray: function () 6822 { 6823 return __arrayProto.slice.call( this ); 6824 }, 6825 6826 to$: function () 6827 { 6828 return $( this ); 6829 }, 6830 6831 toJQuery: function () 6832 { 6833 return $( this ); 6834 }, 6835 6836 unique: function () 6837 { 6838 return new _Api( this.context, _unique(this.toArray()) ); 6839 }, 6840 6841 unshift: __arrayProto.unshift 6842 } ); 6843 6844 6845 function _api_scope( scope, fn, struc ) { 6846 return function () { 6847 var ret = fn.apply( scope || this, arguments ); 6848 6849 // Method extension 6850 _Api.extend( ret, ret, struc.methodExt ); 6851 return ret; 6852 }; 6853 } 6854 6855 function _api_find( src, name ) { 6856 for ( var i=0, ien=src.length ; i<ien ; i++ ) { 6857 if ( src[i].name === name ) { 6858 return src[i]; 6859 } 6860 } 6861 return null; 6862 } 6863 6864 window.__apiStruct = __apiStruct; 6865 6866 _Api.extend = function ( scope, obj, ext ) 6867 { 6868 // Only extend API instances and static properties of the API 6869 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) { 6870 return; 6871 } 6872 6873 var 6874 i, ien, 6875 struct; 6876 6877 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 6878 struct = ext[i]; 6879 6880 if (struct.name === '__proto__') { 6881 continue; 6882 } 6883 6884 // Value 6885 obj[ struct.name ] = struct.type === 'function' ? 6886 _api_scope( scope, struct.val, struct ) : 6887 struct.type === 'object' ? 6888 {} : 6889 struct.val; 6890 6891 obj[ struct.name ].__dt_wrapper = true; 6892 6893 // Property extension 6894 _Api.extend( scope, obj[ struct.name ], struct.propExt ); 6895 } 6896 }; 6897 6898 // [ 6899 // { 6900 // name: 'data' -- string - Property name 6901 // val: function () {}, -- function - Api method (or undefined if just an object 6902 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6903 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6904 // }, 6905 // { 6906 // name: 'row' 6907 // val: {}, 6908 // methodExt: [ ... ], 6909 // propExt: [ 6910 // { 6911 // name: 'data' 6912 // val: function () {}, 6913 // methodExt: [ ... ], 6914 // propExt: [ ... ] 6915 // }, 6916 // ... 6917 // ] 6918 // } 6919 // ] 6920 6921 6922 _Api.register = _api_register = function ( name, val ) 6923 { 6924 if ( Array.isArray( name ) ) { 6925 for ( var j=0, jen=name.length ; j<jen ; j++ ) { 6926 _Api.register( name[j], val ); 6927 } 6928 return; 6929 } 6930 6931 var 6932 i, ien, 6933 heir = name.split('.'), 6934 struct = __apiStruct, 6935 key, method; 6936 6937 for ( i=0, ien=heir.length ; i<ien ; i++ ) { 6938 method = heir[i].indexOf('()') !== -1; 6939 key = method ?
6940 heir[i].replace('()', '') : 6941 heir[i]; 6942 6943 var src = _api_find( struct, key ); 6944 if ( ! src ) { 6945 src = { 6946 name: key, 6947 val: {}, 6948 methodExt: [], 6949 propExt: [], 6950 type: 'object' 6951 }; 6952 struct.push( src ); 6953 } 6954 6955 if ( i === ien-1 ) { 6956 src.val = val; 6957 src.type = typeof val === 'function' ? 6958 'function' : 6959 $.isPlainObject( val ) ? 6960 'object' : 6961 'other'; 6962 } 6963 else { 6964 struct = method ? 6965 src.methodExt : 6966 src.propExt; 6967 } 6968 } 6969 }; 6970 6971 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) { 6972 _Api.register( pluralName, val ); 6973 6974 _Api.register( singularName, function () { 6975 var ret = val.apply( this, arguments ); 6976 6977 if ( ret === this ) { 6978 // Returned item is the API instance that was passed in, return it 6979 return this; 6980 } 6981 else if ( ret instanceof _Api ) { 6982 // New API instance returned, want the value from the first item 6983 // in the returned array for the singular result. 6984 return ret.length ? 6985 Array.isArray( ret[0] ) ? 6986 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced' 6987 ret[0] : 6988 undefined; 6989 } 6990 6991 // Non-API return - just fire it back 6992 return ret; 6993 } ); 6994 }; 6995 6996 6997 /** 6998 * Selector for HTML tables. Apply the given selector to the give array of 6999 * DataTables settings objects. 7000 * 7001 * @param {string|integer} [selector] jQuery selector string or integer 7002 * @param {array} Array of DataTables settings objects to be filtered 7003 * @return {array} 7004 * @ignore 7005 */ 7006 var __table_selector = function ( selector, a ) 7007 { 7008 if ( Array.isArray(selector) ) { 7009 var result = []; 7010 7011 selector.forEach(function (sel) { 7012 var inner = __table_selector(sel, a); 7013 7014 result.push.apply(result, inner); 7015 }); 7016 7017 return result.filter( function (item) { 7018 return item; 7019 }); 7020 } 7021 7022 // Integer is used to pick out a table by index 7023 if ( typeof selector === 'number' ) { 7024 return [ a[ selector ] ]; 7025 } 7026 7027 // Perform a jQuery selector on the table nodes 7028 var nodes = a.map( function (el) { 7029 return el.nTable; 7030 } ); 7031 7032 return $(nodes) 7033 .filter( selector ) 7034 .map( function () { 7035 // Need to translate back from the table node to the settings 7036 var idx = nodes.indexOf(this); 7037 return a[ idx ]; 7038 } ) 7039 .toArray(); 7040 }; 7041 7042 7043 7044 /** 7045 * Context selector for the API's context (i.e. the tables the API instance 7046 * refers to. 7047 * 7048 * @name DataTable.Api#tables 7049 * @param {string|integer} [selector] Selector to pick which tables the iterator 7050 * should operate on. If not given, all tables in the current context are 7051 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to 7052 * select multiple tables or as an integer to select a single table. 7053 * @returns {DataTable.Api} Returns a new API instance if a selector is given. 7054 */ 7055 _api_register( 'tables()', function ( selector ) { 7056 // A new instance is created if there was a selector specified 7057 return selector !== undefined && selector !== null ? 7058 new _Api( __table_selector( selector, this.context ) ) : 7059 this; 7060 } ); 7061 7062 7063 _api_register( 'table()', function ( selector ) { 7064 var tables = this.tables( selector ); 7065 var ctx = tables.context; 7066 7067 // Truncate to the first matched table 7068 return ctx.length ? 7069 new _Api( ctx[0] ) : 7070 tables; 7071 } ); 7072 7073 // Common methods, combined to reduce size 7074 [ 7075 ['nodes', 'node', 'nTable'], 7076 ['body', 'body', 'nTBody'], 7077 ['header', 'header', 'nTHead'], 7078 ['footer', 'footer', 'nTFoot'], 7079 ].forEach(function (item) { 7080 _api_registerPlural( 7081 'tables().' + item[0] + '()', 7082 'table().' + item[1] + '()' , 7083 function () { 7084 return this.iterator( 'table', function ( ctx ) { 7085 return ctx[item[2]]; 7086 }, 1 ); 7087 } 7088 ); 7089 }); 7090 7091 // Structure methods 7092 [ 7093 ['header', 'aoHeader'], 7094 ['footer', 'aoFooter'], 7095 ].forEach(function (item) { 7096 _api_register( 'table().' + item[0] + '.structure()' , function (selector) { 7097 var indexes = this.columns(selector).indexes().flatten(); 7098 var ctx = this.context[0]; 7099 7100 return _fnHeaderLayout(ctx, ctx[item[1]], indexes); 7101 } ); 7102 }) 7103 7104 7105 _api_registerPlural( 'tables().containers()', 'table().container()' , function () { 7106 return this.iterator( 'table', function ( ctx ) { 7107 return ctx.nTableWrapper; 7108 }, 1 ); 7109 } ); 7110 7111 _api_register( 'tables().every()', function ( fn ) { 7112 var that = this; 7113 7114 return this.iterator('table', function (s, i) { 7115 fn.call(that.table(i), i); 7116 }); 7117 }); 7118 7119 _api_register( 'caption()', function ( value, side ) { 7120 var context = this.context; 7121 7122 // Getter - return existing node's content 7123 if ( value === undefined ) { 7124 var caption = context[0].captionNode; 7125 7126 return caption && context.length ? 7127 caption.innerHTML : 7128 null; 7129 } 7130 7131 return this.iterator( 'table', function ( ctx ) { 7132 var table = $(ctx.nTable); 7133 var caption = $(ctx.captionNode); 7134 var container = $(ctx.nTableWrapper); 7135 7136 // Create the node if it doesn't exist yet 7137 if ( ! caption.length ) { 7138 caption = $('<caption/>').html( value ); 7139 ctx.captionNode = caption[0]; 7140 7141 // If side isn't set, we need to insert into the document to let the 7142 // CSS decide so we can read it back, otherwise there is no way to 7143 // know if the CSS would put it top or bottom for scrolling
vendor: 7,380 bytes, lines 7144-7409
7144 if (! side) { 7145 table.prepend(caption); 7146 7147 side = caption.css('caption-side'); 7148 } 7149 } 7150 7151 caption.html( value ); 7152 7153 if ( side ) { 7154 caption.css( 'caption-side', side ); 7155 caption[0]._captionSide = side; 7156 } 7157 7158 if (container.find('div.dataTables_scroll').length) { 7159 var selector = (side === 'top' ? 'Head' : 'Foot'); 7160 7161 container.find('div.dataTables_scroll'+ selector +' table').prepend(caption); 7162 } 7163 else { 7164 table.prepend(caption); 7165 } 7166 }, 1 ); 7167 } ); 7168 7169 _api_register( 'caption.node()', function () { 7170 var ctx = this.context; 7171 7172 return ctx.length ? ctx[0].captionNode : null; 7173 } ); 7174 7175 7176 /** 7177 * Redraw the tables in the current context. 7178 */ 7179 _api_register( 'draw()', function ( paging ) { 7180 return this.iterator( 'table', function ( settings ) { 7181 if ( paging === 'page' ) { 7182 _fnDraw( settings ); 7183 } 7184 else { 7185 if ( typeof paging === 'string' ) { 7186 paging = paging === 'full-hold' ? 7187 false : 7188 true; 7189 } 7190 7191 _fnReDraw( settings, paging===false ); 7192 } 7193 } ); 7194 } ); 7195 7196 7197 7198 /** 7199 * Get the current page index. 7200 * 7201 * @return {integer} Current page index (zero based) 7202 *//** 7203 * Set the current page. 7204 * 7205 * Note that if you attempt to show a page which does not exist, DataTables will 7206 * not throw an error, but rather reset the paging. 7207 * 7208 * @param {integer|string} action The paging action to take. This can be one of: 7209 * * `integer` - The page index to jump to 7210 * * `string` - An action to take: 7211 * * `first` - Jump to first page. 7212 * * `next` - Jump to the next page 7213 * * `previous` - Jump to previous page 7214 * * `last` - Jump to the last page. 7215 * @returns {DataTables.Api} this 7216 */ 7217 _api_register( 'page()', function ( action ) { 7218 if ( action === undefined ) { 7219 return this.page.info().page; // not an expensive call 7220 } 7221 7222 // else, have an action to take on all tables 7223 return this.iterator( 'table', function ( settings ) { 7224 _fnPageChange( settings, action ); 7225 } ); 7226 } ); 7227 7228 7229 /** 7230 * Paging information for the first table in the current context. 7231 * 7232 * If you require paging information for another table, use the `table()` method 7233 * with a suitable selector. 7234 * 7235 * @return {object} Object with the following properties set: 7236 * * `page` - Current page index (zero based - i.e. the first page is `0`) 7237 * * `pages` - Total number of pages 7238 * * `start` - Display index for the first record shown on the current page 7239 * * `end` - Display index for the last record shown on the current page 7240 * * `length` - Display length (number of records). Note that generally `start 7241 * + length = end`, but this is not always true, for example if there are 7242 * only 2 records to show on the final page, with a length of 10. 7243 * * `recordsTotal` - Full data set length 7244 * * `recordsDisplay` - Data set length once the current filtering criterion 7245 * are applied. 7246 */ 7247 _api_register( 'page.info()', function () { 7248 if ( this.context.length === 0 ) { 7249 return undefined; 7250 } 7251 7252 var 7253 settings = this.context[0], 7254 start = settings._iDisplayStart, 7255 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1, 7256 visRecords = settings.fnRecordsDisplay(), 7257 all = len === -1; 7258 7259 return { 7260 "page": all ? 0 : Math.floor( start / len ), 7261 "pages": all ? 1 : Math.ceil( visRecords / len ), 7262 "start": start, 7263 "end": settings.fnDisplayEnd(), 7264 "length": len, 7265 "recordsTotal": settings.fnRecordsTotal(), 7266 "recordsDisplay": visRecords, 7267 "serverSide": _fnDataSource( settings ) === 'ssp' 7268 }; 7269 } ); 7270 7271 7272 /** 7273 * Get the current page length. 7274 * 7275 * @return {integer} Current page length. Note `-1` indicates that all records 7276 * are to be shown. 7277 *//** 7278 * Set the current page length. 7279 * 7280 * @param {integer} Page length to set. Use `-1` to show all records. 7281 * @returns {DataTables.Api} this 7282 */ 7283 _api_register( 'page.len()', function ( len ) { 7284 // Note that we can't call this function 'length()' because `length` 7285 // is a Javascript property of functions which defines how many arguments 7286 // the function expects. 7287 if ( len === undefined ) { 7288 return this.context.length !== 0 ? 7289 this.context[0]._iDisplayLength : 7290 undefined; 7291 } 7292 7293 // else, set the page length 7294 return this.iterator( 'table', function ( settings ) { 7295 _fnLengthChange( settings, len ); 7296 } ); 7297 } ); 7298 7299 7300 7301 var __reload = function ( settings, holdPosition, callback ) { 7302 // Use the draw event to trigger a callback 7303 if ( callback ) { 7304 var api = new _Api( settings ); 7305 7306 api.one( 'draw', function () { 7307 callback( api.ajax.json() ); 7308 } ); 7309 } 7310 7311 if ( _fnDataSource( settings ) == 'ssp' ) { 7312 _fnReDraw( settings, holdPosition ); 7313 } 7314 else { 7315 _fnProcessingDisplay( settings, true ); 7316 7317 // Cancel an existing request 7318 var xhr = settings.jqXHR; 7319 if ( xhr && xhr.readyState !== 4 ) { 7320 xhr.abort(); 7321 } 7322 7323 // Trigger xhr 7324 _fnBuildAjax( settings, {}, function( json ) { 7325 _fnClearTable( settings ); 7326 7327 var data = _fnAjaxDataSrc( settings, json ); 7328 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 7329 _fnAddData( settings, data[i] ); 7330 } 7331 7332 _fnReDraw( settings, holdPosition ); 7333 _fnInitComplete( settings ); 7334 _fnProcessingDisplay( settings, false ); 7335 } ); 7336 } 7337 }; 7338 7339 7340 /** 7341 * Get the JSON response from the last Ajax request that DataTables made to the 7342 * server. Note that this returns the JSON from the first table in the current 7343 * context. 7344 * 7345 * @return {object} JSON received from the server. 7346 */ 7347 _api_register( 'ajax.json()', function () { 7348 var ctx = this.context; 7349 7350 if ( ctx.length > 0 ) { 7351 return ctx[0].json; 7352 } 7353 7354 // else return undefined; 7355 } ); 7356 7357 7358 /** 7359 * Get the data submitted in the last Ajax request 7360 */ 7361 _api_register( 'ajax.params()', function () { 7362 var ctx = this.context; 7363 7364 if ( ctx.length > 0 ) { 7365 return ctx[0].oAjaxData; 7366 } 7367 7368 // else return undefined; 7369 } ); 7370 7371 7372 /** 7373 * Reload tables from the Ajax data source. Note that this function will 7374 * automatically re-draw the table when the remote data has been loaded. 7375 * 7376 * @param {boolean} [reset=true] Reset (default) or hold the current paging 7377 * position. A full re-sort and re-filter is performed when this method is 7378 * called, which is why the pagination reset is the default action. 7379 * @returns {DataTables.Api} this 7380 */ 7381 _api_register( 'ajax.reload()', function ( callback, resetPaging ) { 7382 return this.iterator( 'table', function (settings) { 7383 __reload( settings, resetPaging===false, callback ); 7384 } ); 7385 } ); 7386 7387 7388 /** 7389 * Get the current Ajax URL. Note that this returns the URL from the first 7390 * table in the current context. 7391 * 7392 * @return {string} Current Ajax source URL 7393 *//** 7394 * Set the Ajax URL. Note that this will set the URL for all tables in the 7395 * current context. 7396 * 7397 * @param {string} url URL to set. 7398 * @returns {DataTables.Api} this 7399 */ 7400 _api_register( 'ajax.url()', function ( url ) { 7401 var ctx = this.context; 7402 7403 if ( url === undefined ) { 7404 // get 7405 if ( ctx.length === 0 ) { 7406 return undefined; 7407 } 7408 ctx = ctx[0]; 7409
7410 return $.isPlainObject( ctx.ajax ) ? 7411 ctx.ajax.url : 7412 ctx.ajax; 7413 } 7414 7415 // set 7416 return this.iterator( 'table', function ( settings ) { 7417 if ( $.isPlainObject( settings.ajax ) ) { 7418 settings.ajax.url = url; 7419 } 7420 else { 7421 settings.ajax = url; 7422 } 7423 } ); 7424 } ); 7425 7426 7427 /** 7428 * Load data from the newly set Ajax URL. Note that this method is only 7429 * available when `ajax.url()` is used to set a URL. Additionally, this method 7430 * has the same effect as calling `ajax.reload()` but is provided for 7431 * convenience when setting a new URL. Like `ajax.reload()` it will 7432 * automatically redraw the table once the remote data has been loaded. 7433 * 7434 * @returns {DataTables.Api} this 7435 */ 7436 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) { 7437 // Same as a reload, but makes sense to present it for easy access after a 7438 // url change 7439 return this.iterator( 'table', function ( ctx ) { 7440 __reload( ctx, resetPaging===false, callback ); 7441 } ); 7442 } ); 7443 7444 7445 7446 7447 var _selector_run = function ( type, selector, selectFn, settings, opts ) 7448 { 7449 var 7450 out = [], res, 7451 a, i, ien, j, jen, 7452 selectorType = typeof selector; 7453 7454 // Can't just check for isArray here, as an API or jQuery instance might be 7455 // given with their array like look 7456 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) { 7457 selector = [ selector ]; 7458 } 7459 7460 for ( i=0, ien=selector.length ; i<ien ; i++ ) { 7461 // Only split on simple strings - complex expressions will be jQuery selectors 7462 a = selector[i] && selector[i].split && ! selector[i].match(/[[(:]/) ? 7463 selector[i].split(',') : 7464 [ selector[i] ]; 7465 7466 for ( j=0, jen=a.length ; j<jen ; j++ ) { 7467 res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] ); 7468 7469 // Remove empty items 7470 res = res.filter( function (item) { 7471 return item !== null && item !== undefined; 7472 }); 7473 7474 if ( res && res.length ) { 7475 out = out.concat( res ); 7476 } 7477 } 7478 } 7479 7480 // selector extensions 7481 var ext = _ext.selector[ type ]; 7482 if ( ext.length ) { 7483 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 7484 out = ext[i]( settings, opts, out ); 7485 } 7486 } 7487 7488 return _unique( out ); 7489 }; 7490 7491 7492 var _selector_opts = function ( opts ) 7493 { 7494 if ( ! opts ) { 7495 opts = {}; 7496 } 7497 7498 // Backwards compatibility for 1.9- which used the terminology filter rather 7499 // than search 7500 if ( opts.filter && opts.search === undefined ) { 7501 opts.search = opts.filter; 7502 } 7503 7504 return $.extend( { 7505 search: 'none', 7506 order: 'current', 7507 page: 'all' 7508 }, opts ); 7509 }; 7510 7511 7512 // Reduce the API instance to the first item found 7513 var _selector_first = function ( old ) 7514 { 7515 let inst = new _Api(old.context[0]); 7516 7517 // Use a push rather than passing to the constructor, since it will 7518 // merge arrays down automatically, which isn't what is wanted here 7519 if (old.length) { 7520 inst.push( old[0] ); 7521 } 7522 7523 inst.selector = old.selector; 7524 7525 // Limit to a single row / column / cell 7526 if (inst.length && inst[0].length > 1) { 7527 inst[0].splice(1); 7528 } 7529 7530 return inst; 7531 }; 7532 7533 7534 var _selector_row_indexes = function ( settings, opts ) 7535 { 7536 var 7537 i, ien, tmp, a=[], 7538 displayFiltered = settings.aiDisplay, 7539 displayMaster = settings.aiDisplayMaster; 7540 7541 var 7542 search = opts.search, // none, applied, removed 7543 order = opts.order, // applied, current, index (original - compatibility with 1.9) 7544 page = opts.page; // all, current 7545 7546 if ( page == 'current' ) { 7547 // Current page implies that order=current and filter=applied, since it is 7548 // fairly senseless otherwise, regardless of what order and search actually 7549 // are 7550 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) { 7551 a.push( displayFiltered[i] ); 7552 } 7553 } 7554 else if ( order == 'current' || order == 'applied' ) { 7555 if ( search == 'none') { 7556 a = displayMaster.slice(); 7557 } 7558 else if ( search == 'applied' ) { 7559 a = displayFiltered.slice(); 7560 } 7561 else if ( search == 'removed' ) { 7562 // O(n+m) solution by creating a hash map 7563 var displayFilteredMap = {}; 7564 7565 for ( i=0, ien=displayFiltered.length ; i<ien ; i++ ) { 7566 displayFilteredMap[displayFiltered[i]] = null; 7567 } 7568 7569 displayMaster.forEach(function (item) { 7570 if (! Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) { 7571 a.push(item); 7572 } 7573 }); 7574 } 7575 } 7576 else if ( order == 'index' || order == 'original' ) { 7577 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
vendor: 16,850 bytes, lines 7578-8210
7578 if (! settings.aoData[i]) { 7579 continue; 7580 } 7581 7582 if ( search == 'none' ) { 7583 a.push( i ); 7584 } 7585 else { // applied | removed 7586 tmp = displayFiltered.indexOf(i); 7587 7588 if ((tmp === -1 && search == 'removed') || 7589 (tmp >= 0 && search == 'applied') ) 7590 { 7591 a.push( i ); 7592 } 7593 } 7594 } 7595 } 7596 else if ( typeof order === 'number' ) { 7597 // Order the rows by the given column 7598 var ordered = _fnSort(settings, order, 'asc'); 7599 7600 if (search === 'none') { 7601 a = ordered; 7602 } 7603 else { // applied | removed 7604 for (i=0; i<ordered.length; i++) { 7605 tmp = displayFiltered.indexOf(ordered[i]); 7606 7607 if ((tmp === -1 && search == 'removed') || 7608 (tmp >= 0 && search == 'applied') ) 7609 { 7610 a.push( ordered[i] ); 7611 } 7612 } 7613 } 7614 } 7615 7616 return a; 7617 }; 7618 7619 7620 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 7621 * Rows 7622 * 7623 * {} - no selector - use all available rows 7624 * {integer} - row aoData index 7625 * {node} - TR node 7626 * {string} - jQuery selector to apply to the TR elements 7627 * {array} - jQuery array of nodes, or simply an array of TR nodes 7628 * 7629 */ 7630 var __row_selector = function ( settings, selector, opts ) 7631 { 7632 var rows; 7633 var run = function ( sel ) { 7634 var selInt = _intVal( sel ); 7635 var aoData = settings.aoData; 7636 7637 // Short cut - selector is a number and no options provided (default is 7638 // all records, so no need to check if the index is in there, since it 7639 // must be - dev error if the index doesn't exist). 7640 if ( selInt !== null && ! opts ) { 7641 return [ selInt ]; 7642 } 7643 7644 if ( ! rows ) { 7645 rows = _selector_row_indexes( settings, opts ); 7646 } 7647 7648 if ( selInt !== null && rows.indexOf(selInt) !== -1 ) { 7649 // Selector - integer 7650 return [ selInt ]; 7651 } 7652 else if ( sel === null || sel === undefined || sel === '' ) { 7653 // Selector - none 7654 return rows; 7655 } 7656 7657 // Selector - function 7658 if ( typeof sel === 'function' ) { 7659 return rows.map( function (idx) { 7660 var row = aoData[ idx ]; 7661 return sel( idx, row._aData, row.nTr ) ? idx : null; 7662 } ); 7663 } 7664 7665 // Selector - node 7666 if ( sel.nodeName ) { 7667 var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup 7668 var cellIdx = sel._DT_CellIndex; 7669 7670 if ( rowIdx !== undefined ) { 7671 // Make sure that the row is actually still present in the table 7672 return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ? 7673 [ rowIdx ] : 7674 []; 7675 } 7676 else if ( cellIdx ) { 7677 return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ? 7678 [ cellIdx.row ] : 7679 []; 7680 } 7681 else { 7682 var host = $(sel).closest('*[data-dt-row]'); 7683 return host.length ? 7684 [ host.data('dt-row') ] : 7685 []; 7686 } 7687 } 7688 7689 // ID selector. Want to always be able to select rows by id, regardless 7690 // of if the tr element has been created or not, so can't rely upon 7691 // jQuery here - hence a custom implementation. This does not match 7692 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything, 7693 // but to select it using a CSS selector engine (like Sizzle or 7694 // querySelect) it would need to need to be escaped for some characters. 7695 // DataTables simplifies this for row selectors since you can select 7696 // only a row. A # indicates an id any anything that follows is the id - 7697 // unescaped. 7698 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) { 7699 // get row index from id 7700 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ]; 7701 if ( rowObj !== undefined ) { 7702 return [ rowObj.idx ]; 7703 } 7704 7705 // need to fall through to jQuery in case there is DOM id that 7706 // matches 7707 } 7708 7709 // Get nodes in the order from the `rows` array with null values removed 7710 var nodes = _removeEmpty( 7711 _pluck_order( settings.aoData, rows, 'nTr' ) 7712 ); 7713 7714 // Selector - jQuery selector string, array of nodes or jQuery object/ 7715 // As jQuery's .filter() allows jQuery objects to be passed in filter, 7716 // it also allows arrays, so this will cope with all three options 7717 return $(nodes) 7718 .filter( sel ) 7719 .map( function () { 7720 return this._DT_RowIndex; 7721 } ) 7722 .toArray(); 7723 }; 7724 7725 var matched = _selector_run( 'row', selector, run, settings, opts ); 7726 7727 if (opts.order === 'current' || opts.order === 'applied') { 7728 _fnSortDisplay(settings, matched); 7729 } 7730 7731 return matched; 7732 }; 7733 7734 7735 _api_register( 'rows()', function ( selector, opts ) { 7736 // argument shifting 7737 if ( selector === undefined ) { 7738 selector = ''; 7739 } 7740 else if ( $.isPlainObject( selector ) ) { 7741 opts = selector; 7742 selector = ''; 7743 } 7744 7745 opts = _selector_opts( opts ); 7746 7747 var inst = this.iterator( 'table', function ( settings ) { 7748 return __row_selector( settings, selector, opts ); 7749 }, 1 ); 7750 7751 // Want argument shifting here and in __row_selector? 7752 inst.selector.rows = selector; 7753 inst.selector.opts = opts; 7754 7755 return inst; 7756 } ); 7757 7758 _api_register( 'rows().nodes()', function () { 7759 return this.iterator( 'row', function ( settings, row ) { 7760 return settings.aoData[ row ].nTr || undefined; 7761 }, 1 ); 7762 } ); 7763 7764 _api_register( 'rows().data()', function () { 7765 return this.iterator( true, 'rows', function ( settings, rows ) { 7766 return _pluck_order( settings.aoData, rows, '_aData' ); 7767 }, 1 ); 7768 } ); 7769 7770 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) { 7771 return this.iterator( 'row', function ( settings, row ) { 7772 var r = settings.aoData[ row ]; 7773 return type === 'search' ? r._aFilterData : r._aSortData; 7774 }, 1 ); 7775 } ); 7776 7777 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) { 7778 return this.iterator( 'row', function ( settings, row ) { 7779 _fnInvalidate( settings, row, src ); 7780 } ); 7781 } ); 7782 7783 _api_registerPlural( 'rows().indexes()', 'row().index()', function () { 7784 return this.iterator( 'row', function ( settings, row ) { 7785 return row; 7786 }, 1 ); 7787 } ); 7788 7789 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) { 7790 var a = []; 7791 var context = this.context; 7792 7793 // `iterator` will drop undefined values, but in this case we want them 7794 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 7795 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) { 7796 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData ); 7797 a.push( (hash === true ? '#' : '' )+ id ); 7798 } 7799 } 7800 7801 return new _Api( context, a ); 7802 } ); 7803 7804 _api_registerPlural( 'rows().remove()', 'row().remove()', function () { 7805 this.iterator( 'row', function ( settings, row ) { 7806 var data = settings.aoData; 7807 var rowData = data[ row ]; 7808 7809 // Delete from the display arrays 7810 var idx = settings.aiDisplayMaster.indexOf(row); 7811 if (idx !== -1) { 7812 settings.aiDisplayMaster.splice(idx, 1); 7813 } 7814 7815 // For server-side processing tables - subtract the deleted row from the count 7816 if ( settings._iRecordsDisplay > 0 ) { 7817 settings._iRecordsDisplay--; 7818 } 7819 7820 // Check for an 'overflow' they case for displaying the table 7821 _fnLengthOverflow( settings ); 7822 7823 // Remove the row's ID reference if there is one 7824 var id = settings.rowIdFn( rowData._aData ); 7825 if ( id !== undefined ) { 7826 delete settings.aIds[ id ]; 7827 } 7828 7829 data[row] = null; 7830 } ); 7831 7832 return this; 7833 } ); 7834 7835 7836 _api_register( 'rows.add()', function ( rows ) { 7837 var newRows = this.iterator( 'table', function ( settings ) { 7838 var row, i, ien; 7839 var out = []; 7840 7841 for ( i=0, ien=rows.length ; i<ien ; i++ ) { 7842 row = rows[i]; 7843 7844 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 7845 out.push( _fnAddTr( settings, row )[0] ); 7846 } 7847 else { 7848 out.push( _fnAddData( settings, row ) ); 7849 } 7850 } 7851 7852 return out; 7853 }, 1 ); 7854 7855 // Return an Api.rows() extended instance, so rows().nodes() etc can be used 7856 var modRows = this.rows( -1 ); 7857 modRows.pop(); 7858 modRows.push.apply(modRows, newRows); 7859 7860 return modRows; 7861 } ); 7862 7863 7864 7865 7866 7867 /** 7868 * 7869 */ 7870 _api_register( 'row()', function ( selector, opts ) { 7871 return _selector_first( this.rows( selector, opts ) ); 7872 } ); 7873 7874 7875 _api_register( 'row().data()', function ( data ) { 7876 var ctx = this.context; 7877 7878 if ( data === undefined ) { 7879 // Get 7880 return ctx.length && this.length && this[0].length ? 7881 ctx[0].aoData[ this[0] ]._aData : 7882 undefined; 7883 } 7884 7885 // Set 7886 var row = ctx[0].aoData[ this[0] ]; 7887 row._aData = data; 7888 7889 // If the DOM has an id, and the data source is an array 7890 if ( Array.isArray( data ) && row.nTr && row.nTr.id ) { 7891 _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id ); 7892 } 7893 7894 // Automatically invalidate 7895 _fnInvalidate( ctx[0], this[0], 'data' ); 7896 7897 return this; 7898 } ); 7899 7900 7901 _api_register( 'row().node()', function () { 7902 var ctx = this.context; 7903 7904 return ctx.length && this.length && this[0].length ? 7905 ctx[0].aoData[ this[0] ].nTr || null : 7906 null; 7907 } ); 7908 7909 7910 _api_register( 'row.add()', function ( row ) { 7911 // Allow a jQuery object to be passed in - only a single row is added from 7912 // it though - the first element in the set 7913 if ( row instanceof $ && row.length ) { 7914 row = row[0]; 7915 } 7916 7917 var rows = this.iterator( 'table', function ( settings ) { 7918 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 7919 return _fnAddTr( settings, row )[0]; 7920 } 7921 return _fnAddData( settings, row ); 7922 } ); 7923 7924 // Return an Api.rows() extended instance, with the newly added row selected 7925 return this.row( rows[0] ); 7926 } ); 7927 7928 7929 $(document).on('plugin-init.dt', function (e, context) { 7930 var api = new _Api( context ); 7931 7932 api.on( 'stateSaveParams.DT', function ( e, settings, d ) { 7933 // This could be more compact with the API, but it is a lot faster as a simple 7934 // internal loop 7935 var idFn = settings.rowIdFn; 7936 var rows = settings.aiDisplayMaster; 7937 var ids = []; 7938 7939 for (var i=0 ; i<rows.length ; i++) { 7940 var rowIdx = rows[i]; 7941 var data = settings.aoData[rowIdx]; 7942 7943 if (data._detailsShow) { 7944 ids.push( '#' + idFn(data._aData) ); 7945 } 7946 } 7947 7948 d.childRows = ids; 7949 }); 7950 7951 // For future state loads (e.g. with StateRestore) 7952 api.on( 'stateLoaded.DT', function (e, settings, state) { 7953 __details_state_load( api, state ); 7954 }); 7955 7956 // And the initial load state 7957 __details_state_load( api, api.state.loaded() ); 7958 }); 7959 7960 var __details_state_load = function (api, state) 7961 { 7962 if ( state && state.childRows ) { 7963 api 7964 .rows( state.childRows.map(function (id) { 7965 // Escape any `:` characters from the row id, unless previously escaped 7966 return id.replace(/(?<!\\):/g, '\\:'); 7967 }) ) 7968 .every( function () { 7969 _fnCallbackFire( api.settings()[0], null, 'requestChild', [ this ] ) 7970 }); 7971 } 7972 } 7973 7974 var __details_add = function ( ctx, row, data, klass ) 7975 { 7976 // Convert to array of TR elements 7977 var rows = []; 7978 var addRow = function ( r, k ) { 7979 // Recursion to allow for arrays of jQuery objects 7980 if ( Array.isArray( r ) || r instanceof $ ) { 7981 for ( var i=0, ien=r.length ; i<ien ; i++ ) { 7982 addRow( r[i], k ); 7983 } 7984 return; 7985 } 7986 7987 // If we get a TR element, then just add it directly - up to the dev 7988 // to add the correct number of columns etc 7989 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) { 7990 r.setAttribute( 'data-dt-row', row.idx ); 7991 rows.push( r ); 7992 } 7993 else { 7994 // Otherwise create a row with a wrapper 7995 var created = $('<tr><td></td></tr>') 7996 .attr( 'data-dt-row', row.idx ) 7997 .addClass( k ); 7998 7999 $('td', created) 8000 .addClass( k ) 8001 .html( r )[0].colSpan = _fnVisbleColumns( ctx ); 8002 8003 rows.push( created[0] ); 8004 } 8005 }; 8006 8007 addRow( data, klass ); 8008 8009 if ( row._details ) { 8010 row._details.detach(); 8011 } 8012 8013 row._details = $(rows); 8014 8015 // If the children were already shown, that state should be retained 8016 if ( row._detailsShow ) { 8017 row._details.insertAfter( row.nTr ); 8018 } 8019 }; 8020 8021 8022 // Make state saving of child row details async to allow them to be batch processed 8023 var __details_state = DataTable.util.throttle( 8024 function (ctx) { 8025 _fnSaveState( ctx[0] ) 8026 }, 8027 500 8028 ); 8029 8030 8031 var __details_remove = function ( api, idx ) 8032 { 8033 var ctx = api.context; 8034 8035 if ( ctx.length ) { 8036 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ]; 8037 8038 if ( row && row._details ) { 8039 row._details.remove(); 8040 8041 row._detailsShow = undefined; 8042 row._details = undefined; 8043 $( row.nTr ).removeClass( 'dt-hasChild' ); 8044 __details_state( ctx ); 8045 } 8046 } 8047 }; 8048 8049 8050 var __details_display = function ( api, show ) { 8051 var ctx = api.context; 8052 8053 if ( ctx.length && api.length ) { 8054 var row = ctx[0].aoData[ api[0] ]; 8055 8056 if ( row._details ) { 8057 row._detailsShow = show; 8058 8059 if ( show ) { 8060 row._details.insertAfter( row.nTr ); 8061 $( row.nTr ).addClass( 'dt-hasChild' ); 8062 } 8063 else { 8064 row._details.detach(); 8065 $( row.nTr ).removeClass( 'dt-hasChild' ); 8066 } 8067 8068 _fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] ) 8069 8070 __details_events( ctx[0] ); 8071 __details_state( ctx ); 8072 } 8073 } 8074 }; 8075 8076 8077 var __details_events = function ( settings ) 8078 { 8079 var api = new _Api( settings ); 8080 var namespace = '.dt.DT_details'; 8081 var drawEvent = 'draw'+namespace; 8082 var colvisEvent = 'column-sizing'+namespace; 8083 var destroyEvent = 'destroy'+namespace; 8084 var data = settings.aoData; 8085 8086 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent ); 8087 8088 if ( _pluck( data, '_details' ).length > 0 ) { 8089 // On each draw, insert the required elements into the document 8090 api.on( drawEvent, function ( e, ctx ) { 8091 if ( settings !== ctx ) { 8092 return; 8093 } 8094 8095 api.rows( {page:'current'} ).eq(0).each( function (idx) { 8096 // Internal data grab 8097 var row = data[ idx ]; 8098 8099 if ( row._detailsShow ) { 8100 row._details.insertAfter( row.nTr ); 8101 } 8102 } ); 8103 } ); 8104 8105 // Column visibility change - update the colspan 8106 api.on( colvisEvent, function ( e, ctx ) { 8107 if ( settings !== ctx ) { 8108 return; 8109 } 8110 8111 // Update the colspan for the details rows (note, only if it already has 8112 // a colspan) 8113 var row, visible = _fnVisbleColumns( ctx ); 8114 8115 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8116 row = data[i]; 8117 8118 if ( row && row._details ) { 8119 row._details.each(function () { 8120 var el = $(this).children('td'); 8121 8122 if (el.length == 1) { 8123 el.attr('colspan', visible); 8124 } 8125 }); 8126 } 8127 } 8128 } ); 8129 8130 // Table destroyed - nuke any child rows 8131 api.on( destroyEvent, function ( e, ctx ) { 8132 if ( settings !== ctx ) { 8133 return; 8134 } 8135 8136 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8137 if ( data[i] && data[i]._details ) { 8138 __details_remove( api, i ); 8139 } 8140 } 8141 } ); 8142 } 8143 }; 8144 8145 // Strings for the method names to help minification 8146 var _emp = ''; 8147 var _child_obj = _emp+'row().child'; 8148 var _child_mth = _child_obj+'()'; 8149 8150 // data can be: 8151 // tr 8152 // string 8153 // jQuery or array of any of the above 8154 _api_register( _child_mth, function ( data, klass ) { 8155 var ctx = this.context; 8156 8157 if ( data === undefined ) { 8158 // get 8159 return ctx.length && this.length && ctx[0].aoData[ this[0] ] 8160 ? ctx[0].aoData[ this[0] ]._details 8161 : undefined; 8162 } 8163 else if ( data === true ) { 8164 // show 8165 this.child.show(); 8166 } 8167 else if ( data === false ) { 8168 // remove 8169 __details_remove( this ); 8170 } 8171 else if ( ctx.length && this.length ) { 8172 // set 8173 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass ); 8174 } 8175 8176 return this; 8177 } ); 8178 8179 8180 _api_register( [ 8181 _child_obj+'.show()', 8182 _child_mth+'.show()' // only when `child()` was called with parameters (without 8183 ], function () { // it returns an object and this method is not executed) 8184 __details_display( this, true ); 8185 return this; 8186 } ); 8187 8188 8189 _api_register( [ 8190 _child_obj+'.hide()', 8191 _child_mth+'.hide()' // only when `child()` was called with parameters (without 8192 ], function () { // it returns an object and this method is not executed) 8193 __details_display( this, false ); 8194 return this; 8195 } ); 8196 8197 8198 _api_register( [ 8199 _child_obj+'.remove()', 8200 _child_mth+'.remove()' // only when `child()` was called with parameters (without 8201 ], function () { // it returns an object and this method is not executed) 8202 __details_remove( this ); 8203 return this; 8204 } ); 8205 8206 8207 _api_register( _child_obj+'.isShown()', function () { 8208 var ctx = this.context; 8209 8210 if ( ctx.length && this.length ) {
8211 // _detailsShown as false or undefined will fall through to return false 8212 return ctx[0].aoData[ this[0] ]._detailsShow || false; 8213 } 8214 return false; 8215 } ); 8216 8217 8218 8219 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8220 * Columns 8221 * 8222 * {integer} - column index (>=0 count from left, <0 count from right) 8223 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) 8224 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) 8225 * "{string}:name" - column name 8226 * "{string}" - jQuery selector on column header nodes 8227 * 8228 */ 8229 8230 // can be an array of these items, comma separated list, or an array of comma 8231 // separated lists 8232 8233 var __re_column_selector = /^([^:]+):(name|title|visIdx|visible)$/; 8234 8235 8236 // r1 and r2 are redundant - but it means that the parameters match for the 8237 // iterator callback in columns().data() 8238 var __columnData = function ( settings, column, r1, r2, rows, type ) { 8239 var a = []; 8240 for ( var row=0, ien=rows.length ; row<ien ; row++ ) { 8241 a.push( _fnGetCellData( settings, rows[row], column, type ) ); 8242 } 8243 return a; 8244 }; 8245 8246 8247 var __column_header = function ( settings, column, row ) { 8248 var header = settings.aoHeader; 8249 var target = row !== undefined 8250 ? row 8251 : settings.bSortCellsTop // legacy support 8252 ? 0 8253 : header.length - 1; 8254 8255 return header[target][column].cell; 8256 }; 8257 8258 var __column_selector = function ( settings, selector, opts ) 8259 { 8260 var 8261 columns = settings.aoColumns, 8262 names = _pluck( columns, 'sName' ), 8263 titles = _pluck( columns, 'sTitle' ), 8264 cells = DataTable.util.get('[].[].cell')(settings.aoHeader), 8265 nodes = _unique( _flatten([], cells) ); 8266 8267 var run = function ( s ) { 8268 var selInt = _intVal( s ); 8269 8270 // Selector - all 8271 if ( s === '' ) { 8272 return _range( columns.length ); 8273 } 8274 8275 // Selector - index 8276 if ( selInt !== null ) { 8277 return [ selInt >= 0 ? 8278 selInt : // Count from left 8279 columns.length + selInt // Count from right (+ because its a negative value) 8280 ]; 8281 } 8282 8283 // Selector = function 8284 if ( typeof s === 'function' ) { 8285 var rows = _selector_row_indexes( settings, opts ); 8286 8287 return columns.map(function (col, idx) { 8288 return s( 8289 idx, 8290 __columnData( settings, idx, 0, 0, rows ), 8291 __column_header( settings, idx ) 8292 ) ? idx : null; 8293 }); 8294 } 8295 8296 // jQuery or string selector 8297 var match = typeof s === 'string' ? 8298 s.match( __re_column_selector ) : 8299 ''; 8300 8301 if ( match ) { 8302 switch( match[2] ) { 8303 case 'visIdx': 8304 case 'visible': 8305 var idx = parseInt( match[1], 10 ); 8306 // Visible index given, convert to column index 8307 if ( idx < 0 ) { 8308 // Counting from the right 8309 var visColumns = columns.map( function (col,i) { 8310 return col.bVisible ? i : null; 8311 } ); 8312 return [ visColumns[ visColumns.length + idx ] ]; 8313 } 8314 // Counting from the left 8315 return [ _fnVisibleToColumnIndex( settings, idx ) ]; 8316 8317 case 'name': 8318 // match by name. `names` is column index complete and in order 8319 return names.map( function (name, i) { 8320 return name === match[1] ? i : null; 8321 } ); 8322 8323 case 'title': 8324 // match by column title 8325 return titles.map( function (title, i) { 8326 return title === match[1] ? i : null; 8327 } ); 8328 8329 default: 8330 return []; 8331 } 8332 } 8333 8334 // Cell in the table body 8335 if ( s.nodeName && s._DT_CellIndex ) { 8336 return [ s._DT_CellIndex.column ]; 8337 } 8338 8339 // jQuery selector on the TH elements for the columns 8340 var jqResult = $( nodes ) 8341 .filter( s ) 8342 .map( function () { 8343 return _fnColumnsFromHeader( this ); // `nodes` is column index complete and in order 8344 } ) 8345 .toArray(); 8346 8347 if ( jqResult.length || ! s.nodeName ) { 8348 return jqResult; 8349 } 8350 8351 // Otherwise a node which might have a `dt-column` data attribute, or be 8352 // a child or such an element 8353 var host = $(s).closest('*[data-dt-column]'); 8354 return host.length ? 8355 [ host.data('dt-column') ] : 8356 []; 8357 }; 8358 8359 return _selector_run( 'column', selector, run, settings, opts ); 8360 }; 8361 8362 8363 var __setColumnVis = function ( settings, column, vis ) { 8364 var 8365 cols = settings.aoColumns, 8366 col = cols[ column ], 8367 data = settings.aoData, 8368 cells, i, ien, tr; 8369 8370 // Get 8371 if ( vis === undefined ) { 8372 return col.bVisible; 8373 } 8374 8375 // Set 8376 // No change 8377 if ( col.bVisible === vis ) { 8378 return false; 8379 } 8380 8381 if ( vis ) { 8382 // Insert column 8383 // Need to decide if we should use appendChild or insertBefore 8384 var insertBefore = _pluck(cols, 'bVisible').indexOf(true, column+1); 8385 8386 for ( i=0, ien=data.length ; i<ien ; i++ ) { 8387 if (data[i]) { 8388 tr = data[i].nTr; 8389 cells = data[i].anCells; 8390 8391 if ( tr ) { 8392 // insertBefore can act like appendChild if 2nd arg is null 8393 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null ); 8394 } 8395 } 8396 } 8397 } 8398 else {
8399 // Remove column 8400 $( _pluck( settings.aoData, 'anCells', column ) ).detach(); 8401 } 8402 8403 // Common actions 8404 col.bVisible = vis; 8405 8406 _colGroup(settings); 8407 8408 return true; 8409 }; 8410 8411 8412 _api_register( 'columns()', function ( selector, opts ) { 8413 // argument shifting 8414 if ( selector === undefined ) { 8415 selector = ''; 8416 } 8417 else if ( $.isPlainObject( selector ) ) { 8418 opts = selector; 8419 selector = ''; 8420 } 8421 8422 opts = _selector_opts( opts ); 8423 8424 var inst = this.iterator( 'table', function ( settings ) { 8425 return __column_selector( settings, selector, opts ); 8426 }, 1 ); 8427 8428 // Want argument shifting here and in _row_selector? 8429 inst.selector.cols = selector; 8430 inst.selector.opts = opts; 8431 8432 return inst; 8433 } ); 8434 8435 _api_registerPlural( 'columns().header()', 'column().header()', function ( row ) { 8436 return this.iterator( 'column', function (settings, column) { 8437 return __column_header(settings, column, row); 8438 }, 1 ); 8439 } ); 8440 8441 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( row ) { 8442 return this.iterator( 'column', function ( settings, column ) { 8443 var footer = settings.aoFooter; 8444 8445 if (! footer.length) { 8446 return null; 8447 } 8448 8449 return settings.aoFooter[row !== undefined ? row : 0][column].cell; 8450 }, 1 ); 8451 } ); 8452 8453 _api_registerPlural( 'columns().data()', 'column().data()', function () { 8454 return this.iterator( 'column-rows', __columnData, 1 ); 8455 } ); 8456 8457 _api_registerPlural( 'columns().render()', 'column().render()', function ( type ) { 8458 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8459 return __columnData( settings, column, i, j, rows, type ); 8460 }, 1 ); 8461 } ); 8462 8463 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () { 8464 return this.iterator( 'column', function ( settings, column ) { 8465 return settings.aoColumns[column].mData; 8466 }, 1 ); 8467 } ); 8468 8469 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) { 8470 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8471 return _pluck_order( settings.aoData, rows, 8472 type === 'search' ? '_aFilterData' : '_aSortData', column 8473 ); 8474 }, 1 ); 8475 } ); 8476 8477 _api_registerPlural( 'columns().init()', 'column().init()', function () { 8478 return this.iterator( 'column', function ( settings, column ) { 8479 return settings.aoColumns[column]; 8480 }, 1 ); 8481 } ); 8482 8483 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () { 8484 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8485 return _pluck_order( settings.aoData, rows, 'anCells', column ) ; 8486 }, 1 ); 8487 } ); 8488 8489 _api_registerPlural( 'columns().titles()', 'column().title()', function (title, row) { 8490 return this.iterator( 'column', function ( settings, column ) { 8491 // Argument shifting 8492 if (typeof title === 'number') { 8493 row = title; 8494 title = undefined; 8495 } 8496 8497 var span = $('span.dt-column-title', this.column(column).header(row)); 8498 8499 if (title !== undefined) { 8500 span.html(title); 8501 return this; 8502 } 8503 8504 return span.html(); 8505 }, 1 ); 8506 } ); 8507 8508 _api_registerPlural( 'columns().types()', 'column().type()', function () { 8509 return this.iterator( 'column', function ( settings, column ) { 8510 var type = settings.aoColumns[column].sType; 8511 8512 // If the type was invalidated, then resolve it. This actually does 8513 // all columns at the moment. Would only happen once if getting all 8514 // column's data types. 8515 if (! type) { 8516 _fnColumnTypes(settings); 8517 } 8518 8519 return type; 8520 }, 1 ); 8521 } ); 8522 8523 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) { 8524 var that = this; 8525 var changed = []; 8526 var ret = this.iterator( 'column', function ( settings, column ) { 8527 if ( vis === undefined ) { 8528 return settings.aoColumns[ column ].bVisible; 8529 } // else 8530 8531 if (__setColumnVis( settings, column, vis )) { 8532 changed.push(column); 8533 } 8534 } ); 8535 8536 // Group the column visibility changes 8537 if ( vis !== undefined ) { 8538 this.iterator( 'table', function ( settings ) { 8539 // Redraw the header after changes 8540 _fnDrawHead( settings, settings.aoHeader ); 8541 _fnDrawHead( settings, settings.aoFooter ); 8542 8543 // Update colspan for no records display. Child rows and extensions will use their own 8544 // listeners to do this - only need to update the empty table item here 8545 if ( ! settings.aiDisplay.length ) { 8546 $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings)); 8547 } 8548 8549 _fnSaveState( settings ); 8550 8551 // Second loop once the first is done for events 8552 that.iterator( 'column', function ( settings, column ) { 8553 if (changed.includes(column)) { 8554 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] ); 8555 } 8556 } ); 8557 8558 if ( changed.length && (calc === undefined || calc) ) { 8559 that.columns.adjust(); 8560 } 8561 }); 8562 } 8563 8564 return ret; 8565 } ); 8566 8567 _api_registerPlural( 'columns().widths()', 'column().width()', function () { 8568 // Injects a fake row into the table for just a moment so the widths can 8569 // be read, regardless of colspan in the header and rows being present in 8570 // the body 8571 var columns = this.columns(':visible').count(); 8572 var row = $('<tr>').html('<td>' + Array(columns).join('</td><td>') + '</td>'); 8573 8574 $(this.table().body()).append(row); 8575 8576 var widths = row.children().map(function () { 8577 return $(this).outerWidth(); 8578 }); 8579 8580 row.remove(); 8581 8582 return this.iterator( 'column', function ( settings, column ) { 8583 var visIdx = _fnColumnIndexToVisible( settings, column ); 8584 8585 return visIdx !== null ? widths[visIdx] : 0; 8586 }, 1); 8587 } ); 8588 8589 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) { 8590 return this.iterator( 'column', function ( settings, column ) { 8591 return type === 'visible' ? 8592 _fnColumnIndexToVisible( settings, column ) : 8593 column; 8594 }, 1 ); 8595 } ); 8596 8597 _api_register( 'columns.adjust()', function () { 8598 return this.iterator( 'table', function ( settings ) { 8599 _fnAdjustColumnSizing( settings ); 8600 }, 1 ); 8601 } ); 8602 8603 _api_register( 'column.index()', function ( type, idx ) { 8604 if ( this.context.length !== 0 ) { 8605 var ctx = this.context[0]; 8606 8607 if ( type === 'fromVisible' || type === 'toData' ) { 8608 return _fnVisibleToColumnIndex( ctx, idx ); 8609 } 8610 else if ( type === 'fromData' || type === 'toVisible' ) { 8611 return _fnColumnIndexToVisible( ctx, idx ); 8612 } 8613 } 8614 } ); 8615 8616 _api_register( 'column()', function ( selector, opts ) { 8617 return _selector_first( this.columns( selector, opts ) ); 8618 } ); 8619 8620 var __cell_selector = function ( settings, selector, opts ) 8621 { 8622 var data = settings.aoData; 8623 var rows = _selector_row_indexes( settings, opts ); 8624 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) ); 8625 var allCells = $(_flatten( [], cells )); 8626 var row; 8627 var columns = settings.aoColumns.length; 8628 var a, i, ien, j, o, host; 8629 8630 var run = function ( s ) { 8631 var fnSelector = typeof s === 'function'; 8632 8633 if ( s === null || s === undefined || fnSelector ) { 8634 // All cells and function selectors 8635 a = []; 8636 8637 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
vendor: 4,056 bytes, lines 8638-8796
8638 row = rows[i]; 8639 8640 for ( j=0 ; j<columns ; j++ ) { 8641 o = { 8642 row: row, 8643 column: j 8644 }; 8645 8646 if ( fnSelector ) { 8647 // Selector - function 8648 host = data[ row ]; 8649 8650 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) { 8651 a.push( o ); 8652 } 8653 } 8654 else { 8655 // Selector - all 8656 a.push( o ); 8657 } 8658 } 8659 } 8660 8661 return a; 8662 } 8663 8664 // Selector - index 8665 if ( $.isPlainObject( s ) ) { 8666 // Valid cell index and its in the array of selectable rows 8667 return s.column !== undefined && s.row !== undefined && rows.indexOf(s.row) !== -1 ? 8668 [s] : 8669 []; 8670 } 8671 8672 // Selector - jQuery filtered cells 8673 var jqResult = allCells 8674 .filter( s ) 8675 .map( function (i, el) { 8676 return { // use a new object, in case someone changes the values 8677 row: el._DT_CellIndex.row, 8678 column: el._DT_CellIndex.column 8679 }; 8680 } ) 8681 .toArray(); 8682 8683 if ( jqResult.length || ! s.nodeName ) { 8684 return jqResult; 8685 } 8686 8687 // Otherwise the selector is a node, and there is one last option - the 8688 // element might be a child of an element which has dt-row and dt-column 8689 // data attributes 8690 host = $(s).closest('*[data-dt-row]'); 8691 return host.length ? 8692 [ { 8693 row: host.data('dt-row'), 8694 column: host.data('dt-column') 8695 } ] : 8696 []; 8697 }; 8698 8699 return _selector_run( 'cell', selector, run, settings, opts ); 8700 }; 8701 8702 8703 8704 8705 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) { 8706 // Argument shifting 8707 if ( $.isPlainObject( rowSelector ) ) { 8708 // Indexes 8709 if ( rowSelector.row === undefined ) { 8710 // Selector options in first parameter 8711 opts = rowSelector; 8712 rowSelector = null; 8713 } 8714 else { 8715 // Cell index objects in first parameter 8716 opts = columnSelector; 8717 columnSelector = null; 8718 } 8719 } 8720 if ( $.isPlainObject( columnSelector ) ) { 8721 opts = columnSelector; 8722 columnSelector = null; 8723 } 8724 8725 // Cell selector 8726 if ( columnSelector === null || columnSelector === undefined ) { 8727 return this.iterator( 'table', function ( settings ) { 8728 return __cell_selector( settings, rowSelector, _selector_opts( opts ) ); 8729 } ); 8730 } 8731 8732 // The default built in options need to apply to row and columns 8733 var internalOpts = opts ? { 8734 page: opts.page, 8735 order: opts.order, 8736 search: opts.search 8737 } : {}; 8738 8739 // Row + column selector 8740 var columns = this.columns( columnSelector, internalOpts ); 8741 var rows = this.rows( rowSelector, internalOpts ); 8742 var i, ien, j, jen; 8743 8744 var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) { 8745 var a = []; 8746 8747 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) { 8748 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) { 8749 a.push( { 8750 row: rows[idx][i], 8751 column: columns[idx][j] 8752 } ); 8753 } 8754 } 8755 8756 return a; 8757 }, 1 ); 8758 8759 // There is currently only one extension which uses a cell selector extension 8760 // It is a _major_ performance drag to run this if it isn't needed, so this is 8761 // an extension specific check at the moment 8762 var cells = opts && opts.selected ? 8763 this.cells( cellsNoOpts, opts ) : 8764 cellsNoOpts; 8765 8766 $.extend( cells.selector, { 8767 cols: columnSelector, 8768 rows: rowSelector, 8769 opts: opts 8770 } ); 8771 8772 return cells; 8773 } ); 8774 8775 8776 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () { 8777 return this.iterator( 'cell', function ( settings, row, column ) { 8778 var data = settings.aoData[ row ]; 8779 8780 return data && data.anCells ? 8781 data.anCells[ column ] : 8782 undefined; 8783 }, 1 ); 8784 } ); 8785 8786 8787 _api_register( 'cells().data()', function () { 8788 return this.iterator( 'cell', function ( settings, row, column ) { 8789 return _fnGetCellData( settings, row, column ); 8790 }, 1 ); 8791 } ); 8792 8793 8794 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) { 8795 type = type === 'search' ? '_aFilterData' : '_aSortData'; 8796
8797 return this.iterator( 'cell', function ( settings, row, column ) { 8798 return settings.aoData[ row ][ type ][ column ]; 8799 }, 1 ); 8800 } ); 8801 8802 8803 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) { 8804 return this.iterator( 'cell', function ( settings, row, column ) { 8805 return _fnGetCellData( settings, row, column, type ); 8806 }, 1 ); 8807 } ); 8808 8809 8810 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () { 8811 return this.iterator( 'cell', function ( settings, row, column ) { 8812 return { 8813 row: row, 8814 column: column, 8815 columnVisible: _fnColumnIndexToVisible( settings, column ) 8816 }; 8817 }, 1 ); 8818 } ); 8819 8820 8821 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) { 8822 return this.iterator( 'cell', function ( settings, row, column ) { 8823 _fnInvalidate( settings, row, src, column ); 8824 } ); 8825 } ); 8826 8827 8828 8829 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) { 8830 return _selector_first( this.cells( rowSelector, columnSelector, opts ) ); 8831 } ); 8832 8833 8834 _api_register( 'cell().data()', function ( data ) { 8835 var ctx = this.context; 8836 var cell = this[0]; 8837 8838 if ( data === undefined ) { 8839 // Get 8840 return ctx.length && cell.length ? 8841 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) : 8842 undefined; 8843 } 8844 8845 // Set 8846 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data ); 8847 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column ); 8848 8849 return this; 8850 } ); 8851 8852 8853 8854 /** 8855 * Get current ordering (sorting) that has been applied to the table. 8856 * 8857 * @returns {array} 2D array containing the sorting information for the first 8858 * table in the current context. Each element in the parent array represents 8859 * a column being sorted upon (i.e. multi-sorting with two columns would have 8860 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is 8861 * the column index that the sorting condition applies to, the second is the 8862 * direction of the sort (`desc` or `asc`) and, optionally, the third is the 8863 * index of the sorting order from the `column.sorting` initialisation array. 8864 *//** 8865 * Set the ordering for the table. 8866 * 8867 * @param {integer} order Column index to sort upon. 8868 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`) 8869 * @returns {DataTables.Api} this 8870 *//** 8871 * Set the ordering for the table. 8872 * 8873 * @param {array} order 1D array of sorting information to be applied. 8874 * @param {array} [...] Optional additional sorting conditions 8875 * @returns {DataTables.Api} this 8876 *//** 8877 * Set the ordering for the table. 8878 * 8879 * @param {array} order 2D array of sorting information to be applied. 8880 * @returns {DataTables.Api} this 8881 */ 8882 _api_register( 'order()', function ( order, dir ) { 8883 var ctx = this.context; 8884 var args = Array.prototype.slice.call( arguments ); 8885 8886 if ( order === undefined ) { 8887 // get 8888 return ctx.length !== 0 ? 8889 ctx[0].aaSorting : 8890 undefined; 8891 } 8892 8893 // set 8894 if ( typeof order === 'number' ) { 8895 // Simple column / direction passed in 8896 order = [ [ order, dir ] ]; 8897 } 8898 else if ( args.length > 1 ) { 8899 // Arguments passed in (list of 1D arrays) 8900 order = args; 8901 } 8902 // otherwise a 2D array was passed in 8903 8904 return this.iterator( 'table', function ( settings ) { 8905 settings.aaSorting = Array.isArray(order) ? order.slice() : order; 8906 } ); 8907 } ); 8908 8909 8910 /** 8911 * Attach a sort listener to an element for a given column 8912 * 8913 * @param {node|jQuery|string} node Identifier for the element(s) to attach the 8914 * listener to. This can take the form of a single DOM node, a jQuery 8915 * collection of nodes or a jQuery selector which will identify the node(s). 8916 * @param {integer} column the column that a click on this node will sort on 8917 * @param {function} [callback] callback function when sort is run 8918 * @returns {DataTables.Api} this 8919 */ 8920 _api_register( 'order.listener()', function ( node, column, callback ) { 8921 return this.iterator( 'table', function ( settings ) { 8922 _fnSortAttachListener(settings, node, {}, column, callback); 8923 } ); 8924 } ); 8925 8926 8927 _api_register( 'order.fixed()', function ( set ) { 8928 if ( ! set ) { 8929 var ctx = this.context; 8930 var fixed = ctx.length ? 8931 ctx[0].aaSortingFixed : 8932 undefined; 8933 8934 return Array.isArray( fixed ) ? 8935 { pre: fixed } : 8936 fixed; 8937 } 8938 8939 return this.iterator( 'table', function ( settings ) { 8940 settings.aaSortingFixed = $.extend( true, {}, set ); 8941 } ); 8942 } ); 8943 8944 8945 // Order by the selected column(s) 8946 _api_register( [ 8947 'columns().order()', 8948 'column().order()' 8949 ], function ( dir ) { 8950 var that = this; 8951 8952 if ( ! dir ) { 8953 return this.iterator( 'column', function ( settings, idx ) { 8954 var sort = _fnSortFlatten( settings ); 8955 8956 for ( var i=0, ien=sort.length ; i<ien ; i++ ) { 8957 if ( sort[i].col === idx ) { 8958 return sort[i].dir; 8959 } 8960 } 8961 8962 return null; 8963 }, 1 ); 8964 } 8965 else { 8966 return this.iterator( 'table', function ( settings, i ) { 8967 settings.aaSorting = that[i].map( function (col) { 8968 return [ col, dir ]; 8969 } ); 8970 } ); 8971 } 8972 } ); 8973 8974 _api_registerPlural('columns().orderable()', 'column().orderable()', function ( directions ) { 8975 return this.iterator( 'column', function ( settings, idx ) { 8976 var col = settings.aoColumns[idx]; 8977 8978 return directions ? 8979 col.asSorting : 8980 col.bSortable; 8981 }, 1 ); 8982 } ); 8983 8984 8985 _api_register( 'processing()', function ( show ) { 8986 return this.iterator( 'table', function ( ctx ) { 8987 _fnProcessingDisplay( ctx, show ); 8988 } ); 8989 } ); 8990 8991 8992 _api_register( 'search()', function ( input, regex, smart, caseInsen ) { 8993 var ctx = this.context; 8994 8995 if ( input === undefined ) { 8996 // get 8997 return ctx.length !== 0 ? 8998 ctx[0].oPreviousSearch.search : 8999 undefined; 9000 } 9001 9002 // set 9003 return this.iterator( 'table', function ( settings ) { 9004 if ( ! settings.oFeatures.bFilter ) { 9005 return; 9006 } 9007 9008 if (typeof regex === 'object') { 9009 // New style options to pass to the search builder 9010 _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, regex, { 9011 search: input 9012 } ) ); 9013 } 9014 else { 9015 // Compat for the old options 9016 _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, { 9017 search: input, 9018 regex: regex === null ? false : regex, 9019 smart: smart === null ? true : smart, 9020 caseInsensitive: caseInsen === null ? true : caseInsen 9021 } ) ); 9022 } 9023 } ); 9024 } ); 9025 9026 _api_register( 'search.fixed()', function ( name, search ) { 9027 var ret = this.iterator( true, 'table', function ( settings ) { 9028 var fixed = settings.searchFixed; 9029 9030 if (! name) { 9031 return Object.keys(fixed) 9032 } 9033 else if (search === undefined) { 9034 return fixed[name]; 9035 } 9036 else if (search === null) { 9037 delete fixed[name]; 9038 } 9039 else { 9040 fixed[name] = search; 9041 } 9042 9043 return this; 9044 } ); 9045 9046 return name !== undefined && search === undefined 9047 ? ret[0] 9048 : ret; 9049 } ); 9050 9051 _api_registerPlural( 9052 'columns().search()', 9053 'column().search()', 9054 function ( input, regex, smart, caseInsen ) { 9055 return this.iterator( 'column', function ( settings, column ) { 9056 var preSearch = settings.aoPreSearchCols; 9057 9058 if ( input === undefined ) { 9059 // get 9060 return preSearch[ column ].search; 9061 } 9062 9063 // set 9064 if ( ! settings.oFeatures.bFilter ) { 9065 return; 9066 } 9067 9068 if (typeof regex === 'object') { 9069 // New style options to pass to the search builder 9070 $.extend( preSearch[ column ], regex, { 9071 search: input 9072 } ); 9073 } 9074 else { 9075 // Old style (with not all options available) 9076 $.extend( preSearch[ column ], { 9077 search: input, 9078 regex: regex === null ? false : regex, 9079 smart: smart === null ? true : smart, 9080 caseInsensitive: caseInsen === null ? true : caseInsen 9081 } ); 9082 } 9083 9084 _fnFilterComplete( settings, settings.oPreviousSearch ); 9085 } ); 9086 } 9087 ); 9088 9089 _api_register([ 9090 'columns().search.fixed()', 9091 'column().search.fixed()' 9092 ], 9093 function ( name, search ) { 9094 var ret = this.iterator( true, 'column', function ( settings, colIdx ) { 9095 var fixed = settings.aoColumns[colIdx].searchFixed; 9096 9097 if (! name) { 9098 return Object.keys(fixed) 9099 } 9100 else if (search === undefined) { 9101 return fixed[name]; 9102 } 9103 else if (search === null) { 9104 delete fixed[name]; 9105 } 9106 else { 9107 fixed[name] = search; 9108 } 9109 9110 return this; 9111 } ); 9112 9113 return name !== undefined && search === undefined 9114 ? ret[0] 9115 : ret; 9116 } 9117 ); 9118 /* 9119 * State API methods 9120 */ 9121 9122 _api_register( 'state()', function ( set, ignoreTime ) { 9123 // getter 9124 if ( ! set ) { 9125 return this.context.length ? 9126 this.context[0].oSavedState : 9127 null; 9128 } 9129 9130 var setMutate = $.extend( true, {}, set ); 9131 9132 // setter 9133 return this.iterator( 'table', function ( settings ) { 9134 if ( ignoreTime !== false ) { 9135 setMutate.time = +new Date() + 100; 9136 } 9137 9138 _fnImplementState( settings, setMutate, function(){} ); 9139 } ); 9140 } ); 9141 9142 9143 _api_register( 'state.clear()', function () { 9144 return this.iterator( 'table', function ( settings ) { 9145 // Save an empty object 9146 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} ); 9147 } ); 9148 } ); 9149 9150 9151 _api_register( 'state.loaded()', function () { 9152 return this.context.length ? 9153 this.context[0].oLoadedState : 9154 null; 9155 } ); 9156 9157 9158 _api_register( 'state.save()', function () { 9159 return this.iterator( 'table', function ( settings ) { 9160 _fnSaveState( settings ); 9161 } ); 9162 } ); 9163 9164 /** 9165 * Set the jQuery or window object to be used by DataTables 9166 * 9167 * @param {*} module Library / container object 9168 * @param {string} [type] Library or container type `lib`, `win` or `datetime`. 9169 * If not provided, automatic detection is attempted. 9170 */ 9171 DataTable.use = function (module, type) { 9172 if (type === 'lib' || module.fn) { 9173 $ = module; 9174 } 9175 else if (type == 'win' || module.document) { 9176 window = module; 9177 document = module.document; 9178 } 9179 else if (type === 'datetime' || module.type === 'DateTime') { 9180 DataTable.DateTime = module; 9181 } 9182 } 9183 9184 /** 9185 * CommonJS factory function pass through. This will check if the arguments 9186 * given are a window object or a jQuery object. If so they are set 9187 * accordingly. 9188 * @param {*} root Window 9189 * @param {*} jq jQUery 9190 * @returns {boolean} Indicator 9191 */ 9192 DataTable.factory = function (root, jq) { 9193 var is = false; 9194 9195 // Test if the first parameter is a window object 9196 if (root && root.document) { 9197 window = root; 9198 document = root.document; 9199 } 9200 9201 // Test if the second parameter is a jQuery object 9202 if (jq && jq.fn && jq.fn.jquery) { 9203 $ = jq; 9204 is = true; 9205 } 9206 9207 return is; 9208 } 9209 9210 /**
9211 * Provide a common method for plug-ins to check the version of DataTables being 9212 * used, in order to ensure compatibility. 9213 * 9214 * @param {string} version Version string to check for, in the format "X.Y.Z". 9215 * Note that the formats "X" and "X.Y" are also acceptable. 9216 * @param {string} [version2=current DataTables version] As above, but optional. 9217 * If not given the current DataTables version will be used. 9218 * @returns {boolean} true if this version of DataTables is greater or equal to 9219 * the required version, or false if this version of DataTales is not 9220 * suitable 9221 * @static 9222 * @dtopt API-Static 9223 * 9224 * @example 9225 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) ); 9226 */ 9227 DataTable.versionCheck = function( version, version2 ) 9228 { 9229 var aThis = version2 ? 9230 version2.split('.') : 9231 DataTable.version.split('.'); 9232 var aThat = version.split('.'); 9233 var iThis, iThat; 9234 9235 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) { 9236 iThis = parseInt( aThis[i], 10 ) || 0; 9237 iThat = parseInt( aThat[i], 10 ) || 0; 9238 9239 // Parts are the same, keep comparing 9240 if (iThis === iThat) { 9241 continue; 9242 } 9243 9244 // Parts are different, return immediately 9245 return iThis > iThat; 9246 } 9247 9248 return true; 9249 }; 9250 9251 9252 /** 9253 * Check if a `<table>` node is a DataTable table already or not. 9254 * 9255 * @param {node|jquery|string} table Table node, jQuery object or jQuery 9256 * selector for the table to test. Note that if more than more than one 9257 * table is passed on, only the first will be checked 9258 * @returns {boolean} true the table given is a DataTable, or false otherwise 9259 * @static 9260 * @dtopt API-Static 9261 * 9262 * @example 9263 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { 9264 * $('#example').dataTable(); 9265 * } 9266 */ 9267 DataTable.isDataTable = function ( table ) 9268 { 9269 var t = $(table).get(0); 9270 var is = false; 9271 9272 if ( table instanceof DataTable.Api ) { 9273 return true; 9274 } 9275 9276 $.each( DataTable.settings, function (i, o) { 9277 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null; 9278 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null; 9279 9280 if ( o.nTable === t || head === t || foot === t ) { 9281 is = true; 9282 } 9283 } ); 9284 9285 return is; 9286 }; 9287 9288 9289 /** 9290 * Get all DataTable tables that have been initialised - optionally you can 9291 * select to get only currently visible tables. 9292 * 9293 * @param {boolean} [visible=false] Flag to indicate if you want all (default) 9294 * or visible tables only. 9295 * @returns {array} Array of `table` nodes (not DataTable instances) which are 9296 * DataTables 9297 * @static 9298 * @dtopt API-Static 9299 * 9300 * @example 9301 * $.each( $.fn.dataTable.tables(true), function () { 9302 * $(table).DataTable().columns.adjust(); 9303 * } ); 9304 */ 9305 DataTable.tables = function ( visible ) 9306 { 9307 var api = false; 9308 9309 if ( $.isPlainObject( visible ) ) { 9310 api = visible.api; 9311 visible = visible.visible; 9312 } 9313 9314 var a = DataTable.settings 9315 .filter( function (o) { 9316 return !visible || (visible && $(o.nTable).is(':visible')) 9317 ? true 9318 : false; 9319 } ) 9320 .map( function (o) { 9321 return o.nTable; 9322 }); 9323 9324 return api ? 9325 new _Api( a ) : 9326 a; 9327 }; 9328 9329 9330 /** 9331 * Convert from camel case parameters to Hungarian notation. This is made public 9332 * for the extensions to provide the same ability as DataTables core to accept 9333 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase 9334 * parameters. 9335 * 9336 * @param {object} src The model object which holds all parameters that can be 9337 * mapped. 9338 * @param {object} user The object to convert from camel case to Hungarian. 9339 * @param {boolean} force When set to `true`, properties which already have a 9340 * Hungarian value in the `user` object will be overwritten. Otherwise they 9341 * won't be. 9342 */ 9343 DataTable.camelToHungarian = _fnCamelToHungarian; 9344 9345 9346 9347 /** 9348 * 9349 */ 9350 _api_register( '$()', function ( selector, opts ) { 9351 var 9352 rows = this.rows( opts ).nodes(), // Get all rows 9353 jqRows = $(rows); 9354 9355 return $( [].concat( 9356 jqRows.filter( selector ).toArray(), 9357 jqRows.find( selector ).toArray() 9358 ) ); 9359 } ); 9360 9361 9362 // jQuery functions to operate on the tables 9363 $.each( [ 'on', 'one', 'off' ], function (i, key) { 9364 _api_register( key+'()', function ( /* event, handler */ ) { 9365 var args = Array.prototype.slice.call(arguments); 9366 9367 // Add the `dt` namespace automatically if it isn't already present 9368 args[0] = args[0].split( /\s/ ).map( function ( e ) { 9369 return ! e.match(/\.dt\b/) ? 9370 e+'.dt' : 9371 e; 9372 } ).join( ' ' ); 9373 9374 var inst = $( this.tables().nodes() ); 9375 inst[key].apply( inst, args ); 9376 return this; 9377 } ); 9378 } ); 9379 9380 9381 _api_register( 'clear()', function () { 9382 return this.iterator( 'table', function ( settings ) { 9383 _fnClearTable( settings ); 9384 } ); 9385 } ); 9386 9387 9388 _api_register( 'error()', function (msg) { 9389 return this.iterator( 'table', function ( settings ) { 9390 _fnLog( settings, 0, msg ); 9391 } ); 9392 } ); 9393 9394 9395 _api_register( 'settings()', function () { 9396 return new _Api( this.context, this.context ); 9397 } ); 9398 9399 9400 _api_register( 'init()', function () { 9401 var ctx = this.context; 9402 return ctx.length ? ctx[0].oInit : null; 9403 } ); 9404 9405 9406 _api_register( 'data()', function () { 9407 return this.iterator( 'table', function ( settings ) { 9408 return _pluck( settings.aoData, '_aData' ); 9409 } ).flatten(); 9410 } ); 9411 9412 9413 _api_register( 'trigger()', function ( name, args, bubbles ) { 9414 return this.iterator( 'table', function ( settings ) { 9415 return _fnCallbackFire( settings, null, name, args, bubbles ); 9416 } ).flatten(); 9417 } ); 9418 9419 9420 _api_register( 'ready()', function ( fn ) { 9421 var ctx = this.context; 9422 9423 // Get status of first table 9424 if (! fn) { 9425 return ctx.length 9426 ? (ctx[0]._bInitComplete || false) 9427 : null; 9428 } 9429 9430 // Function to run either once the table becomes ready or 9431 // immediately if it is already ready. 9432 return this.tables().every(function () { 9433 if (this.context[0]._bInitComplete) { 9434 fn.call(this); 9435 } 9436 else { 9437 this.on('init', function () { 9438 fn.call(this); 9439 }); 9440 } 9441 } ); 9442 } ); 9443 9444 9445 _api_register( 'destroy()', function ( remove ) { 9446 remove = remove || false; 9447 9448 return this.iterator( 'table', function ( settings ) { 9449 var classes = settings.oClasses; 9450 var table = settings.nTable; 9451 var tbody = settings.nTBody; 9452 var thead = settings.nTHead; 9453 var tfoot = settings.nTFoot; 9454 var jqTable = $(table); 9455 var jqTbody = $(tbody); 9456 var jqWrapper = $(settings.nTableWrapper); 9457 var rows = settings.aoData.map( function (r) { return r ? r.nTr : null; } ); 9458 var orderClasses = classes.order; 9459 9460 // Flag to note that the table is currently being destroyed - no action 9461 // should be taken 9462 settings.bDestroying = true; 9463
9464 // Fire off the destroy callbacks for plug-ins etc 9465 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings], true ); 9466 9467 // If not being removed from the document, make all columns visible 9468 if ( ! remove ) { 9469 new _Api( settings ).columns().visible( true ); 9470 } 9471 9472 // Blitz all `DT` namespaced events (these are internal events, the 9473 // lowercase, `dt` events are user subscribed and they are responsible 9474 // for removing them 9475 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT'); 9476 $(window).off('.DT-'+settings.sInstance); 9477 9478 // When scrolling we had to break the table up - restore it 9479 if ( table != thead.parentNode ) { 9480 jqTable.children('thead').detach(); 9481 jqTable.append( thead ); 9482 } 9483 9484 if ( tfoot && table != tfoot.parentNode ) { 9485 jqTable.children('tfoot').detach(); 9486 jqTable.append( tfoot ); 9487 } 9488 9489 settings.colgroup.remove(); 9490 9491 settings.aaSorting = []; 9492 settings.aaSortingFixed = []; 9493 _fnSortingClasses( settings ); 9494 9495 $('th, td', thead) 9496 .removeClass( 9497 orderClasses.canAsc + ' ' + 9498 orderClasses.canDesc + ' ' + 9499 orderClasses.isAsc + ' ' + 9500 orderClasses.isDesc 9501 ) 9502 .css('width', ''); 9503 9504 // Add the TR elements back into the table in their original order 9505 jqTbody.children().detach(); 9506 jqTbody.append( rows ); 9507 9508 var orig = settings.nTableWrapper.parentNode; 9509 var insertBefore = settings.nTableWrapper.nextSibling; 9510 9511 // Remove the DataTables generated nodes, events and classes 9512 var removedMethod = remove ? 'remove' : 'detach'; 9513 jqTable[ removedMethod ](); 9514 jqWrapper[ removedMethod ](); 9515 9516 // If we need to reattach the table to the document 9517 if ( ! remove && orig ) { 9518 // insertBefore acts like appendChild if !arg[1] 9519 orig.insertBefore( table, insertBefore ); 9520 9521 // Restore the width of the original table - was read from the style property, 9522 // so we can restore directly to that 9523 jqTable 9524 .css( 'width', settings.sDestroyWidth ) 9525 .removeClass( classes.table ); 9526 } 9527 9528 /* Remove the settings object from the settings array */ 9529 var idx = DataTable.settings.indexOf(settings); 9530 if ( idx !== -1 ) { 9531 DataTable.settings.splice( idx, 1 ); 9532 } 9533 } ); 9534 } ); 9535 9536 9537 // Add the `every()` method for rows, columns and cells in a compact form 9538 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) { 9539 _api_register( type+'s().every()', function ( fn ) { 9540 var opts = this.selector.opts; 9541 var api = this; 9542 var inst; 9543 var counter = 0; 9544 9545 return this.iterator( 'every', function ( settings, selectedIdx, tableIdx ) { 9546 inst = api[ type ](selectedIdx, opts); 9547 9548 if (type === 'cell') { 9549 fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter); 9550 } 9551 else { 9552 fn.call(inst, selectedIdx, tableIdx, counter); 9553 } 9554 9555 counter++; 9556 } ); 9557 } ); 9558 } ); 9559 9560 9561 // i18n method for extensions to be able to use the language object from the 9562 // DataTable 9563 _api_register( 'i18n()', function ( token, def, plural ) { 9564 var ctx = this.context[0]; 9565 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage ); 9566 9567 if ( resolved === undefined ) { 9568 resolved = def; 9569 } 9570 9571 if ( $.isPlainObject( resolved ) ) { 9572 resolved = plural !== undefined && resolved[ plural ] !== undefined ? 9573 resolved[ plural ] : 9574 resolved._; 9575 } 9576 9577 return typeof resolved === 'string' 9578 ? resolved.replace( '%d', plural ) // nb: plural might be undefined, 9579 : resolved; 9580 } ); 9581 9582 /** 9583 * Version string for plug-ins to check compatibility. Allowed format is 9584 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used 9585 * only for non-release builds. See https://semver.org/ for more information. 9586 * @member 9587 * @type string 9588 * @default Version number 9589 */ 9590 DataTable.version = "2.0.5"; 9591 9592 /** 9593 * Private data store, containing all of the settings objects that are 9594 * created for the tables on a given page. 9595 * 9596 * Note that the `DataTable.settings` object is aliased to 9597 * `jQuery.fn.dataTableExt` through which it may be accessed and 9598 * manipulated, or `jQuery.fn.dataTable.settings`. 9599 * @member 9600 * @type array 9601 * @default [] 9602 * @private 9603 */ 9604 DataTable.settings = []; 9605 9606 /** 9607 * Object models container, for the various models that DataTables has 9608 * available to it. These models define the objects that are used to hold 9609 * the active state and configuration of the table. 9610 * @namespace 9611 */ 9612 DataTable.models = {}; 9613 9614 9615 9616 /** 9617 * Template object for the way in which DataTables holds information about 9618 * search information for the global filter and individual column filters. 9619 * @namespace 9620 */ 9621 DataTable.models.oSearch = { 9622 /** 9623 * Flag to indicate if the filtering should be case insensitive or not 9624 */ 9625 "caseInsensitive": true, 9626 9627 /** 9628 * Applied search term 9629 */ 9630 "search": "", 9631 9632 /** 9633 * Flag to indicate if the search term should be interpreted as a 9634 * regular expression (true) or not (false) and therefore and special 9635 * regex characters escaped. 9636 */ 9637 "regex": false, 9638 9639 /** 9640 * Flag to indicate if DataTables is to use its smart filtering or not. 9641 */ 9642 "smart": true, 9643 9644 /** 9645 * Flag to indicate if DataTables should only trigger a search when 9646 * the return key is pressed. 9647 */ 9648 "return": false 9649 }; 9650 9651 9652 9653 9654 /** 9655 * Template object for the way in which DataTables holds information about 9656 * each individual row. This is the object format used for the settings 9657 * aoData array. 9658 * @namespace 9659 */ 9660 DataTable.models.oRow = { 9661 /** 9662 * TR element for the row 9663 */ 9664 "nTr": null, 9665 9666 /** 9667 * Array of TD elements for each row. This is null until the row has been 9668 * created. 9669 */ 9670 "anCells": null, 9671 9672 /** 9673 * Data object from the original data source for the row. This is either 9674 * an array if using the traditional form of DataTables, or an object if 9675 * using mData options. The exact type will depend on the passed in 9676 * data from the data source, or will be an array if using DOM a data 9677 * source. 9678 */ 9679 "_aData": [], 9680 9681 /** 9682 * Sorting data cache - this array is ostensibly the same length as the 9683 * number of columns (although each index is generated only as it is 9684 * needed), and holds the data that is used for sorting each column in the 9685 * row. We do this cache generation at the start of the sort in order that 9686 * the formatting of the sort data need be done only once for each cell 9687 * per sort. This array should not be read from or written to by anything 9688 * other than the master sorting methods. 9689 */ 9690 "_aSortData": null, 9691 9692 /** 9693 * Per cell filtering data cache. As per the sort data cache, used to 9694 * increase the performance of the filtering in DataTables 9695 */ 9696 "_aFilterData": null, 9697 9698 /** 9699 * Filtering data cache. This is the same as the cell filtering cache, but 9700 * in this case a string rather than an array. This is easily computed with 9701 * a join on `_aFilterData`, but is provided as a cache so the join isn't 9702 * needed on every search (memory traded for performance) 9703 */ 9704 "_sFilterRow": null, 9705 9706 /** 9707 * Denote if the original data source was from the DOM, or the data source 9708 * object. This is used for invalidating data, so DataTables can 9709 * automatically read data from the original source, unless uninstructed 9710 * otherwise. 9711 */ 9712 "src": null, 9713 9714 /** 9715 * Index in the aoData array. This saves an indexOf lookup when we have the 9716 * object, but want to know the index 9717 */ 9718 "idx": -1, 9719 9720 /** 9721 * Cached display value 9722 */ 9723 displayData: null 9724 }; 9725 9726 9727 /** 9728 * Template object for the column information object in DataTables. This object 9729 * is held in the settings aoColumns array and contains all the information that 9730 * DataTables needs about each individual column. 9731 * 9732 * Note that this object is related to {@link DataTable.defaults.column} 9733 * but this one is the internal data store for DataTables's cache of columns. 9734 * It should NOT be manipulated outside of DataTables. Any configuration should 9735 * be done through the initialisation options. 9736 * @namespace 9737 */ 9738 DataTable.models.oColumn = { 9739 /** 9740 * Column index. 9741 */ 9742 "idx": null, 9743 9744 /** 9745 * A list of the columns that sorting should occur on when this column 9746 * is sorted. That this property is an array allows multi-column s
9746orting 9747 * to be defined for a column (for example first name / last name columns 9748 * would benefit from this). The values are integers pointing to the 9749 * columns to be sorted on (typically it will be a single integer pointing 9750 * at itself, but that doesn't need to be the case). 9751 */ 9752 "aDataSort": null, 9753 9754 /** 9755 * Define the sorting directions that are applied to the column, in sequence 9756 * as the column is repeatedly sorted upon - i.e. the first value is used 9757 * as the sorting direction when the column if first sorted (clicked on). 9758 * Sort it again (click again) and it will move on to the next index. 9759 * Repeat until loop. 9760 */ 9761 "asSorting": null, 9762 9763 /** 9764 * Flag to indicate if the column is searchable, and thus should be included 9765 * in the filtering or not. 9766 */ 9767 "bSearchable": null, 9768 9769 /** 9770 * Flag to indicate if the column is sortable or not. 9771 */ 9772 "bSortable": null, 9773 9774 /** 9775 * Flag to indicate if the column is currently visible in the table or not 9776 */ 9777 "bVisible": null, 9778 9779 /** 9780 * Store for manual type assignment using the `column.type` option. This 9781 * is held in store so we can manipulate the column's `sType` property. 9782 */ 9783 "_sManualType": null, 9784 9785 /** 9786 * Flag to indicate if HTML5 data attributes should be used as the data 9787 * source for filtering or sorting. True is either are. 9788 */ 9789 "_bAttrSrc": false, 9790 9791 /** 9792 * Developer definable function that is called whenever a cell is created (Ajax source, 9793 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 9794 * allowing you to modify the DOM element (add background colour for example) when the 9795 * element is available. 9796 */ 9797 "fnCreatedCell": null, 9798 9799 /** 9800 * Function to get data from a cell in a column. You should <b>never</b> 9801 * access data directly through _aData internally in DataTables - always use 9802 * the method attached to this property. It allows mData to function as 9803 * required. This function is automatically assigned by the column 9804 * initialisation method 9805 */ 9806 "fnGetData": null, 9807 9808 /** 9809 * Function to set data for a cell in the column. You should <b>never</b> 9810 * set the data directly to _aData internally in DataTables - always use 9811 * this method. It allows mData to function as required. This function 9812 * is automatically assigned by the column initialisation method 9813 */ 9814 "fnSetData": null, 9815 9816 /** 9817 * Property to read the value for the cells in the column from the data 9818 * source array / object. If null, then the default content is used, if a 9819 * function is given then the return from the function is used. 9820 */ 9821 "mData": null, 9822 9823 /** 9824 * Partner property to mData which is used (only when defined) to get 9825 * the data - i.e. it is basically the same as mData, but without the 9826 * 'set' option, and also the data fed to it is the result from mData. 9827 * This is the rendering method to match the data method of mData. 9828 */ 9829 "mRender": null, 9830 9831 /** 9832 * The class to apply to all TD elements in the table's TBODY for the column 9833 */ 9834 "sClass": null, 9835 9836 /** 9837 * When DataTables calculates the column widths to assign to each column, 9838 * it finds the longest string in each column and then constructs a 9839 * temporary table and reads the widths from that. The problem with this 9840 * is that "mmm" is much wider then "iiii", but the latter is a longer 9841 * string - thus the calculation can go wrong (doing it properly and putting 9842 * it into an DOM object and measuring that is horribly(!) slow). Thus as 9843 * a "work around" we provide this option. It will append its value to the 9844 * text that is found to be the longest string for the column - i.e. padding. 9845 */ 9846 "sContentPadding": null, 9847 9848 /** 9849 * Allows a default value to be given for a column's data, and will be used 9850 * whenever a null data source is encountered (this can be because mData 9851 * is set to null, or because the data source itself is null). 9852 */ 9853 "sDefaultContent": null, 9854 9855 /** 9856 * Name for the column, allowing reference to the column by name as well as 9857 * by index (needs a lookup to work by name). 9858 */ 9859 "sName": null, 9860 9861 /** 9862 * Custom sorting data type - defines which of the available plug-ins in 9863 * afnSortData the custom sorting will use - if any is defined. 9864 */ 9865 "sSortDataType": 'std', 9866 9867 /** 9868 * Class to be applied to the header element when sorting on this column 9869 */ 9870 "sSortingClass": null, 9871 9872 /** 9873 * Title of the column - what is seen in the TH element (nTh). 9874 */ 9875 "sTitle": null, 9876 9877 /** 9878 * Column sorting and filtering type 9879 */ 9880 "sType": null, 9881 9882 /** 9883 * Width of the column 9884 */ 9885 "sWidth": null, 9886 9887 /** 9888 * Width of the column when it was first "encountered" 9889 */ 9890 "sWidthOrig": null, 9891 9892 /** Cached string which is the longest in the column */ 9893 maxLenString: null, 9894 9895 /** 9896 * Store for named searches 9897 */ 9898 searchFixed: null 9899 }; 9900 9901 9902 /* 9903 * Developer note: The properties of the object below are given in Hungarian 9904 * notation, that was used as the interface for DataTables prior to v1.10, however 9905 * from v1.10 onwards the primary interface is camel case. In order to avoid 9906 * breaking backwards compatibility utterly with this change, the Hungarian 9907 * version is still, internally the primary interface, but is is not documented 9908 * - hence the @name tags in each doc comment. This allows a Javascript function 9909 * to create a map from Hungarian notation to camel case (going the other direction 9910 * would require each property to be listed, which would add around 3K to the size 9911 * of DataTables, while this method is about a 0.5K hit). 9912 * 9913 * Ultimately this does pave the way for Hungarian notation to be dropped 9914 * completely, but that is a massive amount of work and will break current 9915 * installs (therefore is on-hold until v2). 9916 */ 9917 9918 /** 9919 * Initialisation options that can be given to DataTables at initialisation 9920 * time. 9921 * @namespace 9922 */ 9923 DataTable.defaults = { 9924 /** 9925 * An array of data to use for the table, passed in at initialisation which 9926 * will be used in preference to any data which is already in the DOM. This is 9927 * particularly useful for constructing tables purely in Javascript, for 9928 * example with a custom Ajax call. 9929 */ 9930 "aaData": null, 9931 9932 9933 /** 9934 * If ordering is enabled, then DataTables will perform a first pass sort on 9935 * initialisation. You can define which column(s) the sort is performed 9936 * upon, and the sorting direction, with this variable. The `sorting` array 9937 * should contain an array for each column to be sorted initially containing 9938 * the column's index and a direction string ('asc' or 'desc'). 9939 */ 9940 "aaSorting": [[0,'asc']], 9941 9942 9943 /** 9944 * This parameter is basically identical to the `sorting` parameter, but 9945 * cannot be overridden by user interaction with the table. What this means 9946 * is that you could have a column (visible or hidden) which the sorting 9947 * will always be forced on first - any sorting after that (from the user) 9948 * will then be performed as required. This can be useful for grouping rows 9949 * together. 9950 */ 9951 "aaSortingFixed": [], 9952 9953 9954 /** 9955 * DataTables can be instructed to load data to display in the table from a 9956 * Ajax source. This option defines how that Ajax call is made and where to. 9957 * 9958 * The `ajax` property has three different modes of operation, depending on 9959 * how it is defined. These are: 9960 * 9961 * * `string` - Set the URL from where the data should be loaded from. 9962 * * `object` - Define properties for `jQuery.ajax`. 9963 * * `function` - Custom data get function 9964 * 9965 * `string` 9966 * -------- 9967 * 9968 * As a string, the `ajax` property simply defines the URL from which 9969 * DataTables will load data. 9970 * 9971 * `object` 9972 * -------- 9973 * 9974 * As an object, the parameters in the object are passed to 9975 * [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) allowing fine control
9976 * of the Ajax request. DataTables has a number of default parameters which 9977 * you can override using this option. Please refer to the jQuery 9978 * documentation for a full description of the options available, although 9979 * the following parameters provide additional options in DataTables or 9980 * require special consideration: 9981 * 9982 * * `data` - As with jQuery, `data` can be provided as an object, but it 9983 * can also be used as a function to manipulate the data DataTables sends 9984 * to the server. The function takes a single parameter, an object of 9985 * parameters with the values that DataTables has readied for sending. An 9986 * object may be returned which will be merged into the DataTables 9987 * defaults, or you can add the items to the object that was passed in and 9988 * not return anything from the function. This supersedes `fnServerParams` 9989 * from DataTables 1.9-. 9990 * 9991 * * `dataSrc` - By default DataTables will look for the property `data` (or 9992 * `aaData` for compatibility with DataTables 1.9-) when obtaining data 9993 * from an Ajax source or for server-side processing - this parameter 9994 * allows that property to be changed. You can use Javascript dotted 9995 * object notation to get a data source for multiple levels of nesting, or 9996 * it my be used as a function. As a function it takes a single parameter, 9997 * the JSON returned from the server, which can be manipulated as 9998 * required, with the returned value being that used by DataTables as the 9999 * data source for the table. 10000 * 10001 * * `success` - Should not be overridden it is used internally in 10002 * DataTables. To manipulate / transform the data returned by the server 10003 * use `ajax.dataSrc`, or use `ajax` as a function (see below). 10004 * 10005 * `function` 10006 * ---------- 10007 * 10008 * As a function, making the Ajax call is left up to yourself allowing 10009 * complete control of the Ajax request. Indeed, if desired, a method other 10010 * than Ajax could be used to obtain the required data, such as Web storage 10011 * or an AIR database. 10012 * 10013 * The function is given four parameters and no return is required. The 10014 * parameters are: 10015 * 10016 * 1. _object_ - Data to send to the server 10017 * 2. _function_ - Callback function that must be executed when the required 10018 * data has been obtained. That data should be passed into the callback 10019 * as the only parameter 10020 * 3. _object_ - DataTables settings object for the table 10021 */ 10022 "ajax": null, 10023 10024 10025 /** 10026 * This parameter allows you to readily specify the entries in the length drop 10027 * down menu that DataTables shows when pagination is enabled. It can be 10028 * either a 1D array of options which will be used for both the displayed 10029 * option and the value, or a 2D array which will use the array in the first 10030 * position as the value, and the array in the second position as the 10031 * displayed options (useful for language strings such as 'All'). 10032 * 10033 * Note that the `pageLength` property will be automatically set to the 10034 * first value given in this array, unless `pageLength` is also provided. 10035 */ 10036 "aLengthMenu": [ 10, 25, 50, 100 ], 10037 10038 10039 /** 10040 * The `columns` option in the initialisation parameter allows you to define 10041 * details about the way individual columns behave. For a full list of 10042 * column options that can be set, please see 10043 * {@link DataTable.defaults.column}. Note that if you use `columns` to 10044 * define your columns, you must have an entry in the array for every single 10045 * column that you have in your table (these can be null if you don't which 10046 * to specify any options). 10047 */ 10048 "aoColumns": null, 10049 10050 /** 10051 * Very similar to `columns`, `columnDefs` allows you to target a specific 10052 * column, multiple columns, or all columns, using the `targets` property of 10053 * each object in the array. This allows great flexibility when creating 10054 * tables, as the `columnDefs` arrays can be of any length, targeting the 10055 * columns you specifically want. `columnDefs` may use any of the column 10056 * options available: {@link DataTable.defaults.column}, but it _must_ 10057 * have `targets` defined in each object in the array. Values in the `targets` 10058 * array may be: 10059 * <ul> 10060 * <li>
10060a string - class name will be matched on the TH for the column</li> 10061 * <li>0 or a positive integer - column index counting from the left</li> 10062 * <li>a negative integer - column index counting from the right</li> 10063 * <li>the string "_all" - all columns (i.e. assign a default)</li> 10064 * </ul> 10065 */ 10066 "aoColumnDefs": null, 10067 10068 10069 /** 10070 * Basically the same as `search`, this parameter defines the individual column 10071 * filtering state at initialisation time. The array must be of the same size 10072 * as the number of columns, and each element be an object with the parameters 10073 * `search` and `escapeRegex` (the latter is optional). 'null' is also 10074 * accepted and the default will be used. 10075 */ 10076 "aoSearchCols": [], 10077 10078 10079 /** 10080 * Enable or disable automatic column width calculation. This can be disabled 10081 * as an optimisation (it takes some time to calculate the widths) if the 10082 * tables widths are passed in using `columns`. 10083 */ 10084 "bAutoWidth": true, 10085 10086 10087 /** 10088 * Deferred rendering can provide DataTables with a huge speed boost when you 10089 * are using an Ajax or JS data source for the table. This option, when set to 10090 * true, will cause DataTables to defer the creation of the table elements for 10091 * each row until they are needed for a draw - saving a significant amount of 10092 * time. 10093 */ 10094 "bDeferRender": true, 10095 10096 10097 /** 10098 * Replace a DataTable which matches the given selector and replace it with 10099 * one which has the properties of the new initialisation object passed. If no 10100 * table matches the selector, then the new DataTable will be constructed as 10101 * per normal. 10102 */ 10103 "bDestroy": false, 10104 10105 10106 /** 10107 * Enable or disable filtering of data. Filtering in DataTables is "smart" in 10108 * that it allows the end user to input multiple words (space separated) and 10109 * will match a row containing those words, even if not in the order that was 10110 * specified (this allow matching across multiple columns). Note that if you 10111 * wish to use filtering in DataTables this must remain 'true' - to remove the 10112 * default filtering input box and retain filtering abilities, please use 10113 * {@link DataTable.defaults.dom}. 10114 */ 10115 "bFilter": true, 10116 10117 /** 10118 * Used only for compatiblity with DT1 10119 * @deprecated 10120 */ 10121 "bInfo": true, 10122 10123 /** 10124 * Used only for compatiblity with DT1 10125 * @deprecated 10126 */ 10127 "bLengthChange": true, 10128 10129 /** 10130 * Enable or disable pagination. 10131 */ 10132 "bPaginate": true, 10133 10134 10135 /** 10136 * Enable or disable the display of a 'processing' indicator when the table is 10137 * being processed (e.g. a sort). This is particularly useful for tables with 10138 * large amounts of data where it can take a noticeable amount of time to sort 10139 * the entries. 10140 */ 10141 "bProcessing": false, 10142 10143 10144 /** 10145 * Retrieve the DataTables object for the given selector. Note that if the 10146 * table has already been initialised, this parameter will cause DataTables 10147 * to simply return the object that has already been set up - it will not take 10148 * account of any changes you might have made to the initialisation object 10149 * passed to DataTables (setting this parameter to true is an acknowledgement 10150 * that you understand this). `destroy` can be used to reinitialise a table if 10151 * you need. 10152 */ 10153 "bRetrieve": false, 10154 10155 10156 /** 10157 * When vertical (y) scrolling is enabled, DataTables will force the height of 10158 * the table's viewport to the given height at all times (useful for layout). 10159 * However, this can look odd when filtering data down to a small data set, 10160 * and the footer is left "floating" further down. This parameter (when 10161 * enabled) will cause DataTables to collapse the table's viewport down when 10162 * the result set will fit within the given Y height. 10163 */ 10164 "bScrollCollapse": false, 10165 10166 10167 /** 10168 * Configure DataTables to use server-side processing. Note that the 10169 * `ajax` parameter must also be given in order to give DataTables a 10170 * source to obtain the required data for each draw. 10171 */ 10172 "bServerSide": false, 10173 10174 10175 /** 10176 * Enable or disable sorting of columns. Sorting of individual columns can be 10177 * disabled by the `sortable` option for each column. 10178 */ 10179 "bSort": true, 10180 10181 10182 /** 10183 * Enable or display DataTables' ability to sort multiple columns at the 10184 * same time (activated by shift-click by the user). 10185 */ 10186 "bSortMulti": true, 10187 10188 10189 /** 10190 * Allows control over whether DataTables should use the top (true) unique 10191 * cell that is found for a single column, or the bottom (false - default). 10192 * This is useful when using complex headers. 10193 */ 10194 "bSortCellsTop": null, 10195 10196 10197 /** 10198 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and 10199 * `sorting\_3` to the columns which are currently being sorted on. This is 10200 * presented as a feature switch as it can increase processing time (while 10201 * classes are removed and added) so for large data sets you might want to 10202 * turn this off. 10203 */ 10204 "bSortClasses": true, 10205 10206 10207 /** 10208 * Enable or disable state saving. When enabled HTML5 `localStorage` will be 10209 * used to save table display information such as pagination information, 10210 * display length, filtering and sorting. As such when the end user reloads 10211 * the page the display display will match what thy had previously set up. 10212 */ 10213 "bStateSave": false, 10214 10215 10216 /** 10217 * This function is called when a TR element is created (and all TD child 10218 * elements have been inserted), or registered if using a DOM source, allowing 10219 * manipulation of the TR element (adding classes etc). 10220 */ 10221 "fnCreatedRow": null, 10222 10223 10224 /** 10225 * This function is called on every 'draw' event, and allows you to 10226 * dynamically modify any aspect you want about the created DOM. 10227 */ 10228 "fnDrawCallback": null, 10229 10230 10231 /** 10232 * Identical to fnHeaderCallback() but for the table footer this function 10233 * allows you to modify the table footer on every 'draw' event. 10234 */ 10235 "fnFooterCallback": null, 10236 10237 10238 /** 10239 * When rendering large numbers in the information element for the table 10240 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers 10241 * to have a comma separator for the 'thousands' units (e.g. 1 million is 10242 * rendered as "1,000,000") to help readability for the end user. This 10243 * function will override the default method DataTables uses. 10244 */ 10245 "fnFormatNumber": function ( toFormat ) { 10246 return toFormat.toString().replace( 10247 /\B(?=(\d{3})+(?!\d))/g, 10248 this.oLanguage.sThousands 10249 ); 10250 }, 10251 10252 10253 /** 10254 * This function is called on every 'draw' event, and allows you to 10255 * dynamically modify the header row. This can be used to calculate and 10256 * display useful information about the table. 10257 */ 10258 "fnHeaderCallback": null, 10259 10260 10261 /** 10262 * The information element can be used to convey information about the current 10263 * state of the table. Although the internationalisation options presented by 10264 * DataTables are quite capable of dealing with most customisations, there may 10265 * be times where you wish to customise the string further. This callback 10266 * allows you to do exactly that. 10267 */ 10268 "fnInfoCallback": null, 10269 10270 10271 /** 10272 * Called when the table has been initialised. Normally DataTables will 10273 * initialise sequentially and there will be no need for this function, 10274 * however, this does not hold true when using external language information 10275 * since that is obtained using an async XHR call. 10276 */ 10277 "fnInitComplete": null, 10278 10279 10280 /** 10281 * Called at the very start of each table draw and can be used to cancel the 10282 * draw by returning false, any other return (including undefined) results in 10283 * the full draw occurring). 10284 */ 10285 "fnPreDrawCallback": null, 10286 10287 10288 /** 10289 * This function allows you to 'post process' each row after it have been 10290 * generated for each table draw, but before it is rendered on screen. This 10291 * function might be used for setting the row class name etc. 10292 */ 10293 "fnRowCallback": null, 10294 10295 10296 /** 10297 * Load the table state. With this function you can define from where, and how, the 10298 * state of a table is loaded. By default DataTables will load from `localStorage` 10299 * but you might wish to use a server-side database or cookies. 10300 */ 10301 "fnStateLoadCallback": function ( settings ) { 10302 try { 10303 return JSON.parse(
vendor: 6,442 bytes, lines 10304-10499
10304 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem( 10305 'DataTables_'+settings.sInstance+'_'+location.pathname 10306 ) 10307 ); 10308 } catch (e) { 10309 return {}; 10310 } 10311 }, 10312 10313 10314 /** 10315 * Callback which allows modification of the saved state prior to loading that state. 10316 * This callback is called when the table is loading state from the stored data, but 10317 * prior to the settings object being modified by the saved state. Note that for 10318 * plug-in authors, you should use the `stateLoadParams` event to load parameters for 10319 * a plug-in. 10320 */ 10321 "fnStateLoadParams": null, 10322 10323 10324 /** 10325 * Callback that is called when the state has been loaded from the state saving method 10326 * and the DataTables settings object has been modified as a result of the loaded state. 10327 */ 10328 "fnStateLoaded": null, 10329 10330 10331 /** 10332 * Save the table state. This function allows you to define where and how the state 10333 * information for the table is stored By default DataTables will use `localStorage` 10334 * but you might wish to use a server-side database or cookies. 10335 */ 10336 "fnStateSaveCallback": function ( settings, data ) { 10337 try { 10338 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem( 10339 'DataTables_'+settings.sInstance+'_'+location.pathname, 10340 JSON.stringify( data ) 10341 ); 10342 } catch (e) { 10343 // noop 10344 } 10345 }, 10346 10347 10348 /** 10349 * Callback which allows modification of the state to be saved. Called when the table 10350 * has changed state a new state save is required. This method allows modification of 10351 * the state saving object prior to actually doing the save, including addition or 10352 * other state properties or modification. Note that for plug-in authors, you should 10353 * use the `stateSaveParams` event to save parameters for a plug-in. 10354 */ 10355 "fnStateSaveParams": null, 10356 10357 10358 /** 10359 * Duration for which the saved state information is considered valid. After this period 10360 * has elapsed the state will be returned to the default. 10361 * Value is given in seconds. 10362 */ 10363 "iStateDuration": 7200, 10364 10365 10366 /** 10367 * Number of rows to display on a single page when using pagination. If 10368 * feature enabled (`lengthChange`) then the end user will be able to override 10369 * this to a custom setting using a pop-up menu. 10370 */ 10371 "iDisplayLength": 10, 10372 10373 10374 /** 10375 * Define the starting point for data display when using DataTables with 10376 * pagination. Note that this parameter is the number of records, rather than 10377 * the page number, so if you have 10 records per page and want to start on 10378 * the third page, it should be "20". 10379 */ 10380 "iDisplayStart": 0, 10381 10382 10383 /** 10384 * By default DataTables allows keyboard navigation of the table (sorting, paging, 10385 * and filtering) by adding a `tabindex` attribute to the required elements. This 10386 * allows you to tab through the controls and press the enter key to activate them. 10387 * The tabindex is default 0, meaning that the tab follows the flow of the document. 10388 * You can overrule this using this parameter if you wish. Use a value of -1 to 10389 * disable built-in keyboard navigation. 10390 */ 10391 "iTabIndex": 0, 10392 10393 10394 /** 10395 * Classes that DataTables assigns to the various components and features 10396 * that it adds to the HTML table. This allows classes to be configured 10397 * during initialisation in addition to through the static 10398 * {@link DataTable.ext.oStdClasses} object). 10399 */ 10400 "oClasses": {}, 10401 10402 10403 /** 10404 * All strings that DataTables uses in the user interface that it creates 10405 * are defined in this object, allowing you to modified them individually or 10406 * completely replace them all as required. 10407 */ 10408 "oLanguage": { 10409 /** 10410 * Strings that are used for WAI-ARIA labels and controls only (these are not 10411 * actually visible on the page, but will be read by screenreaders, and thus 10412 * must be internationalised as well). 10413 */ 10414 "oAria": { 10415 /** 10416 * ARIA label that is added to the table headers when the column may be sorted 10417 */ 10418 "orderable": ": Activate to sort", 10419 10420 /** 10421 * ARIA label that is added to the table headers when the column is currently being sorted 10422 */ 10423 "orderableReverse": ": Activate to invert sorting", 10424 10425 /** 10426 * ARIA label that is added to the table headers when the column is currently being 10427 * sorted and next step is to remove sorting 10428 */ 10429 "orderableRemove": ": Activate to remove sorting", 10430 10431 paginate: { 10432 first: 'First', 10433 last: 'Last', 10434 next: 'Next', 10435 previous: 'Previous' 10436 } 10437 }, 10438 10439 /** 10440 * Pagination string used by DataTables for the built-in pagination 10441 * control types. 10442 */ 10443 "oPaginate": { 10444 /** 10445 * Label and character for first page button («) 10446 */ 10447 "sFirst": "\u00AB", 10448 10449 /** 10450 * Last page button (») 10451 */ 10452 "sLast": "\u00BB", 10453 10454 /** 10455 * Next page button (âº) 10456 */ 10457 "sNext": "\u203A", 10458 10459 /** 10460 * Previous page button (â¹) 10461 */ 10462 "sPrevious": "\u2039", 10463 }, 10464 10465 /** 10466 * Plural object for the data type the table is showing 10467 */ 10468 entries: { 10469 _: "entries", 10470 1: "entry" 10471 }, 10472 10473 /** 10474 * This string is shown in preference to `zeroRecords` when the table is 10475 * empty of data (regardless of filtering). Note that this is an optional 10476 * parameter - if it is not given, the value of `zeroRecords` will be used 10477 * instead (either the default or given value). 10478 */ 10479 "sEmptyTable": "No data available.", 10480 10481 10482 /** 10483 * This string gives information to the end user about the information 10484 * that is current on display on the page. The following tokens can be 10485 * used in the string and will be dynamically replaced as the table 10486 * display updates. This tokens can be placed anywhere in the string, or 10487 * removed as needed by the language requires: 10488 * 10489 * * `\_START\_` - Display index of the first record on the current page 10490 * * `\_END\_` - Display index of the last record on the current page 10491 * * `\_TOTAL\_` - Number of records in the table after filtering 10492 * * `\_MAX\_` - Number of records in the table without filtering 10493 * * `\_PAGE\_` - Current page number 10494 * * `\_PAGES\_` - Total number of pages of data in the table 10495 */ 10496 "sInfo": "Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_", 10497 10498 10499 /**
10500 * Display information string for when the table is empty. Typically the 10501 * format of this string should match `info`. 10502 */ 10503 "sInfoEmpty": "Showing 0 to 0 of 0 _ENTRIES-TOTAL_", 10504 10505 10506 /** 10507 * When a user filters the information in a table, this string is appended 10508 * to the information (`info`) to give an idea of how strong the filtering 10509 * is. The variable _MAX_ is dynamically updated. 10510 */ 10511 "sInfoFiltered": "(filtered from _MAX_ total _ENTRIES-MAX_)", 10512 10513 10514 /** 10515 * If can be useful to append extra information to the info string at times, 10516 * and this variable does exactly that. This information will be appended to 10517 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are 10518 * being used) at all times. 10519 */ 10520 "sInfoPostFix": "", 10521 10522 10523 /** 10524 * This decimal place operator is a little different from the other 10525 * language options since DataTables doesn't output floating point 10526 * numbers, so it won't ever use this for display of a number. Rather, 10527 * what this parameter does is modify the sort methods of the table so 10528 * that numbers which are in a format which has a character other than 10529 * a period (`.`) as a decimal place will be sorted numerically. 10530 * 10531 * Note that numbers with different decimal places cannot be shown in 10532 * the same table and still be sortable, the table must be consistent. 10533 * However, multiple different tables on the page can use different 10534 * decimal place characters. 10535 */ 10536 "sDecimal": "", 10537 10538 10539 /** 10540 * DataTables has a build in number formatter (`formatNumber`) which is 10541 * used to format large numbers that are used in the table information. 10542 * By default a comma is used, but this can be trivially changed to any 10543 * character you wish with this parameter. 10544 */ 10545 "sThousands": ",", 10546 10547 10548 /** 10549 * Detail the action that will be taken when the drop down menu for the 10550 * pagination length option is changed. The '_MENU_' variable is replaced 10551 * with a default select list of 10, 25, 50 and 100, and can be replaced 10552 * with a custom select box if required. 10553 */ 10554 "sLengthMenu": "_MENU_ _ENTRIES_ per page", 10555 10556 10557 /** 10558 * When using Ajax sourced data and during the first draw when DataTables is 10559 * gathering the data, this message is shown in an empty row in the table to 10560 * indicate to the end user the the data is being loaded. Note that this 10561 * parameter is not used when loading data by server-side processing, just 10562 * Ajax sourced data with client-side processing. 10563 */ 10564 "sLoadingRecords": "Loading...", 10565 10566 10567 /** 10568 * Text which is displayed when the table is processing a user action 10569 * (usually a sort command or similar). 10570 */ 10571 "sProcessing": "", 10572 10573 10574 /** 10575 * Details the actions that will be taken when the user types into the 10576 * filtering input text box. The variable "_INPUT_", if used in the string, 10577 * is replaced with the HTML text box for the filtering input allowing 10578 * control over where it appears in the string. If "_INPUT_" is not given 10579 * then the input box is appended to the string automatically. 10580 */ 10581 "sSearch": "Search:", 10582 10583 10584 /** 10585 * Assign a `placeholder` attribute to the search `input` element 10586 * @type string 10587 * @default 10588 * 10589 * @dtopt Language 10590 * @name DataTable.defaults.language.searchPlaceholder 10591 */ 10592 "sSearchPlaceholder": "", 10593 10594 10595 /** 10596 * All of the language information can be stored in a file on the 10597 * server-side, which DataTables will look up if this parameter is passed. 10598 * It must store the URL of the language file, which is in a JSON format, 10599 * and the object has the same properties as the oLanguage object in the 10600 * initialiser object (i.e. the above parameters). Please refer to one of 10601 * the example language files to see how this works in action. 10602 */ 10603 "sUrl": "", 10604 10605 10606 /** 10607 * Text shown inside the table records when the is no information to be 10608 * displayed after filtering. `emptyTable` is shown when there is simply no 10609 * information in the table at all (regardless of filtering). 10610 */
10611 "sZeroRecords": "No matching records found" 10612 }, 10613 10614 10615 /** 10616 * This parameter allows you to have define the global filtering state at 10617 * initialisation time. As an object the `search` parameter must be 10618 * defined, but all other parameters are optional. When `regex` is true, 10619 * the search string will be treated as a regular expression, when false 10620 * (default) it will be treated as a straight string. When `smart` 10621 * DataTables will use it's smart filtering methods (to word match at 10622 * any point in the data), when false this will not be done. 10623 */ 10624 "oSearch": $.extend( {}, DataTable.models.oSearch ), 10625 10626 10627 /** 10628 * Table and control layout. This replaces the legacy `dom` option. 10629 */ 10630 layout: { 10631 topStart: 'pageLength', 10632 topEnd: 'search', 10633 bottomStart: 'info', 10634 bottomEnd: 'paging' 10635 }, 10636 10637 10638 /** 10639 * Legacy DOM layout option 10640 */ 10641 "sDom": null, 10642 10643 10644 /** 10645 * Search delay option. This will throttle full table searches that use the 10646 * DataTables provided search input element (it does not effect calls to 10647 * `dt-api search()`, providing a delay before the search is made. 10648 */ 10649 "searchDelay": null, 10650 10651 10652 /** 10653 * DataTables features six different built-in options for the buttons to 10654 * display for pagination control: 10655 * 10656 * * `numbers` - Page number buttons only 10657 * * `simple` - 'Previous' and 'Next' buttons only 10658 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers 10659 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons 10660 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers 10661 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers 10662 */ 10663 "sPaginationType": "full_numbers", 10664 10665 10666 /** 10667 * Enable horizontal scrolling. When a table is too wide to fit into a 10668 * certain layout, or you have a large number of columns in the table, you 10669 * can enable x-scrolling to show the table in a viewport, which can be 10670 * scrolled. This property can be `true` which will allow the table to 10671 * scroll horizontally when needed, or any CSS unit, or a number (in which 10672 * case it will be treated as a pixel measurement). Setting as simply `true` 10673 * is recommended. 10674 */ 10675 "sScrollX": "", 10676 10677 10678 /** 10679 * This property can be used to force a DataTable to use more width than it 10680 * might otherwise do when x-scrolling is enabled. For example if you have a 10681 * table which requires to be well spaced, this parameter is useful for 10682 * "over-sizing" the table, and thus forcing scrolling. This property can by 10683 * any CSS unit, or a number (in which case it will be treated as a pixel 10684 * measurement). 10685 */ 10686 "sScrollXInner": "", 10687 10688 10689 /** 10690 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable 10691 * to the given height, and enable scrolling for any data which overflows the 10692 * current viewport. This can be used as an alternative to paging to display 10693 * a lot of data in a small area (although paging and scrolling can both be 10694 * enabled at the same time). This property can be any CSS unit, or a number 10695 * (in which case it will be treated as a pixel measurement). 10696 */ 10697 "sScrollY": "", 10698 10699 10700 /** 10701 * __Deprecated__ The functionality provided by this parameter has now been 10702 * superseded by that provided through `ajax`, which should be used instead. 10703 * 10704 * Set the HTTP method that is used to make the Ajax call for server-side 10705 * processing or Ajax sourced data. 10706 */ 10707 "sServerMethod": "GET", 10708 10709 10710 /** 10711 * DataTables makes use of renderers when displaying HTML elements for 10712 * a table. These renderers can be added or modified by plug-ins to 10713 * generate suitable mark-up for a site. For example the Bootstrap 10714 * integration plug-in for DataTables uses a paging button renderer to 10715 * display pagination buttons in the mark-up required by Bootstrap. 10716 * 10717 * For further information about the renderers available see 10718 * DataTable.ext.renderer 10719 */ 10720 "renderer": null, 10721 10722 10723 /** 10724 * Set the data property name that DataTables should use to get a row's id 10725 * to set as the `id` property in the node. 10726 */ 10727 "rowId": "DT_RowId", 10728 10729 10730 /** 10731 * Caption value 10732 */ 10733 "caption": null 10734 }; 10735 10736 _fnHungarianMap( DataTable.defaults ); 10737 10738 10739 10740 /* 10741 * Developer note - See note in model.defaults.js about the use of Hungarian 10742 * notation and camel case. 10743 */ 10744 10745 /** 10746 * Column options that can be given to DataTables at initialisation time. 10747 * @namespace 10748 */ 10749 DataTable.defaults.column = { 10750 /** 10751 * Define which column(s) an order will occur on for this column. This 10752 * allows a column's ordering to take multiple columns into account when
10753 * doing a sort or use the data from a different column. For example first 10754 * name / last name columns make sense to do a multi-column sort over the 10755 * two columns. 10756 */ 10757 "aDataSort": null, 10758 "iDataSort": -1, 10759 10760 ariaTitle: '', 10761 10762 10763 /** 10764 * You can control the default ordering direction, and even alter the 10765 * behaviour of the sort handler (i.e. only allow ascending ordering etc) 10766 * using this parameter. 10767 */ 10768 "asSorting": [ 'asc', 'desc', '' ], 10769 10770 10771 /** 10772 * Enable or disable filtering on the data in this column. 10773 */ 10774 "bSearchable": true, 10775 10776 10777 /** 10778 * Enable or disable ordering on this column. 10779 */ 10780 "bSortable": true, 10781 10782 10783 /** 10784 * Enable or disable the display of this column. 10785 */ 10786 "bVisible": true, 10787 10788 10789 /** 10790 * Developer definable function that is called whenever a cell is created (Ajax source, 10791 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 10792 * allowing you to modify the DOM element (add background colour for example) when the 10793 * element is available. 10794 */ 10795 "fnCreatedCell": null, 10796 10797 10798 /** 10799 * This property can be used to read data from any data source property, 10800 * including deeply nested objects / properties. `data` can be given in a 10801 * number of different ways which effect its behaviour: 10802 * 10803 * * `integer` - treated as an array index for the data source. This is the 10804 * default that DataTables uses (incrementally increased for each column). 10805 * * `string` - read an object property from the data source. There are 10806 * three 'special' options that can be used in the string to alter how 10807 * DataTables reads the data from the source object: 10808 * * `.` - Dotted Javascript notation. Just as you use a `.` in 10809 * Javascript to read from nested objects, so to can the options 10810 * specified in `data`. For example: `browser.version` or 10811 * `browser.name`. If your object parameter name contains a period, use 10812 * `\\` to escape it - i.e. `first\\.name`. 10813 * * `[]` - Array notation. DataTables can automatically combine data 10814 * from and array source, joining the data with the characters provided 10815 * between the two brackets. For example: `name[, ]` would provide a 10816 * comma-space separated list from the source array. If no characters 10817 * are provided between the brackets, the original array source is 10818 * returned. 10819 * * `()` - Function notation. Adding `()` to the end of a parameter will 10820 * execute a function of the name given. For example: `browser()` for a 10821 * simple function on the data source, `browser.version()` for a 10822 * function in a nested property or even `browser().version` to get an 10823 * object property if the function called returns an object. Note that 10824 * function notation is recommended for use in `render` rather than 10825 * `data` as it is much simpler to use as a renderer. 10826 * * `null` - use the original data source for the row rather than plucking 10827 * data directly from it. This action has effects on two other 10828 * initialisation options: 10829 * * `defaultContent` - When null is given as the `data` option and 10830 * `defaultContent` is specified for the column, the value defined by 10831 * `defaultContent` will be used for the cell. 10832 * * `render` - When null is used for the `data` option and the `render` 10833 * option is specified for the column, the whole data source for the 10834 * row is used for the renderer. 10835 * * `function` - the function given will be executed whenever DataTables 10836 * needs to set or get the data for a cell in the column. The function 10837 * takes three parameters: 10838 * * Parameters: 10839 * * `{array|object}` The data source for the row 10840 * * `{string}` The type call data requested - this will be 'set' when 10841 * setting data or 'filter', 'display', 'type', 'sort' or undefined 10842 * when gathering data. Note that when `undefined` is given for the 10843 * type DataTables expects to get the raw data for the object back< 10844 * * `{*}` Data to set when the second parameter is 'set'. 10845 * * Return: 10846 * * The return value from the function is not required when '
10846set' is 10847 * the type of call, but otherwise the return is what will be used 10848 * for the data requested. 10849 * 10850 * Note that `data` is a getter and setter option. If you just require 10851 * formatting of data for output, you will likely want to use `render` which 10852 * is simply a getter and thus simpler to use. 10853 * 10854 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The 10855 * name change reflects the flexibility of this property and is consistent 10856 * with the naming of mRender. If 'mDataProp' is given, then it will still 10857 * be used by DataTables, as it automatically maps the old name to the new 10858 * if required. 10859 */ 10860 "mData": null, 10861 10862 10863 /** 10864 * This property is the rendering partner to `data` and it is suggested that 10865 * when you want to manipulate data for display (including filtering, 10866 * sorting etc) without altering the underlying data for the table, use this 10867 * property. `render` can be considered to be the the read only companion to 10868 * `data` which is read / write (then as such more complex). Like `data` 10869 * this option can be given in a number of different ways to effect its 10870 * behaviour: 10871 * 10872 * * `integer` - treated as an array index for the data source. This is the 10873 * default that DataTables uses (incrementally increased for each column). 10874 * * `string` - read an object property from the data source. There are 10875 * three 'special' options that can be used in the string to alter how 10876 * DataTables reads the data from the source object: 10877 * * `.` - Dotted Javascript notation. Just as you use a `.` in 10878 * Javascript to read from nested objects, so to can the options 10879 * specified in `data`. For example: `browser.version` or 10880 * `browser.name`. If your object parameter name contains a period, use 10881 * `\\` to escape it - i.e. `first\\.name`. 10882 * * `[]` - Array notation. DataTables can automatically combine data 10883 * from and array source, joining the data with the characters provided 10884 * between the two brackets. For example: `name[, ]` would provide a 10885 * comma-space separated list from the source array. If no characters 10886 * are provided between the brackets, the original array source is 10887 * returned. 10888 * * `()` - Function notation. Adding `()` to the end of a parameter will 10889 * execute a function of the name given. For example: `browser()` for a 10890 * simple function on the data source, `browser.version()` for a 10891 * function in a nested property or even `browser().version` to get an 10892 * object property if the function called returns an object. 10893 * * `object` - use different data for the different data types requested by 10894 * DataTables ('filter', 'display', 'type' or 'sort'). The property names 10895 * of the object is the data type the property refers to and the value can 10896 * defined using an integer, string or function using the same rules as 10897 * `render` normally does. Note that an `_` option _must_ be specified. 10898 * This is the default value to use if you haven't specified a value for 10899 * the data type requested by DataTables. 10900 * * `function` - the function given will be executed whenever DataTables 10901 * needs to set or get the data for a cell in the column. The function 10902 * takes three parameters: 10903 * * Parameters: 10904 * * {array|object} The data source for the row (based on `data`) 10905 * * {string} The type call data requested - this will be 'filter', 10906 * 'display', 'type' or 'sort'. 10907 * * {array|object} The full data source for the row (not based on 10908 * `data`) 10909 * * Return: 10910 * * The return value from the function is what will be used for the 10911 * data requested. 10912 */ 10913 "mRender": null, 10914 10915 10916 /** 10917 * Change the cell type created for the column - either TD cells or TH cells. This 10918 * can be useful as TH cells have semantic meaning in the table body, allowing them 10919 * to act as a header for a row (you may wish to add scope='row' to the TH elements). 10920 */ 10921 "sCellType": "td", 10922 10923 10924 /** 10925 * Class to give to each cell in this column. 10926 */ 10927 "sClass": "", 10928 10929 /** 10930 * When DataTables calculates the column widths to assign to each column, 10931 * it finds the longest string in each column and then constructs a 10932 * temporary table and reads the widths from that. The problem with this 10933 * is that "mmm" is much wider then "iiii", but the latter is a longer 10934 * string - thus the calculation can go wrong (doing it properly and putting 10935 * it into an DOM object and measuring that is horribly(!) slow). Thus as 10936 * a "work around" we provide this option. It will append its value to the 10937 * text that is found to be the longest string for the column - i.e. padding. 10938 * Generally you shouldn't need this! 10939 */ 10940 "sContentPadding": "", 10941 10942 10943 /** 10944 * Allows a default value to be given for a column's data, and will be used 10945 * whenever a null data source is encountered (this can be because `data` 10946 * is set to null, or because the data source itself is null). 10947 */ 10948 "sDefaultContent": null, 10949 10950 10951 /** 10952 * This parameter is only used in DataTables' server-side processing. It can 10953 * be exceptionally useful to know what columns are being displayed on the 10954 * client side, and to map these to database fields. When defined, the names 10955 * also allow DataTables to reorder information from the server if it comes 10956 * back in an unexpected order (i.e. if you switch your columns around on the 10957 * client-side, your server-side code does not also need updating). 10958 */ 10959 "sName": "", 10960 10961 10962 /** 10963 * Defines a data source type for the ordering which can be used to read 10964 * real-time information from the table (updating the internally cached 10965 * version) prior to ordering. This allows ordering to occur on user 10966 * editable elements such as form inputs. 10967 */ 10968 "sSortDataType": "std", 10969 10970 10971 /** 10972 * The title of this column. 10973 */ 10974 "sTitle": null, 10975 10976 10977 /** 10978 * The type allows you to specify how the data for this column will be
10979 * ordered. Four types (string, numeric, date and html (which will strip 10980 * HTML tags before ordering)) are currently available. Note that only date 10981 * formats understood by Javascript's Date() object will be accepted as type 10982 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 10983 * 'numeric', 'date' or 'html' (by default). Further types can be adding 10984 * through plug-ins. 10985 */ 10986 "sType": null, 10987 10988 10989 /** 10990 * Defining the width of the column, this parameter may take any CSS value 10991 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not 10992 * been given a specific width through this interface ensuring that the table 10993 * remains readable. 10994 */ 10995 "sWidth": null 10996 }; 10997 10998 _fnHungarianMap( DataTable.defaults.column ); 10999 11000 11001 11002 /** 11003 * DataTables settings object - this holds all the information needed for a 11004 * given table, including configuration, data and current application of the 11005 * table options. DataTables does not have a single instance for each DataTable 11006 * with the settings attached to that instance, but rather instances of the 11007 * DataTable "class" are created on-the-fly as needed (typically by a 11008 * $().dataTable() call) and the settings object is then applied to that 11009 * instance. 11010 * 11011 * Note that this object is related to {@link DataTable.defaults} but this 11012 * one is the internal data store for DataTables's cache of columns. It should 11013 * NOT be manipulated outside of DataTables. Any configuration should be done 11014 * through the initialisation options. 11015 */ 11016 DataTable.models.oSettings = { 11017 /** 11018 * Primary features of DataTables and their enablement state. 11019 */ 11020 "oFeatures": { 11021 11022 /** 11023 * Flag to say if DataTables should automatically try to calculate the 11024 * optimum table and columns widths (true) or not (false). 11025 * Note that this parameter will be set by the initialisation routine. To 11026 * set a default use {@link DataTable.defaults}. 11027 */ 11028 "bAutoWidth": null, 11029 11030 /** 11031 * Delay the creation of TR and TD elements until they are actually 11032 * needed by a driven page draw. This can give a significant speed 11033 * increase for Ajax source and Javascript source data, but makes no 11034 * difference at all for DOM and server-side processing tables. 11035 * Note that this parameter will be set by the initialisation routine. To 11036 * set a default use {@link DataTable.defaults}. 11037 */ 11038 "bDeferRender": null, 11039 11040 /** 11041 * Enable filtering on the table or not. Note that if this is disabled 11042 * then there is no filtering at all on the table, including fnFilter. 11043 * To just remove the filtering input use sDom and remove the 'f' option. 11044 * Note that this parameter will be set by the initialisation routine. To 11045 * set a default use {@link DataTable.defaults}. 11046 */ 11047 "bFilter": null, 11048 11049 /** 11050 * Used only for compatiblity with DT1 11051 * @deprecated 11052 */ 11053 "bInfo": true, 11054 11055 /** 11056 * Used only for compatiblity with DT1 11057 * @deprecated 11058 */ 11059 "bLengthChange": true, 11060 11061 /** 11062 * Pagination enabled or not. Note that if this is disabled then length 11063 * changing must also be disabled. 11064 * Note that this parameter will be set by the initialisation routine. To 11065 * set a default use {@link DataTable.defaults}. 11066 */ 11067 "bPaginate": null, 11068 11069 /** 11070 * Processing indicator enable flag whenever DataTables is enacting a 11071 * user request - typically an Ajax request for server-side processing. 11072 * Note that this parameter will be set by the initialisation routine. To 11073 * set a default use {@link DataTable.defaults}. 11074 */ 11075 "bProcessing": null, 11076 11077 /** 11078 * Server-side processing enabled flag - when enabled DataTables will 11079 * get all data from the server for every draw - there is no filtering, 11080 * sorting or paging done on the client-side. 11081 * Note that this parameter will be set by the initialisation routine. To 11082 * set a default use {@link DataTable.defaults}. 11083 */ 11084 "bServerSide": null, 11085 11086 /** 11087 * Sorting enablement flag. 11088 * Note that this parameter will be set by the initialisation routine. To 11089 * set a default use {@link DataTable.defaults}. 11090 */ 11091 "bSort": null, 11092 11093 /**
11094 * Multi-column sorting 11095 * Note that this parameter will be set by the initialisation routine. To 11096 * set a default use {@link DataTable.defaults}. 11097 */ 11098 "bSortMulti": null, 11099 11100 /** 11101 * Apply a class to the columns which are being sorted to provide a 11102 * visual highlight or not. This can slow things down when enabled since 11103 * there is a lot of DOM interaction. 11104 * Note that this parameter will be set by the initialisation routine. To 11105 * set a default use {@link DataTable.defaults}. 11106 */ 11107 "bSortClasses": null, 11108 11109 /** 11110 * State saving enablement flag. 11111 * Note that this parameter will be set by the initialisation routine. To 11112 * set a default use {@link DataTable.defaults}. 11113 */ 11114 "bStateSave": null 11115 }, 11116 11117 11118 /** 11119 * Scrolling settings for a table. 11120 */ 11121 "oScroll": { 11122 /** 11123 * When the table is shorter in height than sScrollY, collapse the 11124 * table container down to the height of the table (when true). 11125 * Note that this parameter will be set by the initialisation routine. To 11126 * set a default use {@link DataTable.defaults}. 11127 */ 11128 "bCollapse": null, 11129 11130 /** 11131 * Width of the scrollbar for the web-browser's platform. Calculated 11132 * during table initialisation. 11133 */ 11134 "iBarWidth": 0, 11135 11136 /** 11137 * Viewport width for horizontal scrolling. Horizontal scrolling is 11138 * disabled if an empty string. 11139 * Note that this parameter will be set by the initialisation routine. To 11140 * set a default use {@link DataTable.defaults}. 11141 */ 11142 "sX": null, 11143 11144 /** 11145 * Width to expand the table to when using x-scrolling. Typically you 11146 * should not need to use this. 11147 * Note that this parameter will be set by the initialisation routine. To 11148 * set a default use {@link DataTable.defaults}. 11149 * @deprecated 11150 */ 11151 "sXInner": null, 11152 11153 /** 11154 * Viewport height for vertical scrolling. Vertical scrolling is disabled 11155 * if an empty string. 11156 * Note that this parameter will be set by the initialisation routine. To 11157 * set a default use {@link DataTable.defaults}. 11158 */ 11159 "sY": null 11160 }, 11161 11162 /** 11163 * Language information for the table. 11164 */ 11165 "oLanguage": { 11166 /** 11167 * Information callback function. See 11168 * {@link DataTable.defaults.fnInfoCallback} 11169 */ 11170 "fnInfoCallback": null 11171 }, 11172 11173 /** 11174 * Browser support parameters 11175 */ 11176 "oBrowser": { 11177 /** 11178 * Determine if the vertical scrollbar is on the right or left of the 11179 * scrolling container - needed for rtl language layout, although not 11180 * all browsers move the scrollbar (Safari). 11181 */ 11182 "bScrollbarLeft": false, 11183 11184 /** 11185 * Browser scrollbar width 11186 */ 11187 "barWidth": 0 11188 }, 11189 11190 11191 "ajax": null, 11192 11193 11194 /** 11195 * Array referencing the nodes which are used for the features. The 11196 * parameters of this object match what is allowed by sDom - i.e. 11197 * <ul> 11198 * <li>'l' - Length changing</li> 11199 * <li>'f' - Filtering input</li> 11200 * <li>'t' - The table!</li> 11201 * <li>'i' - Information</li> 11202 * <li>'p' - Pagination</li> 11203 * <li>'r' - pRocessing</li> 11204 * </ul> 11205 */ 11206 "aanFeatures": [], 11207 11208 /** 11209 * Store data information - see {@link DataTable.models.oRow} for detailed 11210 * information. 11211 */ 11212 "aoData": [], 11213 11214 /** 11215 * Array of indexes which are in the current display (after filtering etc) 11216 */ 11217 "aiDisplay": [], 11218 11219 /** 11220 * Array of indexes for display - no filtering 11221 */ 11222 "aiDisplayMaster": [], 11223 11224 /** 11225 * Map of row ids to data indexes 11226 */ 11227 "aIds": {}, 11228 11229 /** 11230 * Store information about each column that is in use 11231 */ 11232 "aoColumns": [], 11233 11234 /** 11235 * Store information about the table's header 11236 */ 11237 "aoHeader": [], 11238 11239 /** 11240 * Store information about the table's footer 11241 */ 11242 "aoFooter": [], 11243 11244 /** 11245 * Store the applied global search information in case we want to force a 11246 * research or compare the old search to a new one. 11247 * Note that this parameter will be set by the initialisation routine. To 11248 * set a default use {@link DataTable.defaults}. 11249 */ 11250 "oPreviousSearch": {}, 11251 11252 /** 11253 * Store for named searches 11254 */ 11255 searchFixed: {}, 11256 11257 /** 11258 * Store the applied search for each column - see 11259 * {@link DataTable.models.oSearch} for the format that is used for the 11260 * filtering information for each column. 11261 */ 11262 "aoPreSearchCols": [], 11263 11264 /** 11265 * Sorting that is applied to the table. Note that the inner arrays are 11266 * used in the following manner: 11267 * <ul> 11268 * <li>Index 0 - column number</li> 11269 * <li>Index 1 - current sorting direction</li> 11270 * </ul> 11271 * Note that this parameter will be set by the initialisation routine. To 11272 * set a default use {@link DataTable.defaults}. 11273 */ 11274 "aaSorting": null, 11275 11276 /** 11277 * Sorting that is always applied to the table (i.e. prefixed in front of 11278 * aaSorting). 11279 * Note that this parameter will be set by the initialisation routine. To 11280 * set a default use {@link DataTable.defaults}. 11281 */ 11282 "aaSortingFixed": [], 11283 11284 /** 11285 * If restoring a table - we should restore its width 11286 */ 11287 "sDestroyWidth": 0, 11288 11289 /** 11290 * Callback functions array for every time a row is inserted (i.e. on a draw). 11291 */ 11292 "aoRowCallback": [], 11293 11294 /** 11295 * Callback functions for the header on each draw. 11296 */ 11297 "aoHeaderCallback": [], 11298 11299 /** 11300 * Callback function for the footer on each draw. 11301 */ 11302 "aoFooterCallback": [], 11303 11304 /** 11305 * Array of callback functions for draw callback functions 11306 */ 11307 "aoDrawCallback": [], 11308 11309 /** 11310 * Array of callback functions for row created function 11311 */ 11312 "aoRowCreatedCallback": [], 11313 11314 /** 11315 * Callback functions for just before the table is redrawn. A return of 11316 * false will be used to cancel the draw. 11317 */ 11318 "aoPreDrawCallback": [], 11319 11320 /** 11321 * Callback functions for when the table has been initialised. 11322 */ 11323 "aoInitComplete": [], 11324 11325 11326 /** 11327 * Callbacks for modifying the settings to be stored for state saving, prior to 11328 * saving state. 11329 */ 11330 "aoStateSaveParams": [], 11331 11332 /** 11333 * Callbacks for modifying the settings that have been stored for state saving 11334 * prior to using the stored values to restore the state. 11335 */ 11336 "aoStateLoadParams": [], 11337 11338 /** 11339 * Callbacks for operating on the settings object once the saved state has been 11340 * loaded 11341 */ 11342 "aoStateLoaded": [], 11343 11344 /** 11345 * Cache the table ID for quick access 11346 */ 11347 "sTableId": "", 11348 11349 /** 11350 * The TABLE node for the main table 11351 */ 11352 "nTable": null, 11353 11354 /** 11355 * Permanent ref to the thead element 11356 */ 11357 "nTHead": null, 11358 11359 /** 11360 * Permanent ref to the tfoot element - if it exists 11361 */ 11362 "nTFoot": null, 11363 11364 /** 11365 * Permanent ref to the tbody element 11366 */ 11367 "nTBody": null, 11368 11369 /** 11370 * Cache the wrapper node (contains all DataTables controlled elements) 11371 */ 11372 "nTableWrapper": null, 11373 11374 /** 11375 * Indicate if all required information has been read in 11376 */ 11377 "bInitialised": false, 11378 11379 /** 11380 * Information about open rows. Each object in the array has the parameters 11381 * 'nTr' and 'nParent' 11382 */ 11383 "aoOpenRows": [], 11384 11385 /**
11386 * Dictate the positioning of DataTables' control elements - see 11387 * {@link DataTable.model.oInit.sDom}. 11388 * Note that this parameter will be set by the initialisation routine. To 11389 * set a default use {@link DataTable.defaults}. 11390 */ 11391 "sDom": null, 11392 11393 /** 11394 * Search delay (in mS) 11395 */ 11396 "searchDelay": null, 11397 11398 /** 11399 * Which type of pagination should be used. 11400 * Note that this parameter will be set by the initialisation routine. To 11401 * set a default use {@link DataTable.defaults}. 11402 */ 11403 "sPaginationType": "two_button", 11404 11405 /** 11406 * Number of paging controls on the page. Only used for backwards compatibility 11407 */ 11408 pagingControls: 0, 11409 11410 /** 11411 * The state duration (for `stateSave`) in seconds. 11412 * Note that this parameter will be set by the initialisation routine. To 11413 * set a default use {@link DataTable.defaults}. 11414 */ 11415 "iStateDuration": 0, 11416 11417 /** 11418 * Array of callback functions for state saving. Each array element is an 11419 * object with the following parameters: 11420 * <ul> 11421 * <li>function:fn - function to call. Takes two parameters, oSettings 11422 * and the JSON string to save that has been thus far created. Returns 11423 * a JSON string to be inserted into a json object 11424 * (i.e. '"param": [ 0, 1, 2]')</li> 11425 * <li>string:sName - name of callback</li> 11426 * </ul> 11427 */ 11428 "aoStateSave": [], 11429 11430 /** 11431 * Array of callback functions for state loading. Each array element is an 11432 * object with the following parameters: 11433 * <ul> 11434 * <li>function:fn - function to call. Takes two parameters, oSettings 11435 * and the object stored. May return false to cancel state loading</li> 11436 * <li>string:sName - name of callback</li> 11437 * </ul> 11438 */ 11439 "aoStateLoad": [], 11440 11441 /** 11442 * State that was saved. Useful for back reference 11443 */ 11444 "oSavedState": null, 11445 11446 /** 11447 * State that was loaded. Useful for back reference 11448 */ 11449 "oLoadedState": null, 11450 11451 /** 11452 * Note if draw should be blocked while getting data 11453 */ 11454 "bAjaxDataGet": true, 11455 11456 /** 11457 * The last jQuery XHR object that was used for server-side data gathering. 11458 * This can be used for working with the XHR information in one of the 11459 * callbacks 11460 */ 11461 "jqXHR": null, 11462 11463 /** 11464 * JSON returned from the server in the last Ajax request 11465 */ 11466 "json": undefined, 11467 11468 /** 11469 * Data submitted as part of the last Ajax request 11470 */ 11471 "oAjaxData": undefined, 11472 11473 /** 11474 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if 11475 * required). 11476 * Note that this parameter will be set by the initialisation routine. To 11477 * set a default use {@link DataTable.defaults}. 11478 */ 11479 "sServerMethod": null, 11480 11481 /** 11482 * Format numbers for display. 11483 * Note that this parameter will be set by the initialisation routine. To 11484 * set a default use {@link DataTable.defaults}. 11485 */ 11486 "fnFormatNumber": null, 11487 11488 /** 11489 * List of options that can be used for the user selectable length menu. 11490 * Note that this parameter will be set by the initialisation routine. To 11491 * set a default use {@link DataTable.defaults}. 11492 */ 11493 "aLengthMenu": null, 11494 11495 /** 11496 * Counter for the draws that the table does. Also used as a tracker for 11497 * server-side processing 11498 */ 11499 "iDraw": 0, 11500 11501 /** 11502 * Indicate if a redraw is being done - useful for Ajax 11503 */ 11504 "bDrawing": false, 11505 11506 /** 11507 * Draw index (iDraw) of the last error when parsing the returned data 11508 */ 11509 "iDrawError": -1, 11510 11511 /** 11512 * Paging display length 11513 */ 11514 "_iDisplayLength": 10, 11515 11516 /** 11517 * Paging start point - aiDisplay index 11518 */ 11519 "_iDisplayStart": 0, 11520 11521 /** 11522 * Server-side processing - number of records in the result set 11523 * (i.e. before filtering), Use fnRecordsTotal rather than 11524 * this property to get the value of the number of records, regardless of 11525 * the server-side processing setting. 11526 */ 11527 "_iRecordsTotal": 0, 11528 11529 /** 11530 * Server-side processing - number of records in the current display set 11531 * (i.e. after filtering). Use fnRecordsDisplay rather than 11532 * this property to get the value of the number of records, regardless of 11533 * the server-side processing setting. 11534 */ 11535 "_iRecordsDisplay": 0, 11536 11537 /** 11538 * The classes to use for the table 11539 */
11540 "oClasses": {}, 11541 11542 /** 11543 * Flag attached to the settings object so you can check in the draw 11544 * callback if filtering has been done in the draw. Deprecated in favour of 11545 * events. 11546 * @deprecated 11547 */ 11548 "bFiltered": false, 11549 11550 /** 11551 * Flag attached to the settings object so you can check in the draw 11552 * callback if sorting has been done in the draw. Deprecated in favour of 11553 * events. 11554 * @deprecated 11555 */ 11556 "bSorted": false, 11557 11558 /** 11559 * Indicate that if multiple rows are in the header and there is more than 11560 * one unique cell per column, if the top one (true) or bottom one (false) 11561 * should be used for sorting / title by DataTables. 11562 * Note that this parameter will be set by the initialisation routine. To 11563 * set a default use {@link DataTable.defaults}. 11564 */ 11565 "bSortCellsTop": null, 11566 11567 /** 11568 * Initialisation object that is used for the table 11569 */ 11570 "oInit": null, 11571 11572 /** 11573 * Destroy callback functions - for plug-ins to attach themselves to the 11574 * destroy so they can clean up markup and events. 11575 */ 11576 "aoDestroyCallback": [], 11577 11578 11579 /** 11580 * Get the number of records in the current record set, before filtering 11581 */ 11582 "fnRecordsTotal": function () 11583 { 11584 return _fnDataSource( this ) == 'ssp' ? 11585 this._iRecordsTotal * 1 : 11586 this.aiDisplayMaster.length; 11587 }, 11588 11589 /** 11590 * Get the number of records in the current record set, after filtering 11591 */ 11592 "fnRecordsDisplay": function () 11593 { 11594 return _fnDataSource( this ) == 'ssp' ? 11595 this._iRecordsDisplay * 1 : 11596 this.aiDisplay.length; 11597 }, 11598 11599 /** 11600 * Get the display end point - aiDisplay index 11601 */ 11602 "fnDisplayEnd": function () 11603 { 11604 var 11605 len = this._iDisplayLength, 11606 start = this._iDisplayStart, 11607 calc = start + len, 11608 records = this.aiDisplay.length, 11609 features = this.oFeatures, 11610 paginate = features.bPaginate; 11611 11612 if ( features.bServerSide ) { 11613 return paginate === false || len === -1 ? 11614 start + records : 11615 Math.min( start+len, this._iRecordsDisplay ); 11616 } 11617 else { 11618 return ! paginate || calc>records || len===-1 ? 11619 records : 11620 calc; 11621 } 11622 }, 11623 11624 /** 11625 * The DataTables object for this table 11626 */ 11627 "oInstance": null, 11628 11629 /** 11630 * Unique identifier for each instance of the DataTables object. If there 11631 * is an ID on the table node, then it takes that value, otherwise an 11632 * incrementing internal counter is used. 11633 */ 11634 "sInstance": null, 11635 11636 /** 11637 * tabindex attribute value that is added to DataTables control elements, allowing 11638 * keyboard navigation of the table and its controls. 11639 */ 11640 "iTabIndex": 0, 11641 11642 /** 11643 * DIV container for the footer scrolling table if scrolling 11644 */ 11645 "nScrollHead": null, 11646 11647 /** 11648 * DIV container for the footer scrolling table if scrolling 11649 */ 11650 "nScrollFoot": null, 11651 11652 /** 11653 * Last applied sort 11654 */ 11655 "aLastSort": [], 11656 11657 /** 11658 * Stored plug-in instances 11659 */ 11660 "oPlugins": {}, 11661 11662 /** 11663 * Function used to get a row's id from the row's data 11664 */ 11665 "rowIdFn": null, 11666 11667 /** 11668 * Data location where to store a row's id 11669 */ 11670 "rowId": null, 11671 11672 caption: '', 11673 11674 captionNode: null, 11675 11676 colgroup: null 11677 }; 11678 11679 /** 11680 * Extension object for DataTables that is used to provide all extension 11681 * options. 11682 * 11683 * Note that the `DataTable.ext` object is available through 11684 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is 11685 * also aliased to `jQuery.fn.dataTableExt` for historic reasons. 11686 * @namespace 11687 * @extends DataTable.models.ext 11688 */ 11689 11690 11691 var extPagination = DataTable.ext.pager; 11692 11693 // Paging buttons configuration 11694 $.extend( extPagination, { 11695 simple: function () { 11696 return [ 'previous', 'next' ]; 11697 }, 11698 11699 full: function () { 11700 return [ 'first', 'previous', 'next', 'last' ]; 11701 }, 11702 11703 numbers: function () { 11704 return [ 'numbers' ]; 11705 }, 11706 11707 simple_numbers: function () { 11708 return [ 'previous', 'numbers', 'next' ]; 11709 }, 11710 11711 full_numbers: function () { 11712 return [ 'first', 'previous', 'numbers', 'next', 'last' ]; 11713 }, 11714 11715 first_last: function () { 11716 return ['first', 'last']; 11717 }, 11718 11719 first_last_numbers: function () { 11720 return ['first', 'numbers', 'last']; 11721 }, 11722 11723 // For testing and plug-ins to use 11724 _numbers: _pagingNumbers, 11725 11726 // Number of number buttons - legacy, use `numbers` option for paging feature 11727 numbers_length: 7 11728 } ); 11729 11730 11731 $.extend( true, DataTable.ext.renderer, { 11732 pagingButton: { 11733 _: function (settings, buttonType, content, active, disabled) { 11734 var classes = settings.oClasses.paging; 11735 var btnClasses = [classes.button]; 11736 var btn; 11737 11738 if (active) { 11739 btnClasses.push(classes.active); 11740 } 11741 11742 if (disabled) { 11743 btnClasses.push(classes.disabled) 11744 } 11745 11746 if (buttonType === 'ellipsis') { 11747 btn = $('<span class="ellipsis"></span>').html(content)[0]; 11748 } 11749 else { 11750 btn = $('<button>', { 11751 class: btnClasses.join(' '), 11752 role: 'link', 11753 type: 'button' 11754 }).html(content); 11755 } 11756 11757 return { 11758 display: btn, 11759 clicker: btn 11760 } 11761 } 11762 }, 11763 11764 pagingContainer: { 11765 _: function (settings, buttons) { 11766 // No wrapping element - just append directly to the host 11767 return buttons; 11768 } 11769 } 11770 } ); 11771 11772 // Common function to remove new lines, strip HTML and diacritic control 11773 var _filterString = function (stripHtml, normalize) { 11774 return function (str) { 11775 if (_empty(str) || typeof str !== 'string') { 11776 return str; 11777 } 11778 11779 str = str.replace( _re_new_lines, " " ); 11780 11781 if (stripHtml) { 11782 str = _stripHtml(str); 11783 } 11784 11785 if (normalize) { 11786 str = _normalize(str, false); 11787 } 11788 11789 return str; 11790 }; 11791 } 11792 11793 /* 11794 * Public helper functions. These aren't used internally by DataTables, or 11795 * called by any of the options passed into DataTables, but they can be used 11796 * externally by developers working with DataTables. They are helper functions 11797 * to make working with DataTables a little bit easier. 11798 */ 11799 11800 function __mldFnName(name) { 11801 return name.replace(/[\W]/g, '_') 11802 } 11803 11804 // Common logic for moment, luxon or a date action 11805 function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) { 11806 if (window.moment) { 11807 return dt[momentFn]( arg1 ); 11808 } 11809 else if (window.luxon) { 11810 return dt[luxonFn]( arg1 ); 11811 } 11812 11813 return dateFn ? dt[dateFn]( arg1 ) : dt; 11814 } 11815 11816 11817 var __mlWarning = false; 11818 function __mldObj (d, format, locale) { 11819 var dt; 11820 11821 if (window.moment) { 11822 dt = window.moment.utc( d, format, locale, true ); 11823 11824 if (! dt.isValid()) { 11825 return null; 11826 } 11827 } 11828 else if (window.luxon) { 11829 dt = format && typeof d === 'string' 11830 ? window.luxon.DateTime.fromFormat( d, format ) 11831 : window.luxon.DateTime.fromISO( d ); 11832 11833 if (! dt.isValid) { 11834 return null; 11835 } 11836 11837 dt.setLocale(locale); 11838 } 11839 else if (! format) { 11840 // No format given, must be ISO 11841 dt = new Date(d); 11842 } 11843 else { 11844 if (! __mlWarning) { 11845 alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17'); 11846 } 11847 11848 __mlWarning = true; 11849 } 11850 11851 return dt; 11852 } 11853 11854 // Wrapper for date, datetime and time which all operate the same way with the exception of 11855 // the output string for auto locale support 11856 function __mlHelper (localeString) { 11857 return function ( from, to, locale, def ) { 11858 // Luxon and Moment support 11859 // Argument shifting 11860 if ( arguments.length === 0 ) { 11861 locale = 'en'; 11862 to = null;
vendor: 5,221 bytes, lines 11862-12039
11862 // means toLocaleString 11863 from = null; // means iso8601 11864 } 11865 else if ( arguments.length === 1 ) { 11866 locale = 'en'; 11867 to = from; 11868 from = null; 11869 } 11870 else if ( arguments.length === 2 ) { 11871 locale = to; 11872 to = from; 11873 from = null; 11874 } 11875 11876 var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : ''); 11877 11878 // Add type detection and sorting specific to this date format - we need to be able to identify 11879 // date type columns as such, rather than as numbers in extensions. Hence the need for this. 11880 if (! DataTable.ext.type.order[typeName]) { 11881 DataTable.type(typeName, { 11882 detect: function (d) { 11883 // The renderer will give the value to type detect as the type! 11884 return d === typeName ? typeName : false; 11885 }, 11886 order: { 11887 pre: function (d) { 11888 // The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a 11889 // `valueOf` which gives milliseconds epoch 11890 return d.valueOf(); 11891 } 11892 }, 11893 className: 'dt-right' 11894 }); 11895 } 11896 11897 return function ( d, type ) { 11898 // Allow for a default value 11899 if (d === null || d === undefined) { 11900 if (def === '--now') { 11901 // We treat everything as UTC further down, so no changes are 11902 // made, as such need to get the local date / time as if it were 11903 // UTC 11904 var local = new Date(); 11905 d = new Date( Date.UTC( 11906 local.getFullYear(), local.getMonth(), local.getDate(), 11907 local.getHours(), local.getMinutes(), local.getSeconds() 11908 ) ); 11909 } 11910 else { 11911 d = ''; 11912 } 11913 } 11914 11915 if (type === 'type') { 11916 // Typing uses the type name for fast matching 11917 return typeName; 11918 } 11919 11920 if (d === '') { 11921 return type !== 'sort' 11922 ? '' 11923 : __mldObj('0000-01-01 00:00:00', null, locale); 11924 } 11925 11926 // Shortcut. If `from` and `to` are the same, we are using the renderer to 11927 // format for ordering, not display - its already in the display format. 11928 if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) { 11929 return d; 11930 } 11931 11932 var dt = __mldObj(d, from, locale); 11933 11934 if (dt === null) { 11935 return d; 11936 } 11937 11938 if (type === 'sort') { 11939 return dt; 11940 } 11941 11942 var formatted = to === null 11943 ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]() 11944 : __mld(dt, 'format', 'toFormat', 'toISOString', to); 11945 11946 // XSS protection 11947 return type === 'display' ? 11948 _escapeHtml( formatted ) : 11949 formatted; 11950 }; 11951 } 11952 } 11953 11954 // Based on locale, determine standard number formatting 11955 // Fallback for legacy browsers is US English 11956 var __thousands = ','; 11957 var __decimal = '.'; 11958 11959 if (window.Intl !== undefined) { 11960 try { 11961 var num = new Intl.NumberFormat().formatToParts(100000.1); 11962 11963 for (var i=0 ; i<num.length ; i++) { 11964 if (num[i].type === 'group') { 11965 __thousands = num[i].value; 11966 } 11967 else if (num[i].type === 'decimal') { 11968 __decimal = num[i].value; 11969 } 11970 } 11971 } 11972 catch (e) { 11973 // noop 11974 } 11975 } 11976 11977 // Formatted date time detection - use by declaring the formats you are going to use 11978 DataTable.datetime = function ( format, locale ) { 11979 var typeName = 'datetime-detect-' + __mldFnName(format); 11980 11981 if (! locale) { 11982 locale = 'en'; 11983 } 11984 11985 if (! DataTable.ext.type.order[typeName]) { 11986 DataTable.type(typeName, { 11987 detect: function (d) { 11988 var dt = __mldObj(d, format, locale); 11989 return d === '' || dt ? typeName : false; 11990 }, 11991 order: { 11992 pre: function (d) { 11993 return __mldObj(d, format, locale) || 0; 11994 } 11995 }, 11996 className: 'dt-right' 11997 }); 11998 } 11999 } 12000 12001 /** 12002 * Helpers for `columns.render`. 12003 * 12004 * The options defined here can be used with the `columns.render` initialisation 12005 * option to provide a display renderer. The following functions are defined: 12006 * 12007 * * `moment` - Uses the MomentJS library to convert from a given format into another. 12008 * This renderer has three overloads: 12009 * * 1 parameter: 12010 * * `string` - Format to convert to (assumes input is ISO8601 and locale is `en`) 12011 * * 2 parameters: 12012 * * `string` - Format to convert from 12013 * * `string` - Format to convert to. Assumes `en` locale 12014 * * 3 parameters: 12015 * * `string` - Format to convert from 12016 * * `string` - Format to convert to 12017 * * `string` - Locale 12018 * * `number` - Will format numeric data (defined by `columns.data`) for 12019 * display, retaining the original unformatted data for sorting and filtering. 12020 * It takes 5 parameters: 12021 * * `string` - Thousands grouping separator 12022 * * `string` - Decimal point indicator 12023 * * `integer` - Number of decimal points to show 12024 * * `string` (optional) - Prefix. 12025 * * `string` (optional) - Postfix (/suffix). 12026 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional 12027 * parameters. 12028 * 12029 * @example 12030 * // Column definition using the number renderer 12031 * { 12032 * data: "salary", 12033 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' ) 12034 * } 12035 * 12036 * @namespace 12037 */ 12038 DataTable.render = { 12039 date: __mlHelper('toLocaleDateString'),
12040 datetime: __mlHelper('toLocaleString'), 12041 time: __mlHelper('toLocaleTimeString'), 12042 number: function ( thousands, decimal, precision, prefix, postfix ) { 12043 // Auto locale detection 12044 if (thousands === null || thousands === undefined) { 12045 thousands = __thousands; 12046 } 12047 12048 if (decimal === null || decimal === undefined) { 12049 decimal = __decimal; 12050 } 12051 12052 return { 12053 display: function ( d ) { 12054 if ( typeof d !== 'number' && typeof d !== 'string' ) { 12055 return d; 12056 } 12057 12058 if (d === '' || d === null) { 12059 return d; 12060 } 12061 12062 var negative = d < 0 ? '-' : ''; 12063 var flo = parseFloat( d ); 12064 var abs = Math.abs(flo); 12065 12066 // Scientific notation for large and small numbers 12067 if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0) ) { 12068 var exp = flo.toExponential(precision).split(/e\+?/); 12069 return exp[0] + ' x 10<sup>' + exp[1] + '</sup>'; 12070 } 12071 12072 // If NaN then there isn't much formatting that we can do - just 12073 // return immediately, escaping any HTML (this was supposed to 12074 // be a number after all) 12075 if ( isNaN( flo ) ) { 12076 return _escapeHtml( d ); 12077 } 12078 12079 flo = flo.toFixed( precision ); 12080 d = Math.abs( flo ); 12081 12082 var intPart = parseInt( d, 10 ); 12083 var floatPart = precision ? 12084 decimal+(d - intPart).toFixed( precision ).substring( 2 ): 12085 ''; 12086 12087 // If zero, then can't have a negative prefix 12088 if (intPart === 0 && parseFloat(floatPart) === 0) { 12089 negative = ''; 12090 } 12091 12092 return negative + (prefix||'') + 12093 intPart.toString().replace( 12094 /\B(?=(\d{3})+(?!\d))/g, thousands 12095 ) + 12096 floatPart + 12097 (postfix||''); 12098 } 12099 }; 12100 }, 12101 12102 text: function () { 12103 return { 12104 display: _escapeHtml, 12105 filter: _escapeHtml 12106 }; 12107 } 12108 }; 12109 12110 12111 var _extTypes = DataTable.ext.type; 12112 12113 // Get / set type 12114 DataTable.type = function (name, prop, val) { 12115 if (! prop) { 12116 return { 12117 className: _extTypes.className[name], 12118 detect: _extTypes.detect.find(function (fn) { 12119 return fn.name === name; 12120 }), 12121 order: { 12122 pre: _extTypes.order[name + '-pre'], 12123 asc: _extTypes.order[name + '-asc'], 12124 desc: _extTypes.order[name + '-desc'] 12125 }, 12126 render: _extTypes.render[name], 12127 search: _extTypes.search[name] 12128 }; 12129 } 12130 12131 var setProp = function(prop, propVal) { 12132 _extTypes[prop][name] = propVal; 12133 }; 12134 var setDetect = function (fn) { 12135 // Wrap to allow the function to return `true` rather than 12136 // specifying the type name. 12137 var cb = function (d, s) { 12138 var ret = fn(d, s); 12139 12140 return ret === true 12141 ? name 12142 : ret; 12143 }; 12144 Object.defineProperty(cb, "name", {value: name}); 12145 12146 var idx = _extTypes.detect.findIndex(function (fn) { 12147 return fn.name === name; 12148 }); 12149 12150 if (idx === -1) { 12151 _extTypes.detect.unshift(cb); 12152 } 12153 else { 12154 _extTypes.detect.splice(idx, 1, cb); 12155 } 12156 }; 12157 var setOrder = function (obj) { 12158 _extTypes.order[name + '-pre'] = obj.pre; // can be undefined 12159 _extTypes.order[name + '-asc'] = obj.asc; // can be undefined 12160 _extTypes.order[name + '-desc'] = obj.desc; // can be undefined 12161 }; 12162 12163 // prop is optional 12164 if (val === undefined) { 12165 val = prop; 12166 prop = null; 12167 } 12168 12169 if (prop === 'className') { 12170 setProp('className', val); 12171 } 12172 else if (prop === 'detect') { 12173 setDetect(val); 12174 } 12175 else if (prop === 'order') { 12176 setOrder(val); 12177 } 12178 else if (prop === 'render') { 12179 setProp('render', val); 12180 } 12181 else if (prop === 'search') { 12182 setProp('search', val); 12183 } 12184 else if (! prop) { 12185 if (val.className) { 12186 setProp('className', val.className); 12187 } 12188 12189 if (val.detect !== undefined) { 12190 setDetect(val.detect); 12191 } 12192 12193 if (val.order) { 12194 setOrder(val.order); 12195 } 12196 12197 if (val.render !== undefined) { 12198 setProp('render', val.render); 12199 } 12200 12201 if (val.search !== undefined) { 12202 setProp('search', val.search); 12203 } 12204 } 12205 } 12206 12207 // Get a list of types 12208 DataTable.types = function () { 12209 return _extTypes.detect.map(function (fn) { 12210 return fn.name; 12211 }); 12212 }; 12213 12214 // 12215 // Built in data types 12216 // 12217 12218 DataTable.type('string', { 12219 detect: function () { 12220 return 'string'; 12221 }, 12222 order: { 12223 pre: function ( a ) { 12224 // This is a little complex, but faster than always calling toString, 12225 // http://jsperf.com/tostring-v-check 12226 return _empty(a) ? 12227 '' : 12228 typeof a === 'string' ? 12229 a.toLowerCase() : 12230 ! a.toString ? 12231 '' : 12232 a.toString(); 12233 } 12234 }, 12235 search: _filterString(false, true) 12236 }); 12237 12238 12239 DataTable.type('html', { 12240 detect: function ( d ) { 12241 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? 12242 'html' : null; 12243 }, 12244 order: { 12245 pre: function ( a ) { 12246 return _empty(a) ? 12247 '' : 12248 a.replace ?
12249 _stripHtml(a).trim().toLowerCase() : 12250 a+''; 12251 } 12252 }, 12253 search: _filterString(true, true) 12254 }); 12255 12256 12257 DataTable.type('date', { 12258 className: 'dt-type-date', 12259 detect: function ( d ) 12260 { 12261 // V8 tries _very_ hard to make a string passed into `Date.parse()` 12262 // valid, so we need to use a regex to restrict date formats. Use a 12263 // plug-in for anything other than ISO8601 style strings 12264 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { 12265 return null; 12266 } 12267 var parsed = Date.parse(d); 12268 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; 12269 }, 12270 order: { 12271 pre: function ( d ) { 12272 var ts = Date.parse( d ); 12273 return isNaN(ts) ? -Infinity : ts; 12274 } 12275 } 12276 }); 12277 12278 12279 DataTable.type('html-num-fmt', { 12280 className: 'dt-type-numeric', 12281 detect: function ( d, settings ) 12282 { 12283 var decimal = settings.oLanguage.sDecimal; 12284 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt' : null; 12285 }, 12286 order: { 12287 pre: function ( d, s ) { 12288 var dp = s.oLanguage.sDecimal; 12289 return __numericReplace( d, dp, _re_html, _re_formatted_numeric ); 12290 } 12291 }, 12292 search: _filterString(true, true) 12293 }); 12294 12295 12296 DataTable.type('html-num', { 12297 className: 'dt-type-numeric', 12298 detect: function ( d, settings ) 12299 { 12300 var decimal = settings.oLanguage.sDecimal; 12301 return _htmlNumeric( d, decimal ) ? 'html-num' : null; 12302 }, 12303 order: { 12304 pre: function ( d, s ) { 12305 var dp = s.oLanguage.sDecimal; 12306 return __numericReplace( d, dp, _re_html ); 12307 } 12308 }, 12309 search: _filterString(true, true) 12310 }); 12311 12312 12313 DataTable.type('num-fmt', { 12314 className: 'dt-type-numeric', 12315 detect: function ( d, settings ) 12316 { 12317 var decimal = settings.oLanguage.sDecimal; 12318 return _isNumber( d, decimal, true ) ? 'num-fmt' : null; 12319 }, 12320 order: { 12321 pre: function ( d, s ) { 12322 var dp = s.oLanguage.sDecimal; 12323 return __numericReplace( d, dp, _re_formatted_numeric ); 12324 } 12325 } 12326 }); 12327 12328 12329 DataTable.type('num', { 12330 className: 'dt-type-numeric', 12331 detect: function ( d, settings ) 12332 { 12333 var decimal = settings.oLanguage.sDecimal; 12334 return _isNumber( d, decimal ) ? 'num' : null; 12335 }, 12336 order: { 12337 pre: function (d, s) { 12338 var dp = s.oLanguage.sDecimal; 12339 return __numericReplace( d, dp ); 12340 } 12341 } 12342 }); 12343 12344 12345 12346 12347 var __numericReplace = function ( d, decimalPlace, re1, re2 ) { 12348 if ( d !== 0 && (!d || d === '-') ) { 12349 return -Infinity; 12350 } 12351 12352 var type = typeof d; 12353 12354 if (type === 'number' || type === 'bigint') { 12355 return d; 12356 } 12357 12358 // If a decimal place other than `.` is used, it needs to be given to the 12359 // function so we can detect it and replace with a `.` which is the only 12360 // decimal place Javascript recognises - it is not locale aware. 12361 if ( decimalPlace ) { 12362 d = _numToDecimal( d, decimalPlace ); 12363 } 12364 12365 if ( d.replace ) { 12366 if ( re1 ) { 12367 d = d.replace( re1, '' ); 12368 } 12369 12370 if ( re2 ) { 12371 d = d.replace( re2, '' ); 12372 } 12373 } 12374 12375 return d * 1; 12376 }; 12377 12378 12379 $.extend( true, DataTable.ext.renderer, { 12380 footer: { 12381 _: function ( settings, cell, classes ) {
12382 cell.addClass(classes.tfoot.cell); 12383 } 12384 }, 12385 12386 header: { 12387 _: function ( settings, cell, classes ) { 12388 cell.addClass(classes.thead.cell); 12389 12390 if (! settings.oFeatures.bSort) { 12391 cell.addClass(classes.order.none); 12392 } 12393 12394 var legacyTop = settings.bSortCellsTop; 12395 var headerRows = cell.closest('thead').find('tr'); 12396 var rowIdx = cell.parent().index(); 12397 12398 // Conditions to not apply the ordering icons 12399 if ( 12400 // Cells and rows which have the attribute to disable the icons 12401 cell.attr('data-dt-order') === 'disable' || 12402 cell.parent().attr('data-dt-order') === 'disable' || 12403 12404 // Legacy support for `orderCellsTop`. If it is set, then cells 12405 // which are not in the top or bottom row of the header (depending 12406 // on the value) do not get the sorting classes applied to them 12407 (legacyTop === true && rowIdx !== 0) || 12408 (legacyTop === false && rowIdx !== headerRows.length - 1) 12409 ) { 12410 return; 12411 } 12412 12413 // No additional mark-up required 12414 // Attach a sort listener to update on sort - note that using the 12415 // `DT` namespace will allow the event to be removed automatically 12416 // on destroy, while the `dt` namespaced event is the one we are 12417 // listening for 12418 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting ) { 12419 if ( settings !== ctx ) { // need to check this this is the host 12420 return; // table, not a nested one 12421 } 12422 12423 var orderClasses = classes.order; 12424 var columns = ctx.api.columns( cell ); 12425 var col = settings.aoColumns[columns.flatten()[0]]; 12426 var orderable = columns.orderable().includes(true); 12427 var ariaType = ''; 12428 var indexes = columns.indexes(); 12429 var sortDirs = columns.orderable(true).flatten(); 12430 var orderedColumns = ',' + sorting.map( function (val) { 12431 return val.col; 12432 } ).join(',') + ','; 12433 12434 cell 12435 .removeClass( 12436 orderClasses.isAsc +' '+ 12437 orderClasses.isDesc 12438 ) 12439 .toggleClass( orderClasses.none, ! orderable ) 12440 .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') ) 12441 .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') ); 12442 12443 var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' ); 12444 12445 if ( sortIdx !== -1 ) { 12446 // Get the ordering direction for the columns under this cell 12447 // Note that it is possible for a cell to be asc and desc sorting 12448 // (column spanning cells) 12449 var orderDirs = columns.order(); 12450 12451 cell.addClass( 12452 orderDirs.includes('asc') ? orderClasses.isAsc : '' + 12453 orderDirs.includes('desc') ? orderClasses.isDesc : '' 12454 ); 12455 } 12456 12457 // The ARIA spec says that only one column should be marked with aria-sort 12458 if ( sortIdx === 0 ) { 12459 var firstSort = sorting[0]; 12460 var sortOrder = col.asSorting; 12461 12462 cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending'); 12463 12464 // Determine if the next click will remove sorting or change the sort 12465 ariaType = ! sortOrder[firstSort.index + 1] ? 'Remove' : 'Reverse'; 12466 } 12467 else { 12468 cell.removeAttr('aria-sort'); 12469 } 12470 12471 cell.attr('aria-label', orderable 12472 ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType) 12473 : col.ariaTitle 12474 ); 12475 12476 if (orderable) { 12477 cell.find('.dt-column-title').attr('role', 'button'); 12478 cell.attr('tabindex', 0) 12479 } 12480 } ); 12481 } 12482 }, 12483 12484 layout: { 12485 _: function ( settings, container, items ) { 12486 var row = $('<div/>') 12487 .addClass('dt-layout-row') 12488 .appendTo( container ); 12489 12490 $.each( items, function (key, val) { 12491 var klass = ! val.table ? 12492 'dt-'+key+' ' : 12493 ''; 12494 12495 if (val.table) { 12496 row.addClass('dt-layout-table'); 12497 } 12498 12499 $('<div/>') 12500 .attr({ 12501 id: val.id || null, 12502 "class": 'dt-layout-cell '+klass+(val.className || '') 12503 }) 12504 .append( val.contents ) 12505 .appendTo( row ); 12506 } ); 12507 } 12508 } 12509 } ); 12510 12511 12512 DataTable.feature = {}; 12513 12514 // Third parameter is internal only! 12515 DataTable.feature.register = function ( name, cb, legacy ) { 12516 DataTable.ext.features[ name ] = cb; 12517 12518 if (legacy) { 12519 _ext.feature.push({ 12520 cFeature: legacy, 12521 fnInit: cb 12522 }); 12523 } 12524 }; 12525 12526 DataTable.feature.register( 'info', function ( settings, opts ) { 12527 // For compatibility with the legacy `info` top level option 12528 if (! settings.oFeatures.bInfo) { 12529 return null; 12530 } 12531 12532 var 12533 lang = settings.oLanguage, 12534 tid = settings.sTableId, 12535 n = $('<div/>', { 12536 'class': settings.oClasses.info.container, 12537 } ); 12538 12539 opts = $.extend({ 12540 callback: lang.fnInfoCallback, 12541 empty: lang.sInfoEmpty, 12542 postfix: lang.sInfoPostFix, 12543 search: lang.sInfoFiltered, 12544 text: lang.sInfo, 12545 }, opts); 12546 12547 12548 // Update display on each draw 12549 settings.aoDrawCallback.push(function (s) { 12550 _fnUpdateInfo(s, opts, n); 12551 }); 12552 12553 // For the first info display in the table, we add a callback and aria information. 12554 if (! settings._infoEl) { 12555 n.attr({ 12556 'aria-live': 'polite', 12557 id: tid+'_info', 12558 role: 'status' 12559 }); 12560 12561 // Table is described by our info div 12562 $(settings.nTable).attr( 'aria-describedby', tid+'_info' ); 12563 12564 settings._infoEl = n; 12565 } 12566 12567 return n; 12568 }, 'i' ); 12569 12570 /** 12571 * Update the information elements in the display 12572 * @param {object} settings dataTables settings object 12573 * @memberof DataTable#oApi 12574 */ 12575 function _fnUpdateInfo ( settings, opts, node ) 12576 { 12577 var 12578 start = settings._iDisplayStart+1, 12579 end = settings.fnDisplayEnd(), 12580 max = settings.fnRecordsTotal(), 12581 total = settings.fnRecordsDisplay(), 12582 out = total 12583 ? opts.text 12584 : opts.empty; 12585 12586 if ( total !== max ) { 12587 // Record set after filtering 12588 out += ' ' + opts.search; 12589 } 12590 12591 // Convert the macros 12592 out += opts.postfix; 12593 out = _fnMacros( settings, out ); 12594 12595 if ( opts.callback ) { 12596 out = opts.callback.call( settings.oInstance, 12597 settings, start, end, max, total, out 12598 ); 12599 } 12600 12601 node.html( out ); 12602 12603 _fnCallbackFire(settings, null, 'info', [settings, node[0], out]); 12604 } 12605 12606 var __searchCounter = 0; 12607 12608 // opts 12609 // - text 12610 // - placeholder 12611 DataTable.feature.register( 'search', function ( settings, opts ) { 12612 // Don't show the input if filtering isn't available on the table 12613 if (! settings.oFeatures.bFilter) { 12614 return null; 12615 } 12616 12617 var classes = settings.oClasses.search; 12618 var tableId = settings.sTableId; 12619 var language = settings.oLanguage; 12620 var previousSearch = settings.oPreviousSearch; 12621 var input = '<input type="search" class="'+classes.input+'"/>'; 12622 12623 opts = $.extend({ 12624 placeholder: language.sSearchPlaceholder, 12625 text: language.sSearch 12626 }, opts); 12627
12628 // The _INPUT_ is optional - is appended if not present 12629 if (opts.text.indexOf('_INPUT_') === -1) { 12630 opts.text += '_INPUT_'; 12631 } 12632 12633 opts.text = _fnMacros(settings, opts.text); 12634 12635 // We can put the <input> outside of the label if it is at the start or end 12636 // which helps improve accessability (not all screen readers like implicit 12637 // for elements). 12638 var end = opts.text.match(/_INPUT_$/); 12639 var start = opts.text.match(/^_INPUT_/); 12640 var removed = opts.text.replace(/_INPUT_/, ''); 12641 var str = '<label>' + opts.text + '</label>'; 12642 12643 if (start) { 12644 str = '_INPUT_<label>' + removed + '</label>'; 12645 } 12646 else if (end) { 12647 str = '<label>' + removed + '</label>_INPUT_'; 12648 } 12649 12650 var filter = $('<div>') 12651 .addClass(classes.container) 12652 .append(str.replace(/_INPUT_/, input)); 12653 12654 // add for and id to label and input 12655 filter.find('label').attr('for', 'dt-search-' + __searchCounter); 12656 filter.find('input').attr('id', 'dt-search-' + __searchCounter); 12657 __searchCounter++; 12658 12659 var searchFn = function(event) { 12660 var val = this.value; 12661 12662 if(previousSearch.return && event.key !== "Enter") { 12663 return; 12664 } 12665 12666 /* Now do the filter */ 12667 if ( val != previousSearch.search ) { 12668 previousSearch.search = val; 12669 12670 _fnFilterComplete( settings, previousSearch ); 12671 12672 // Need to redraw, without resorting 12673 settings._iDisplayStart = 0; 12674 _fnDraw( settings ); 12675 } 12676 }; 12677 12678 var searchDelay = settings.searchDelay !== null ? 12679 settings.searchDelay : 12680 0; 12681 12682 var jqFilter = $('input', filter) 12683 .val( previousSearch.search ) 12684 .attr( 'placeholder', opts.placeholder ) 12685 .on( 12686 'keyup.DT search.DT input.DT paste.DT cut.DT', 12687 searchDelay ? 12688 DataTable.util.debounce( searchFn, searchDelay ) : 12689 searchFn 12690 ) 12691 .on( 'mouseup.DT', function(e) { 12692 // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking 12693 // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn` 12694 // checks the value to see if it has changed. In other browsers it won't have. 12695 setTimeout( function () { 12696 searchFn.call(jqFilter[0], e); 12697 }, 10); 12698 } ) 12699 .on( 'keypress.DT', function(e) { 12700 /* Prevent form submission */ 12701 if ( e.keyCode == 13 ) { 12702 return false; 12703 } 12704 } ) 12705 .attr('aria-controls', tableId); 12706 12707 // Update the input elements whenever the table is filtered 12708 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) { 12709 if ( settings === s && jqFilter[0] !== document.activeElement ) { 12710 jqFilter.val( typeof previousSearch.search !== 'function' 12711 ? previousSearch.search 12712 : '' 12713 ); 12714 } 12715 } ); 12716 12717 return filter; 12718 }, 'f' ); 12719 12720 // opts 12721 // - type - button configuration 12722 // - buttons - number of buttons to show - must be odd 12723 DataTable.feature.register( 'paging', function ( settings, opts ) { 12724 // Don't show the paging input if the table doesn't have paging enabled 12725 if (! settings.oFeatures.bPaginate) { 12726 return null; 12727 } 12728 12729 opts = $.extend({ 12730 buttons: DataTable.ext.pager.numbers_length, 12731 type: settings.sPaginationType, 12732 boundaryNumbers: true 12733 }, opts); 12734 12735 // To be removed in 2.1 12736 if (opts.numbers) { 12737 opts.buttons = opts.numbers; 12738 } 12739 12740 var host = $('<div/>').addClass( settings.oClasses.paging.container + ' paging_' + opts.type ); 12741 var draw = function () { 12742 _pagingDraw(settings, host, opts); 12743 }; 12744 12745 settings.aoDrawCallback.push(draw); 12746 12747 // Responsive redraw of paging control 12748 $(settings.nTable).on('column-sizing.dt.DT', draw); 12749 12750 return host; 12751 }, 'p' ); 12752 12753 function _pagingDraw(settings, host, opts) { 12754 if (! settings._bInitComplete) { 12755 return; 12756 } 12757 12758 var 12759 plugin = DataTable.ext.pager[ opts.type ], 12760 aria = settings.oLanguage.oAria.paginate || {}, 12761 start = settings._iDisplayStart, 12762 len = settings._iDisplayLength, 12763 visRecords = settings.fnRecordsDisplay(), 12764 all = len === -1, 12765 page = all ? 0 : Math.ceil( start / len ), 12766 pages = all ? 1 : Math.ceil( visRecords / len ), 12767 buttons = plugin() 12768 .map(function (val) { 12769 return val === 'numbers' 12770 ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers) 12771 : val; 12772 }) 12773 .flat(); 12774 12775 var buttonEls = []; 12776 12777 for (var i=0 ; i<buttons.length ; i++) { 12778 var button = buttons[i]; 12779 12780 var btnInfo = _pagingButtonInfo(settings, button, page, pages); 12781 var btn = _fnRenderer( settings, 'pagingButton' )( 12782 settings, 12783 button, 12784 btnInfo.display, 12785 btnInfo.active, 12786 btnInfo.disabled 12787 ); 12788 12789 // Common attributes 12790 $(btn.clicker).attr({ 12791 'aria-controls': settings.sTableId, 12792 'aria-disabled': btnInfo.disabled ? 'true' : null, 12793 'aria-current': btnInfo.active ? 'page' : null, 12794 'aria-label': aria[ button ], 12795 'data-dt-idx': button, 12796 'tabIndex': btnInfo.disabled ? -1 : settings.iTabIndex, 12797 }); 12798 12799 if (typeof button !== 'number') { 12800 $(btn.clicker).addClass(button); 12801 } 12802 12803 _fnBindAction( 12804 btn.clicker, {action: button}, function(e) { 12805 e.preventDefault(); 12806 12807 _fnPageChange( settings, e.data.action, true ); 12808 } 12809 ); 12810 12811 buttonEls.push(btn.display); 12812 } 12813 12814 var wrapped = _fnRenderer(settings, 'pagingContainer')( 12815 settings, buttonEls 12816 ); 12817 12818 var activeEl = host.find(document.activeElement).data('dt-idx'); 12819 12820 host.empty().append(wrapped); 12821 12822 if ( activeEl !== undefined ) { 12823 host.find( '[data-dt-idx='+activeEl+']' ).trigger('focus'); 12824 } 12825 12826 // Responsive - check if the buttons are over two lines based on the 12827 // height of the buttons and the container. 12828 if ( 12829 buttonEls.length && // any buttons 12830 opts.numbers > 1 && // prevent infinite 12831 $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10 12832 ) { 12833 _pagingDraw(settings, host, $.extend({}, opts, { numbers: opts.numbers - 2 })); 12834 } 12835 } 12836 12837 /** 12838 * Get properties for a button based on the current paging state of the table 12839 * 12840 * @param {*} settings DT settings object 12841 * @param {*} button The button type in question 12842 * @param {*} page Table's current page 12843 * @param {*} pages Number of pages 12844 * @returns Info object 12845 */ 12846 function _pagingButtonInfo(settings, button, page, pages) { 12847 var lang = settings.oLanguage.oPaginate; 12848 var o = { 12849 display: '', 12850 active: false, 12851 disabled: false 12852 }; 12853 12854 switch ( button ) { 12855 case 'ellipsis': 12856 o.display = '…'; 12857 o.disabled = true; 12858 break; 12859 12860 case 'first': 12861 o.display = lang.sFirst; 12862 12863 if (page === 0) { 12864 o.disabled = true; 12865 } 12866 break; 12867 12868 case 'previous': 12869 o.display = lang.sPrevious; 12870 12871 if ( page === 0 ) { 12872 o.disabled = true; 12873 } 12874 break; 12875 12876 case 'next': 12877 o.display = lang.sNext; 12878 12879 if ( pages === 0 || page === pages-1 ) { 12880 o.disabled = true; 12881 } 12882 break; 12883 12884 case 'last': 12885 o.display = lang.sLast; 12886 12887 if ( pages === 0 || page === pages-1 ) { 12888 o.disabled = true; 12889 } 12890 break; 12891 12892 default: 12893 if ( typeof button === 'number' ) { 12894 o.display = settings.fnFormatNumber( button + 1 ); 12895 12896 if (page === button) { 12897 o.active = true; 12898 } 12899 } 12900 break; 12901 } 12902 12903 return o; 12904 } 12905 12906 /** 12907 * Compute what number buttons to show in the paging control 12908 * 12909 * @param {*} page Current page 12910 * @param {*} pages Total number of pages 12911 * @param {*} buttons Target number of number buttons 12912 * @param {boolean} addFirstLast Indicate if page 1 and end should be included 12913 * @returns Buttons to show 12914 */ 12915 function _pagingNumbers ( page, pages, buttons, addFirstLast ) { 12916 var 12917 numbers = [], 12918 half = Math.floor(buttons / 2), 12919 before = addFirstLast ? 2 : 1, 12920 after = addFirstLast ? 1 : 0; 12921 12922 if ( pages <= buttons ) { 12923 numbers = _range(0, pages); 12924 } 12925 else if (buttons === 1) { 12926 // Single button - current page only 12927 numbers = [page]; 12928 } 12929 else if (buttons === 3) { 12930 // Special logic for just three buttons 12931 if (page <= 1) { 12932 numbers = [0, 1, 'ellipsis']; 12933 } 12934 else if (page >= pages - 2) { 12935 numbers = _range(pages-2, pages); 12936 numbers.unshift('ellipsis'); 12937 } 12938 else { 12939 numbers = ['ellipsis', page, 'ellipsis']; 12940 } 12941 } 12942 else if ( page <= half ) { 12943 numbers = _range(0, buttons-before); 12944 numbers.push('ellipsis'); 12945 12946 if (addFirstLast) { 12947 numbers.push(pages-1); 12948 } 12949 } 12950 else if ( page >= pages - 1 - half ) { 12951 numbers = _range(pages-(buttons-before), pages); 12952 numbers.unshift('ellipsis'); 12953 12954 if (addFirstLast) { 12955 numbers.unshift(0);
12956 } 12957 } 12958 else { 12959 numbers = _range(page-half+before, page+half-after); 12960 numbers.push('ellipsis'); 12961 numbers.unshift('ellipsis'); 12962 12963 if (addFirstLast) { 12964 numbers.push(pages-1); 12965 numbers.unshift(0); 12966 } 12967 } 12968 12969 return numbers; 12970 } 12971 12972 var __lengthCounter = 0; 12973 12974 // opts 12975 // - menu 12976 // - text 12977 DataTable.feature.register( 'pageLength', function ( settings, opts ) { 12978 var features = settings.oFeatures; 12979 12980 // For compatibility with the legacy `pageLength` top level option 12981 if (! features.bPaginate || ! features.bLengthChange) { 12982 return null; 12983 } 12984 12985 opts = $.extend({ 12986 menu: settings.aLengthMenu, 12987 text: settings.oLanguage.sLengthMenu 12988 }, opts); 12989 12990 var 12991 classes = settings.oClasses.length, 12992 tableId = settings.sTableId, 12993 menu = opts.menu, 12994 lengths = [], 12995 language = [], 12996 i; 12997 12998 // Options can be given in a number of ways 12999 if (Array.isArray( menu[0] )) { 13000 // Old 1.x style - 2D array 13001 lengths = menu[0]; 13002 language = menu[1]; 13003 } 13004 else { 13005 for ( i=0 ; i<menu.length ; i++ ) { 13006 // An object with different label and value 13007 if ($.isPlainObject(menu[i])) { 13008 lengths.push(menu[i].value); 13009 language.push(menu[i].label); 13010 } 13011 else { 13012 // Or just a number to display and use 13013 lengths.push(menu[i]); 13014 language.push(menu[i]); 13015 } 13016 } 13017 } 13018 13019 // We can put the <select> outside of the label if it is at the start or 13020 // end which helps improve accessability (not all screen readers like 13021 // implicit for elements). 13022 var end = opts.text.match(/_MENU_$/); 13023 var start = opts.text.match(/^_MENU_/); 13024 var removed = opts.text.replace(/_MENU_/, ''); 13025 var str = '<label>' + opts.text + '</label>'; 13026 13027 if (start) { 13028 str = '_MENU_<label>' + removed + '</label>'; 13029 } 13030 else if (end) { 13031 str = '<label>' + removed + '</label>_MENU_'; 13032 } 13033 13034 // Wrapper element - use a span as a holder for where the select will go 13035 var div = $('<div/>') 13036 .addClass( classes.container ) 13037 .append( 13038 str.replace( '_MENU_', '<span></span>' ) 13039 ); 13040 13041 // Save text node content for macro updating 13042 var textNodes = []; 13043 div.find('label')[0].childNodes.forEach(function (el) { 13044 if (el.nodeType === Node.TEXT_NODE) { 13045 textNodes.push({ 13046 el: el, 13047 text: el.textContent 13048 }); 13049 } 13050 }) 13051 13052 // Update the label text in case it has an entries value 13053 var updateEntries = function (len) { 13054 textNodes.forEach(function (node) { 13055 node.el.textContent = _fnMacros(settings, node.text, len); 13056 }); 13057 } 13058 13059 // Next, the select itself, along with the options 13060 var select = $('<select/>', { 13061 'name': tableId+'_length', 13062 'aria-controls': tableId, 13063 'class': classes.select 13064 } ); 13065 13066 for ( i=0 ; i<lengths.length ; i++ ) { 13067 select[0][ i ] = new Option( 13068 typeof language[i] === 'number' ? 13069 settings.fnFormatNumber( language[i] ) : 13070 language[i], 13071 lengths[i] 13072 ); 13073 } 13074 13075 // add for and id to label and input 13076 div.find('label').attr('for', 'dt-length-' + __lengthCounter); 13077 select.attr('id', 'dt-length-' + __lengthCounter); 13078 __lengthCounter++; 13079 13080 // Swap in the select list 13081 div.find('span').replaceWith(select); 13082 13083 // Can't use `select` variable as user might provide their own and the 13084 // reference is broken by the use of outerHTML 13085 $('select', div) 13086 .val( settings._iDisplayLength ) 13087 .on( 'change.DT', function() { 13088 _fnLengthChange( settings, $(this).val() ); 13089 _fnDraw( settings ); 13090 } ); 13091 13092 // Update node value whenever anything changes the table's length 13093 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) { 13094 if ( settings === s ) { 13095 $('select', div).val( len ); 13096 13097 // Resolve plurals in the text for the new length 13098 updateEntries(len); 13099 } 13100 } ); 13101 13102 updateEntries(settings._iDisplayLength); 13103 13104 return div; 13105 }, 'l' ); 13106 13107 // jQuery access 13108 $.fn.dataTable = DataTable; 13109 13110 // Provide access to the host jQuery object (circular reference) 13111 DataTable.$ = $; 13112 13113 // Legacy aliases 13114 $.fn.dataTableSettings = DataTable.settings; 13115 $.fn.dataTableExt = DataTable.ext; 13116 13117 // With a capital `D` we return a DataTables API instance rather than a 13118 // jQuery object 13119 $.fn.DataTable = function ( opts ) { 13120 return $(this).dataTable( opts ).api(); 13121 }; 13122 13123 // All properties that are available to $.fn.dataTable should also be 13124 // available on $.fn.DataTable 13125 $.each( DataTable, function ( prop, val ) { 13126 $.fn.DataTable[ prop ] = val; 13127 } ); 13128 13129 return DataTable; 13130}));
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.