vendor: 6,079 bytes, lines 1-169
1/*! DataTables 2.3.4 2 * © SpryMedia Ltd - datatables.net/license 3 */ 4 5(function (factory) { 6 "use strict"; 7 8 if (typeof define === 'function' && define.amd) { 9 // AMD 10 define(['jquery'], function ($) { 11 return factory($, window, document); 12 }); 13 } 14 else if (typeof exports === 'object') { 15 // CommonJS 16 // jQuery's factory checks for a global window - if it isn't present then it 17 // returns a factory function that expects the window object 18 var jq = require('jquery'); 19 20 if (typeof window === 'undefined') { 21 module.exports = function (root, $) { 22 if (!root) { 23 // CommonJS environments without a window global must pass a 24 // root. This will give an error otherwise 25 root = window; 26 } 27 28 if (!$) { 29 $ = jq(root); 30 } 31 32 return factory($, root, root.document); 33 }; 34 } 35 else { 36 module.exports = factory(jq, window, window.document); 37 } 38 } 39 else { 40 // Browser 41 window.DataTable = factory(jQuery, window, document); 42 } 43}(function ($, window, document) { 44 "use strict"; 45 46 47 var DataTable = function (selector, options) { 48 // Check if called with a window or jQuery object for DOM less applications 49 // This is for backwards compatibility 50 if (DataTable.factory(selector, options)) { 51 return DataTable; 52 } 53 54 // When creating with `new`, create a new DataTable, returning the API instance 55 if (this instanceof DataTable) { 56 return $(selector).DataTable(options); 57 } 58 else { 59 // Argument switching 60 options = selector; 61 } 62 63 var _that = this; 64 var emptyInit = options === undefined; 65 var len = this.length; 66 67 if (emptyInit) { 68 options = {}; 69 } 70 71 // Method to get DT API instance from jQuery object 72 this.api = function () { 73 return new _Api(this); 74 }; 75 76 this.each(function () { 77 // For each initialisation we want to give it a clean initialisation 78 // object that can be bashed around 79 var o = {}; 80 var oInit = len > 1 ? // optimisation for single table case 81 _fnExtend(o, options, true) : 82 options; 83 84 85 var i = 0, iLen; 86 var sId = this.getAttribute('id'); 87 var defaults = DataTable.defaults; 88 var $this = $(this); 89 90 // Sanity check 91 if (this.nodeName.toLowerCase() != 'table') { 92 _fnLog(null, 0, 'Non-table node initialisation (' + this.nodeName + ')', 2); 93 return; 94 } 95 96 // Special case for options 97 if (oInit.on && oInit.on.options) { 98 _fnListener($this, 'options', oInit.on.options); 99 } 100 101 $this.trigger('options.dt', oInit); 102 103 /* Backwards compatibility for the defaults */ 104 _fnCompatOpts(defaults); 105 _fnCompatCols(defaults.column); 106 107 /* Convert the camel-case defaults to Hungarian */ 108 _fnCamelToHungarian(defaults, defaults, true); 109 _fnCamelToHungarian(defaults.column, defaults.column, true); 110 111 /* Setting up the initialisation object */ 112 _fnCamelToHungarian(defaults, $.extend(oInit, _fnEscapeObject($this.data())), true); 113 114 115 116 /* Check to see if we are re-initialising a table */ 117 var allSettings = DataTable.settings; 118 for (i = 0, iLen = allSettings.length; i < iLen; i++) { 119 var s = allSettings[i]; 120 121 /* Base check on table node */ 122 if ( 123 s.nTable == this || 124 (s.nTHead && s.nTHead.parentNode == this) || 125 (s.nTFoot && s.nTFoot.parentNode == this) 126 ) { 127 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve; 128 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy; 129 130 if (emptyInit || bRetrieve) { 131 return s.oInstance; 132 } 133 else if (bDestroy) { 134 new DataTable.Api(s).destroy(); 135 break; 136 } 137 else { 138 _fnLog(s, 0, 'Cannot reinitialise DataTable', 3); 139 return; 140 } 141 } 142 143 /* If the element we are initialising has the same ID as a table which was previously 144 * initialised, but the table nodes don't match (from before) then we destroy the old 145 * instance by simply deleting it. This is under the assumption that the table has been 146 * destroyed by other methods. Anyone using non-id selectors will need to do this manually 147 */ 148 if (s.sTableId == this.id) { 149 allSettings.splice(i, 1); 150 break; 151 } 152 } 153 154 /* Ensure the table has an ID - required for accessibility */ 155 if (sId === null || sId === "") { 156 sId = "DataTables_Table_" + (DataTable.ext._unique++); 157 this.id = sId; 158 } 159 160 // Replacing an existing colgroup with our own. Not ideal, but a merge could take a lot of code 161 $this.children('colgroup').remove(); 162 163 /* Create the settings object for this table and set some of the default parameters */ 164 var oSettings = $.extend(true, {}, DataTable.models.oSettings, { 165 "sDestroyWidth": $this[0].style.width, 166 "sInstance": sId, 167 "sTableId": sId, 168 colgroup: $('<colgroup>').prependTo(this), 169 fastData: function (row, column, type) {
170 return _fnGetCellData(oSettings, row, column, type); 171 } 172 }); 173 oSettings.nTable = this; 174 oSettings.oInit = oInit; 175 176 allSettings.push(oSettings); 177 178 // Make a single API instance available for internal handling 179 oSettings.api = new _Api(oSettings); 180 181 // Need to add the instance after the instance after the settings object has been added 182 // to the settings array, so we can self reference the table instance if more than one 183 oSettings.oInstance = (_that.length === 1) ? _that : $this.dataTable(); 184 185 // Backwards compatibility, before we apply all the defaults 186 _fnCompatOpts(oInit); 187 188 // If the length menu is given, but the init display length is not, use the length menu 189 if (oInit.aLengthMenu && !oInit.iDisplayLength) { 190 oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0]) 191 ? oInit.aLengthMenu[0][0] 192 : $.isPlainObject(oInit.aLengthMenu[0]) 193 ? oInit.aLengthMenu[0].value 194 : oInit.aLengthMenu[0]; 195 } 196 197 // Apply the defaults and init options to make a single init object will all 198 // options defined from defaults and instance options. 199 oInit = _fnExtend($.extend(true, {}, defaults), oInit); 200 201 202 // Map the initialisation options onto the settings object 203 _fnMap(oSettings.oFeatures, oInit, [ 204 "bPaginate", 205 "bLengthChange", 206 "bFilter", 207 "bSort", 208 "bSortMulti", 209 "bInfo", 210 "bProcessing", 211 "bAutoWidth", 212 "bSortClasses", 213 "bServerSide", 214 "bDeferRender" 215 ]); 216 _fnMap(oSettings, oInit, [ 217 "ajax", 218 "fnFormatNumber", 219 "sServerMethod", 220 "aaSorting", 221 "aaSortingFixed", 222 "aLengthMenu", 223 "sPaginationType", 224 "iStateDuration", 225 "bSortCellsTop", 226 "iTabIndex", 227 "sDom", 228 "fnStateLoadCallback", 229 "fnStateSaveCallback", 230 "renderer", 231 "searchDelay", 232 "rowId", 233 "caption", 234 "layout", 235 "orderDescReverse", 236 "orderIndicators", 237 "orderHandler", 238 "titleRow", 239 "typeDetect", 240 ["iCookieDuration", "iStateDuration"], // backwards compat 241 ["oSearch", "oPreviousSearch"], 242 ["aoSearchCols", "aoPreSearchCols"], 243 ["iDisplayLength", "_iDisplayLength"] 244 ]); 245 _fnMap(oSettings.oScroll, oInit, [ 246 ["sScrollX", "sX"], 247 ["sScrollXInner", "sXInner"], 248 ["sScrollY", "sY"], 249 ["bScrollCollapse", "bCollapse"] 250 ]); 251 _fnMap(oSettings.oLanguage, oInit, "fnInfoCallback"); 252 253 /* Callback functions which are array driven */ 254 _fnCallbackReg(oSettings, 'aoDrawCallback', oInit.fnDrawCallback); 255 _fnCallbackReg(oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams); 256 _fnCallbackReg(oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams); 257 _fnCallbackReg(oSettings, 'aoStateLoaded', oInit.fnStateLoaded); 258 _fnCallbackReg(oSettings, 'aoRowCallback', oInit.fnRowCallback); 259 _fnCallbackReg(oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow); 260 _fnCallbackReg(oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback); 261 _fnCallbackReg(oSettings, 'aoFooterCallback', oInit.fnFooterCallback); 262 _fnCallbackReg(oSettings, 'aoInitComplete', oInit.fnInitComplete); 263 _fnCallbackReg(oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback); 264 265 oSettings.rowIdFn = _fnGetObjectDataFn(oInit.rowId); 266 267 // Add event listeners 268 if (oInit.on) {
269 Object.keys(oInit.on).forEach(function (key) { 270 _fnListener($this, key, oInit.on[key]); 271 }); 272 } 273 274 /* Browser support detection */ 275 _fnBrowserDetect(oSettings); 276 277 var oClasses = oSettings.oClasses; 278 279 $.extend(oClasses, DataTable.ext.classes, oInit.oClasses); 280 $this.addClass(oClasses.table); 281 282 if (!oSettings.oFeatures.bPaginate) { 283 oInit.iDisplayStart = 0; 284 } 285 286 if (oSettings.iInitDisplayStart === undefined) { 287 /* Display start point, taking into account the save saving */ 288 oSettings.iInitDisplayStart = oInit.iDisplayStart; 289 oSettings._iDisplayStart = oInit.iDisplayStart; 290 } 291 292 var defer = oInit.iDeferLoading; 293 if (defer !== null) { 294 oSettings.deferLoading = true; 295 296 var tmp = Array.isArray(defer); 297 oSettings._iRecordsDisplay = tmp ? defer[0] : defer; 298 oSettings._iRecordsTotal = tmp ? defer[1] : defer; 299 } 300 301 /* 302 * Columns 303 * See if we should load columns automatically or use defined ones 304 */ 305 var columnsInit = []; 306 var thead = this.getElementsByTagName('thead'); 307 var initHeaderLayout = _fnDetectHeader(oSettings, thead[0]); 308 309 // If we don't have a columns array, then generate one with nulls 310 if (oInit.aoColumns) { 311 columnsInit = oInit.aoColumns; 312 } 313 else if (initHeaderLayout.length) { 314 for (i = 0, iLen = initHeaderLayout[0].length; i < iLen; i++) { 315 columnsInit.push(null); 316 } 317 } 318 319 // Add the columns 320 for (i = 0, iLen = columnsInit.length; i < iLen; i++) { 321 _fnAddColumn(oSettings); 322 } 323 324 // Apply the column definitions 325 _fnApplyColumnDefs(oSettings, oInit.aoColumnDefs, columnsInit, initHeaderLayout, function (iCol, oDef) { 326 _fnColumnOptions(oSettings, iCol, oDef); 327 }); 328 329 /* HTML5 attribute detection - build an mData object automatically if the 330 * attributes are found 331 */ 332 var rowOne = $this.children('tbody').find('tr:first-child').eq(0); 333 334 if (rowOne.length) { 335 var a = function (cell, name) { 336 return cell.getAttribute('data-' + name) !== null ? name : null; 337 }; 338 339 $(rowOne[0]).children('th, td').each(function (i, cell) { 340 var col = oSettings.aoColumns[i]; 341 342 if (!col) { 343 _fnLog(oSettings, 0, 'Incorrect column count', 18); 344 } 345 346 if (col.mData === i) { 347 var sort = a(cell, 'sort') || a(cell, 'order'); 348 var filter = a(cell, 'filter') || a(cell, 'search'); 349 350 if (sort !== null || filter !== null) { 351 col.mData = { 352 _: i + '.display', 353 sort: sort !== null ? i + '.@data-' + sort : undefined, 354 type: sort !== null ? i + '.@data-' + sort : undefined, 355 filter: filter !== null ? i + '.@data-' + filter : undefined 356 }; 357 col._isArrayHost = true; 358 359 _fnColumnOptions(oSettings, i); 360 } 361 } 362 }); 363 } 364 365 // Must be done after everything which can be overridden by the state saving! 366 _fnCallbackReg(oSettings, 'aoDrawCallback', _fnSaveState); 367 368 var features = oSettings.oFeatures; 369 if (oInit.bStateSave) { 370 features.bStateSave = true; 371 } 372 373 // If aaSorting is not defined, then we use the first indicator in asSorting 374 // in case that has been altered, so the default sort reflects that option 375 if (oInit.aaSorting === undefined) { 376 var sorting = oSettings.aaSorting; 377 for (i = 0, iLen = sorting.length; i < iLen; i++) { 378 sorting[i][1] = oSettings.aoColumns[i].asSorting[0]; 379 } 380 } 381 382 // Do a first pass on the sorting classes (allows any size changes to be taken into 383 // account, and also will apply sorting disabled classes if disabled 384 _fnSortingClasses(oSettings); 385 386 _fnCallbackReg(oSettings, 'aoDrawCallback', function () { 387 if (oSettings.bSorted || _fnDataSource(oSettings) === 'ssp' || features.bDeferRender) { 388 _fnSortingClasses(oSettings); 389 } 390 }); 391 392 393 /* 394 * Table HTML init 395 * Cache the header, body and footer as required, creating them if needed 396 */ 397 var caption = $this.children('caption'); 398 399 if (oSettings.caption) { 400 if (caption.length === 0) { 401 caption = $('<caption/>').appendTo($this); 402 } 403 404 caption.html(oSettings.caption); 405 } 406 407 // Store the caption side, so we can remove the element from the document 408 // when creating the element 409 if (caption.length) { 410 caption[0]._captionSide = caption.css('caption-side'); 411 oSettings.captionNode = caption[0]; 412 } 413 414 if (thead.length === 0) { 415 thead = $('<thead/>').appendTo($this); 416 } 417 oSettings.nTHead = thead[0]; 418 419 var tbody = $this.children('tbody'); 420 if (tbody.length === 0) { 421 tbody = $('<tbody/>').insertAfter(thead); 422 } 423 oSettings.nTBody = tbody[0]; 424 425 var tfoot = $this.children('tfoot'); 426 if (tfoot.length === 0) { 427 // If we are a scrolling table, and no footer has been given, then we need to create 428 // a tfoot element for the caption element to be appended to 429 tfoot = $('<tfoot/>').appendTo($this); 430 } 431 oSettings.nTFoot = tfoot[0]; 432 433 // Copy the data index array 434 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 435 436 // Initialisation complete - table can be drawn
437 oSettings.bInitialised = true; 438 439 // Language definitions 440 var oLanguage = oSettings.oLanguage; 441 $.extend(true, oLanguage, oInit.oLanguage); 442 443 if (oLanguage.sUrl) { 444 // Get the language definitions from a file 445 $.ajax({ 446 dataType: 'json', 447 url: oLanguage.sUrl, 448 success: function (json) { 449 _fnCamelToHungarian(defaults.oLanguage, json); 450 $.extend(true, oLanguage, json, oSettings.oInit.oLanguage); 451 452 _fnCallbackFire(oSettings, null, 'i18n', [oSettings], true); 453 _fnInitialise(oSettings); 454 }, 455 error: function () { 456 // Error occurred loading language file 457 _fnLog(oSettings, 0, 'i18n file loading error', 21); 458 459 // Continue on as best we can 460 _fnInitialise(oSettings); 461 } 462 }); 463 } 464 else { 465 _fnCallbackFire(oSettings, null, 'i18n', [oSettings], true); 466 _fnInitialise(oSettings); 467 } 468 }); 469 _that = null; 470 return this; 471 }; 472 473 474 475 /** 476 * DataTables extensions 477 * 478 * This namespace acts as a collection area for plug-ins that can be used to 479 * extend DataTables capabilities. Indeed many of the build in methods 480 * use this method to provide their own capabilities (sorting methods for 481 * example). 482 * 483 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy 484 * reasons 485 * 486 * @namespace 487 */ 488 DataTable.ext = _ext = { 489 /** 490 * DataTables build type (expanded by the download builder) 491 * 492 * @type string 493 */ 494 builder: "-source-", 495 496 /** 497 * Buttons. For use with the Buttons extension for DataTables. This is 498 * defined here so other extensions can define buttons regardless of load 499 * order. It is _not_ used by DataTables core. 500 * 501 * @type object 502 * @default {} 503 */ 504 buttons: {}, 505 506 507 /** 508 * ColumnControl buttons and content 509 * 510 * @type object 511 */ 512 ccContent: {}, 513 514 515 /** 516 * Element class names 517 * 518 * @type object 519 * @default {} 520 */ 521 classes: {}, 522 523 524 /** 525 * Error reporting. 526 * 527 * How should DataTables report an error. Can take the value 'alert', 528 * 'throw', 'none' or a function. 529 * 530 * @type string|function 531 * @default alert 532 */ 533 errMode: "alert", 534 535 /** HTML entity escaping */ 536 escape: { 537 /** When reading data-* attributes for initialisation options */ 538 attributes: false 539 }, 540 541 /** 542 * Legacy so v1 plug-ins don't throw js errors on load 543 */ 544 feature: [], 545 546 /** 547 * Feature plug-ins. 548 * 549 * This is an object of callbacks which provide the features for DataTables 550 * to be initialised via the `layout` option. 551 */ 552 features: {}, 553 554 555 /** 556 * Row searching. 557 * 558 * This method of searching is complimentary to the default type based 559 * searching, and a lot more comprehensive as it allows you complete control 560 * over the searching logic. Each element in this array is a function 561 * (parameters described below) that is called for every row in the table, 562 * and your logic decides if it should be included in the searching data set 563 * or not. 564 * 565 * Searching functions have the following input parameters: 566 * 567 * 1. `{object}` DataTables settings object: see 568 * {@link DataTable.models.oSettings} 569 * 2. `{array|object}` Data for the row to be processed (same as the 570 * original format that was passed in as the data source, or an array 571 * from a DOM data source 572 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which 573 * can be useful to retrieve the `TR` element if you need DOM
573interaction. 574 * 575 * And the following return is expected: 576 * 577 * * {boolean} Include the row in the searched result set (true) or not 578 * (false) 579 * 580 * Note that as with the main search ability in DataTables, technically this 581 * is "filtering", since it is subtractive. However, for consistency in 582 * naming we call it searching here. 583 * 584 * @type array 585 * @default [] 586 * 587 * @example 588 * // The following example shows custom search being applied to the 589 * // fourth column (i.e. the data[3] index) based on two input values 590 * // from the end-user, matching the data in a certain range. 591 * $.fn.dataTable.ext.search.push( 592 * function( settings, data, dataIndex ) { 593 * var min = document.getElementById('min').value * 1; 594 * var max = document.getElementById('max').value * 1; 595 * var version = data[3] == "-" ? 0 : data[3]*1; 596 * 597 * if ( min == "" && max == "" ) { 598 * return true; 599 * } 600 * else if ( min == "" && version < max ) { 601 * return true; 602 * } 603 * else if ( min < version && "" == max ) { 604 * return true; 605 * } 606 * else if ( min < version && version < max ) { 607 * return true; 608 * } 609 * return false; 610 * } 611 * ); 612 */ 613 search: [], 614 615 616 /** 617 * Selector extensions 618 * 619 * The `selector` option can be used to extend the options available for the 620 * selector modifier options (`selector-modifier` object data type) that 621 * each of the three built in selector types offer (row, column and cell + 622 * their plural counterparts). For example the Select extension uses this 623 * mechanism to provide an option to select only rows, columns and cells 624 * that have been marked as selected by the end user (`{selected: true}`), 625 * which can be used in conjunction with the existing built in selector 626 * options. 627 * 628 * Each property is an array to which functions can be pushed. The functions 629 * take three attributes: 630 * 631 * * Settings object for the host table 632 * * Options object (`selector-modifier` object type) 633 * * Array of selected item indexes 634 * 635 * The return is an array of the resulting item indexes after the custom 636 * selector has been applied. 637 * 638 * @type object 639 */ 640 selector: { 641 cell: [], 642 column: [], 643 row: [] 644 }, 645 646 647 /** 648 * Legacy configuration options. Enable and disable legacy options that 649 * are available in DataTables. 650 * 651 * @type object 652 */ 653 legacy: { 654 /** 655 * Enable / disable DataTables 1.9 compatible server-side processing 656 * requests 657 * 658 * @type boolean 659 * @default null 660 */ 661 ajax: null 662 }, 663 664 665 /** 666 * Pagination plug-in methods. 667 * 668 * Each entry in this object is a function and defines which buttons should 669 * be shown by the pagination rendering method that is used for the table: 670 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the 671 * buttons are displayed in the document, while the functions here tell it 672 * what buttons to display. This is done by returning an array of button 673 * descriptions (what each button will do). 674 * 675 * Pagination types (the four built in options and any additional plug-in 676 * options defined here) can be used through the `paginationType` 677 * initialisation parameter. 678 * 679 * The functions defined take two parameters: 680 * 681 * 1. `{int} page` The current page index 682 * 2. `{int} pages` The number of pages in the table 683 * 684 * Each function is expected to return an array where each element of the 685 * array can be one of: 686 * 687 * * `first` - Jump to first page when activated 688 * * `last` - Jump to last page when activated 689 * * `previous` - Show previous page when activated 690 * * `next` - Show next page when activated 691 * * `{int}` - Show page of the index given 692 * * `{array}` - A nested array containing the above elements to add a 693 * containing 'DIV' element (might be useful for styling). 694 * 695 * Note that DataTables v1.9- used this object slightly differently whereby 696 * an object with two functions would be defined for each plug-in. That 697 * ability is still supported by DataTables 1.10+ to provide backwards 698 * compatibility, but this option of use is now decremented and no longer 699 * documented in DataTables 1.10+. 700 * 701 * @type object 702 * @default {} 703 * 704 * @example 705 * // Show previous, next and current page buttons only 706 * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { 707 * return [ 'previous', page, 'next' ]; 708 * }; 709 */ 710 pager: {}, 711 712 713 renderer: { 714 pageButton: {}, 715 header: {} 716 }, 717 718 719 /** 720 * Ordering plug-ins - custom data source 721 *
722 * The extension options for ordering of data available here is complimentary 723 * to the default type based ordering that DataTables typically uses. It 724 * allows much greater control over the data that is being used to 725 * order a column, but is necessarily therefore more complex. 726 * 727 * This type of ordering is useful if you want to do ordering based on data 728 * live from the DOM (for example the contents of an 'input' element) rather 729 * than just the static string that DataTables knows of. 730 * 731 * The way these plug-ins work is that you create an array of the values you 732 * wish to be ordering for the column in question and then return that 733 * array. The data in the array much be in the index order of the rows in 734 * the table (not the currently ordering order!). Which order data gathering 735 * function is run here depends on the `dt-init columns.orderDataType` 736 * parameter that is used for the column (if any). 737 * 738 * The functions defined take two parameters: 739 * 740 * 1. `{object}` DataTables settings object: see 741 * {@link DataTable.models.oSettings} 742 * 2. `{int}` Target column index 743 * 744 * Each function is expected to return an array: 745 * 746 * * `{array}` Data for the column to be ordering upon 747 * 748 * @type array 749 * 750 * @example 751 * // Ordering using `input` node values 752 * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) 753 * { 754 * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { 755 * return $('input', td).val(); 756 * } ); 757 * } 758 */ 759 order: {}, 760 761 762 /** 763 * Type based plug-ins. 764 * 765 * Each column in DataTables has a type assigned to it, either by automatic 766 * detection or by direct assignment using the `type` option for the column. 767 * The type of a column will effect how it is ordering and search (plug-ins 768 * can also make use of the column type if required). 769 * 770 * @namespace 771 */ 772 type: { 773 /** 774 * Automatic column class assignment 775 */ 776 className: {}, 777 778 /** 779 * Type detection functions. 780 * 781 * The functions defined in this object are used to automatically detect 782 * a column's type, making initialisation of DataTables super easy, even 783 * when complex data is in the table. 784 * 785 * The functions defined take two parameters: 786 * 787 * 1. `{*}` Data from the column cell to be analysed 788 * 2. `{settings}` DataTables settings object. This can be used to 789 * perform context specific type detection - for example detection 790 * based on language settings such as using a comma for a decimal 791 * place. Generally speaking the options from the settings will not 792 * be required 793 * 794 * Each function is expected to return: 795 * 796 * * `{string|null}` Data type detected, or null if unknown (and thus 797 * pass it on to the other type detection functions. 798 * 799 * @type array 800 * 801 * @example 802 * // Currency type detection plug-in: 803 * $.fn.dataTable.ext.type.detect.push( 804 * function ( data, settings ) { 805 * // Check the numeric part 806 * if ( ! data.substring(1).match(/[0-9]/) ) { 807 * return null; 808 * } 809 * 810 * // Check prefixed by currency 811 * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) { 812 * return 'currency'; 813 * } 814 * return null; 815 * } 816 * ); 817 */ 818 detect: [], 819 820 /** 821 * Automatic renderer assignment 822 */ 823 render: {}, 824 825 826 /** 827 * Type based search formatting. 828 * 829 * The type based searching functions can be used to pre-format the 830 * data to be search on. For example, it can be used to strip HTML 831 * tags or to de-format telephone numbers for numeric only searching. 832 * 833 * Note that is a search is not defined for a column of a given type, 834 * no search formatting will be performed. 835 * 836 * Pre-processing of searching data plug-ins - When you assign the sType 837 * for a column (or have it automatically detected for you by DataTables 838 * or a type detection plug-in), you will typically be using this for 839 * custom sorting, but it can also be used to provide custom searching 840 * by allowing you to pre-processing the data and returning the data in 841 * the format that should be searched upon. This is done by adding 842 * functions this object with a parameter name which matches the sType 843 * for that target column. This is the corollary of <i>afnSortData</i> 844 * for searching data. 845 * 846 * The functions defined take a single parameter: 847 * 848 * 1. `{*}` Data from the column cell to be prepared for searching 849 * 850 * Each function is expected to return: 851 * 852 * * `{string|null}` Formatted string that will be used for the searching. 853 * 854 * @type object 855 * @default {} 856 * 857 * @example 858 * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { 859 * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); 860 * } 861 */ 862 search: {}, 863 864 865 /** 866 * Type based ordering. 867 * 868 * The column type tells DataTables what ordering to apply to the table 869 * when a column is sorted upon. The order for each type that is defined, 870 * is defined by the functions available in this object. 871 * 872 * Each ordering option can be described by three properties added to 873 * this object: 874 * 875 * * `{type}-pre` - Pre-formatting function 876 * * `{type}-asc` - Ascending order function 877 * * `{type}-desc` - Descending order function 878 * 879 * All three can be used together, only `{type}-pre` or only 880 * `{type}-asc` and `{type}-desc` together. It is generally recommended 881 * that only `{type}-pre` is used, as this provides the optimal 882 * implementation in terms of speed, although the others are provided 883 * for compatibility with existing JavaScript sort functions. 884 * 885 * `{type}-pre`: Functions defined take a single parameter: 886 * 887 * 1. `{*}` Data from the column cell to be prepared for ordering 888 * 889 * And return: 890 * 891 * * `{*}` Data to be sorted upon 892 * 893 * `{type}-asc` and `{type}-desc`: Functions are typical JavaScript sort 894 * functions, taking two parameters: 895 * 896 * 1. `{*}` Data to compare to the second parameter 897 * 2. `{*}` Data to compare to the first parameter 898 * 899 * And returning: 900 * 901 * * `{*}` Ordering match: <0 if first parameter should be sorted lower 902 * than the second parameter, ===0 if the two parameters are equal and 903 * >0 if the first parameter should be sorted height than the second 904 * parameter. 905 * 906 * @type object 907 * @default {} 908 * 909 * @example 910 * // Numeric ordering of formatted numbers with a pre-formatter 911 * $.extend( $.fn.dataTable.ext.type.order, { 912 * "string-pre": function(x) { 913 * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); 914 * return parseFloat( a ); 915 * } 916 * } ); 917 * 918 * @example 919 * // Case-sensitive string ordering, with no pre-formatting method 920 * $.extend( $.fn.dataTable.ext.order, { 921 * "string-case-asc": function(x,y) { 922 * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 923 * }, 924 * "string-case-desc": function(x,y) { 925 * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 926 * } 927 * } ); 928 */ 929 order: {} 930 }, 931 932 /** 933 * Unique DataTables instance counter 934 * 935 * @type int 936 * @private 937 */ 938 _unique: 0, 939 940 941 // 942 // Depreciated 943 // The following properties are retained for backwards compatibility only. 944 // The should not be used in new projects and will be removed in a future 945 // version 946 // 947 948 /** 949 * Version check function. 950 * @type function 951 * @depreciated Since 1.10 952 */ 953 fnVersionCheck: DataTable.fnVersionCheck, 954 955 956 /** 957 * Index for what 'this' index API functions should use 958 * @type int 959 * @deprecated Since v1.10 960 */ 961 iApiIndex: 0, 962 963 964 /** 965 * Software version 966 * @type string 967 * @deprecated Since v1.10 968 */
969 sVersion: DataTable.version 970 }; 971 972 973 // 974 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts 975 // 976 $.extend(_ext, { 977 afnFiltering: _ext.search, 978 aTypes: _ext.type.detect, 979 ofnSearch: _ext.type.search, 980 oSort: _ext.type.order, 981 afnSortData: _ext.order, 982 aoFeatures: _ext.feature, 983 oStdClasses: _ext.classes, 984 oPagination: _ext.pager 985 }); 986 987 988 $.extend(DataTable.ext.classes, { 989 container: 'dt-container', 990 empty: { 991 row: 'dt-empty' 992 }, 993 info: { 994 container: 'dt-info' 995 }, 996 layout: { 997 row: 'dt-layout-row', 998 cell: 'dt-layout-cell', 999 tableRow: 'dt-layout-table', 1000 tableCell: '', 1001 start: 'dt-layout-start', 1002 end: 'dt-layout-end', 1003 full: 'dt-layout-full' 1004 }, 1005 length: { 1006 container: 'dt-length', 1007 select: 'dt-input' 1008 }, 1009 order: { 1010 canAsc: 'dt-orderable-asc', 1011 canDesc: 'dt-orderable-desc', 1012 isAsc: 'dt-ordering-asc', 1013 isDesc: 'dt-ordering-desc', 1014 none: 'dt-orderable-none', 1015 position: 'sorting_' 1016 }, 1017 processing: { 1018 container: 'dt-processing' 1019 }, 1020 scrolling: { 1021 body: 'dt-scroll-body', 1022 container: 'dt-scroll', 1023 footer: { 1024 self: 'dt-scroll-foot', 1025 inner: 'dt-scroll-footInner' 1026 }, 1027 header: { 1028 self: 'dt-scroll-head', 1029 inner: 'dt-scroll-headInner' 1030 } 1031 }, 1032 search: { 1033 container: 'dt-search', 1034 input: 'dt-input' 1035 }, 1036 table: 'dataTable', 1037 tbody: { 1038 cell: '', 1039 row: '' 1040 }, 1041 thead: { 1042 cell: '', 1043 row: '' 1044 }, 1045 tfoot: { 1046 cell: '', 1047 row: '' 1048 }, 1049 paging: { 1050 active: 'current', 1051 button: 'dt-paging-button', 1052 container: 'dt-paging', 1053 disabled: 'disabled', 1054 nav: '' 1055 } 1056 }); 1057 1058 1059 /* 1060 * It is useful to have variables which are scoped locally s
1060o only the 1061 * DataTables functions can access them and they don't leak into global space. 1062 * At the same time these functions are often useful over multiple files in the 1063 * core and API, so we list, or at least document, all variables which are used 1064 * by DataTables as private variables here. This also ensures that there is no 1065 * clashing of variable names and that they can easily referenced for reuse. 1066 */ 1067 1068 1069 // Defined else where 1070 // _selector_run 1071 // _selector_opts 1072 // _selector_row_indexes 1073 1074 var _ext; // DataTable.ext 1075 var _Api; // DataTable.Api 1076 var _api_register; // DataTable.Api.register 1077 var _api_registerPlural; // DataTable.Api.registerPlural 1078 1079 var _re_dic = {}; 1080 var _re_new_lines = /[\r\n\u2028]/g; 1081 var _re_html = /<([^>]*>)/g; 1082 var _max_str_len = Math.pow(2, 28); 1083 1084 // This is not strict ISO8601 - Date.parse() is quite lax, although 1085 // implementations differ between browsers. 1086 var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/; 1087 1088 // Escape regular expression special characters 1089 var _re_escape_regex = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-'].join('|\\') + ')', 'g'); 1090 1091 // https://en.wikipedia.org/wiki/Foreign_exchange_market 1092 // - \u20BD - Russian ruble. 1093 // - \u20a9 - South Korean Won 1094 // - \u20BA - Turkish Lira 1095 // - \u20B9 - Indian Rupee 1096 // - R - Brazil (R$) and South Africa 1097 // - fr - Swiss Franc 1098 // - kr - Swedish krona, Norwegian krone and Danish krone 1099 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many 1100 // - É - Bitcoin 1101 // - Î - Ethereum 1102 // standards as thousands separators. 1103 var _re_formatted_numeric = /['\u00A0,$£â¬Â¥%\u2009\u202F\u20BD\u20a9\u20BArfkÉÎ]/gi; 1104 1105 1106 var _empty = function (d) { 1107 return !d || d === true || d === '-' ? true : false; 1108 }; 1109 1110 1111 var _intVal = function (s) { 1112 var integer = parseInt(s, 10); 1113 return !isNaN(integer) && isFinite(s) ? integer : null; 1114 }; 1115 1116 // Convert from a formatted number with characters other than `.` as the 1117 // decimal place, to a JavaScript number 1118 var _numToDecimal = function (num, decimalPoint) { 1119 // Cache created regular expressions for speed as this function is called often 1120 if (!_re_dic[decimalPoint]) { 1121 _re_dic[decimalPoint] = new RegExp(_fnEscapeRegex(decimalPoint), 'g'); 1122 } 1123 return typeof num === 'string' && decimalPoint !== '.' ? 1124 num.replace(/\./g, '').replace(_re_dic[decimalPoint], '.') : 1125 num; 1126 }; 1127 1128 1129 var _isNumber = function (d, decimalPoint, formatted, allowEmpty) { 1130 var type = typeof d; 1131 var strType = type === 'string'; 1132 1133 if (type === 'number' || type === 'bigint') { 1134 return true; 1135 } 1136 1137 // If empty return immediately so there must be a number if it is a 1138 // formatted string (this stops the string "k", or "kr", etc being detected 1139 // as a formatted number for currency 1140 if (allowEmpty && _empty(d)) { 1141 return true; 1142 } 1143 1144 if (decimalPoint && strType) { 1145 d = _numToDecimal(d, decimalPoint); 1146 } 1147 1148 if (formatted && strType) { 1149 d = d.replace(_re_formatted_numeric, ''); 1150 } 1151 1152 return !isNaN(parseFloat(d)) && isFinite(d); 1153 }; 1154 1155 1156 // A string without HTML in it can be considered to be HTML still 1157 var _isHtml = function (d) { 1158 return _empty(d) || typeof d === 'string'; 1159 }; 1160 1161 // Is a string a number surrounded by HTML? 1162 var _htmlNumeric = function (d, decimalPoint, formatted, allowEmpty) { 1163 if (allowEmpty && _empty(d)) { 1164 return true; 1165 } 1166 1167 // input and select strings mean that this isn't just a number 1168 if (typeof d === 'string' && d.match(/<(input|select)/i)) { 1169 return null; 1170 } 1171 1172 var html = _isHtml(d); 1173 return !html ? 1174 null : 1175 _isNumber(_stripHtml(d), decimalPoint, formatted, allowEmpty) ? 1176 true : 1177 null; 1178 }; 1179 1180 1181 var _pluck = function (a, prop, prop2) { 1182 var out = []; 1183 var i = 0, iLen = a.length; 1184 1185 // Could have the test in the loop for slightly smaller code, but speed 1186 // is essential here 1187 if (prop2 !== undefined) { 1188 for (; i < iLen; i++) { 1189 if (a[i] && a[i][prop]) { 1190 out.push(a[i][prop][prop2]); 1191 } 1192 } 1193 } 1194 else { 1195 for (; i < iLen; i++) { 1196 if (a[i]) { 1197 out.push(a[i][prop]); 1198 } 1199 } 1200 } 1201 1202 return out; 1203 }; 1204 1205 1206 // Basically the same as _pluck, but rather than looping over `a` we use `order` 1207 // as the indexes to pick from `a` 1208 var _pluck_order = function (a, order, prop, prop2) { 1209 var out = []; 1210 var i = 0, iLen = order.length; 1211 1212 // Could have the test in the loop for slightly smaller code, but speed 1213 // is essential here 1214 if (prop2 !== undefined) { 1215 for (; i < iLen; i++) { 1216 if (a[order[i]] && a[order[i]][prop]) { 1217 out.push(a[order[i]][prop][prop2]); 1218 } 1219 } 1220 } 1221 else { 1222 for (; i < iLen; i++) { 1223 if (a[order[i]]) { 1224 out.push(a[order[i]][prop]); 1225 } 1226 } 1227 } 1228 1229 return out; 1230 }; 1231 1232 1233 var _range = function (len, start) { 1234 var out = []; 1235 var end; 1236 1237 if (start === undefined) { 1238 start = 0; 1239 end = len; 1240 } 1241 else { 1242 end = start; 1243 start = len; 1244 } 1245 1246 for (var i = start; i < end; i++) { 1247 out.push(i); 1248 } 1249 1250 return out; 1251 }; 1252 1253 1254 var _removeEmpty = function (a) { 1255 var out = []; 1256 1257 for (var i = 0, iLen = a.length; i < iLen; i++) { 1258 if (a[i]) { // careful - will remove all falsy values! 1259 out.push(a[i]); 1260 } 1261 } 1262 1263 return out; 1264 }; 1265 1266 // Replaceable function in api.util 1267 var _stripHtml = function (input) { 1268 if (!input || typeof input !== 'string') { 1269 return input; 1270 } 1271 1272 // Irrelevant check to workaround CodeQL's false positive on the regex 1273 if (input.length > _max_str_len) { 1274 throw new Error('Exceeded max str len'); 1275 } 1276 1277 var previous; 1278 1279 input = input.replace(_re_html, ''); // Complete tags 1280 1281 // Safety for incomplete script tag - use do / while to ensure that 1282 // we get all instances 1283 do { 1284 previous = input; 1285 input = input.replace(/<script/i, ''); 1286 } while (input !== previous); 1287 1288 return previous; 1289 }; 1290 1291 // Replaceable function in api.util 1292 var _escapeHtml = function (d) { 1293 if (Array.isArray(d)) { 1294 d = d.join(','); 1295 } 1296 1297 return typeof d === 'string' ? 1298 d 1299 .replace(/&/g, '&') 1300 .replace(/</g, '<') 1301 .replace(/>/g, '>') 1302 .replace(/"/g, '"') : 1303 d; 1304 }; 1305 1306 // Remove diacritics from a string by decomposing it and then removing 1307 // non-ascii characters 1308 var _normalize = function (str, both) { 1309 if (typeof str !== 'string') { 1310 return str; 1311 } 1312 1313 // It is faster to just run `normalize` than it is to check if 1314 // we need to with a regex! (Check as it isn't available in old 1315 // Safari) 1316 var res = str.normalize 1317 ? str.normalize("NFD") 1318 : str; 1319 1320 // Equally, here we check if a regex is needed or not 1321 return res.length !== str.length 1322 ? (both === true ? str + ' ' : '') + res.replace(/[\u0300-\u036f]/g, "") 1323 : res; 1324 } 1325 1326 /** 1327 * Determine if all values in the array are unique. This means we can short 1328 * cut the _unique method at the cost of a single loop. A sorted array is used 1329 * to easily check the values. 1330 * 1331 * @param {array} src Source array 1332 * @return {boolean} true if all unique, false otherwise 1333 * @ignore 1334 */ 1335 var _areAllUnique = function (src) { 1336 if (src.length < 2) { 1337 return true; 1338 } 1339 1340 var sorted = src.slice().sort(); 1341 var last = sorted[0]; 1342 1343 for (var i = 1, iLen = sorted.length; i < iLen; i++) { 1344 if (sorted[i] === last) { 1345 return false;
vendor: 28,737 bytes, lines 1346-2122
1346 } 1347 1348 last = sorted[i]; 1349 } 1350 1351 return true; 1352 }; 1353 1354 1355 /** 1356 * Find the unique elements in a source array. 1357 * 1358 * @param {array} src Source array 1359 * @return {array} Array of unique items 1360 * @ignore 1361 */ 1362 var _unique = function (src) { 1363 if (Array.from && Set) { 1364 return Array.from(new Set(src)); 1365 } 1366 1367 if (_areAllUnique(src)) { 1368 return src.slice(); 1369 } 1370 1371 // A faster unique method is to use object keys to identify used values, 1372 // but this doesn't work with arrays or objects, which we must also 1373 // consider. See jsperf.app/compare-array-unique-versions/4 for more 1374 // information. 1375 var 1376 out = [], 1377 val, 1378 i, iLen = src.length, 1379 j, k = 0; 1380 1381 again: for (i = 0; i < iLen; i++) { 1382 val = src[i]; 1383 1384 for (j = 0; j < k; j++) { 1385 if (out[j] === val) { 1386 continue again; 1387 } 1388 } 1389 1390 out.push(val); 1391 k++; 1392 } 1393 1394 return out; 1395 }; 1396 1397 // Surprisingly this is faster than [].concat.apply 1398 // https://jsperf.com/flatten-an-array-loop-vs-reduce/2 1399 var _flatten = function (out, val) { 1400 if (Array.isArray(val)) { 1401 for (var i = 0; i < val.length; i++) { 1402 _flatten(out, val[i]); 1403 } 1404 } 1405 else { 1406 out.push(val); 1407 } 1408 1409 return out; 1410 } 1411 1412 // Similar to jQuery's addClass, but use classList.add 1413 function _addClass(el, name) { 1414 if (name) { 1415 name.split(' ').forEach(function (n) { 1416 if (n) { 1417 // `add` does deduplication, so no need to check `contains` 1418 el.classList.add(n); 1419 } 1420 }); 1421 } 1422 } 1423 1424 /** 1425 * DataTables utility methods 1426 * 1427 * This namespace provides helper methods that DataTables uses internally to 1428 * create a DataTable, but which are not exclusively used only for DataTables. 1429 * These methods can be used by extension authors to save the duplication of 1430 * code. 1431 * 1432 * @namespace 1433 */ 1434 DataTable.util = { 1435 /** 1436 * Return a string with diacritic characters decomposed 1437 * @param {*} mixed Function or string to normalize 1438 * @param {*} both Return original string and the normalized string 1439 * @returns String or undefined 1440 */ 1441 diacritics: function (mixed, both) { 1442 var type = typeof mixed; 1443 1444 if (type !== 'function') { 1445 return _normalize(mixed, both); 1446 } 1447 _normalize = mixed; 1448 }, 1449 1450 /** 1451 * Debounce a function 1452 * 1453 * @param {function} fn Function to be called 1454 * @param {integer} freq Call frequency in mS 1455 * @return {function} Wrapped function 1456 */ 1457 debounce: function (fn, timeout) { 1458 var timer; 1459 1460 return function () { 1461 var that = this; 1462 var args = arguments; 1463 1464 clearTimeout(timer); 1465 1466 timer = setTimeout(function () { 1467 fn.apply(that, args); 1468 }, timeout || 250); 1469 }; 1470 }, 1471 1472 /** 1473 * Throttle the calls to a function. Arguments and context are maintained 1474 * for the throttled function. 1475 * 1476 * @param {function} fn Function to be called 1477 * @param {integer} freq Call frequency in mS 1478 * @return {function} Wrapped function 1479 */ 1480 throttle: function (fn, freq) { 1481 var 1482 frequency = freq !== undefined ? freq : 200, 1483 last, 1484 timer; 1485 1486 return function () { 1487 var 1488 that = this, 1489 now = +new Date(), 1490 args = arguments; 1491 1492 if (last && now < last + frequency) { 1493 clearTimeout(timer); 1494 1495 timer = setTimeout(function () { 1496 last = undefined; 1497 fn.apply(that, args); 1498 }, frequency); 1499 } 1500 else { 1501 last = now; 1502 fn.apply(that, args); 1503 } 1504 }; 1505 }, 1506 1507 /** 1508 * Escape a string such that it can be used in a regular expression 1509 * 1510 * @param {string} val string to escape 1511 * @returns {string} escaped string 1512 */ 1513 escapeRegex: function (val) { 1514 return val.replace(_re_escape_regex, '\\$1'); 1515 }, 1516 1517 /** 1518 * Create a function that will write to a nested object or array 1519 * @param {*} source JSON notation string 1520 * @returns Write function 1521 */ 1522 set: function (source) { 1523 if ($.isPlainObject(source)) { 1524 /* Unlike get, only the underscore (global) option is used for for 1525 * setting data since we don't know the type here. This is why an object 1526 * option is not documented for `mData` (which is read/write), but it is 1527 * for `mRender` which is read only. 1528 */ 1529 return DataTable.util.set(source._); 1530 } 1531 else if (source === null) { 1532 // Nothing to do when the data source is null 1533 return function () { }; 1534 } 1535 else if (typeof source === 'function') { 1536 return function (data, val, meta) { 1537 source(data, 'set', val, meta); 1538 }; 1539 } 1540 else if ( 1541 typeof source === 'string' && (source.indexOf('.') !== -1 || 1542 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1543 ) { 1544 // Like the get, we need to get data from a nested object 1545 var setData = function (data, val, src) { 1546 var a = _fnSplitObjNotation(src), b; 1547 var aLast = a[a.length - 1]; 1548 var arrayNotation, funcNotation, o, innerSrc; 1549 1550 for (var i = 0, iLen = a.length - 1; i < iLen; i++) { 1551 // Protect against prototype pollution 1552 if (a[i] === '__proto__' || a[i] === 'constructor') { 1553 throw new Error('Cannot set prototype values'); 1554 } 1555 1556 // Check if we are dealing with an array notation request 1557 arrayNotation = a[i].match(__reArray); 1558 funcNotation = a[i].match(__reFn); 1559 1560 if (arrayNotation) { 1561 a[i] = a[i].replace(__reArray, ''); 1562 data[a[i]] = []; 1563 1564 // Get the remainder of the nested object to set so we can recurse 1565 b = a.slice(); 1566 b.splice(0, i + 1); 1567 innerSrc = b.join('.'); 1568 1569 // Traverse each entry in the array setting the properties requested 1570 if (Array.isArray(val)) { 1571 for (var j = 0, jLen = val.length; j < jLen; j++) { 1572 o = {}; 1573 setData(o, val[j], innerSrc); 1574 data[a[i]].push(o); 1575 } 1576 } 1577 else { 1578 // We've been asked to save data to an array, but it 1579 // isn't array data to be saved. Best that can be done 1580 // is to just save the value. 1581 data[a[i]] = val; 1582 } 1583 1584 // The inner call to setData has already traversed through the remainder 1585 // of the source and has set the data, thus we can exit here 1586 return; 1587 } 1588 else if (funcNotation) { 1589 // Function call 1590 a[i] = a[i].replace(__reFn, ''); 1591 data = data[a[i]](val); 1592 } 1593 1594 // If the nested object doesn't currently exist - since we are 1595 // trying to set the value - create it 1596 if (data[a[i]] === null || data[a[i]] === undefined) { 1597 data[a[i]] = {}; 1598 } 1599 data = data[a[i]]; 1600 } 1601 1602 // Last item in the input - i.e, the actual set 1603 if (aLast.match(__reFn)) { 1604 // Function call 1605 data = data[aLast.replace(__reFn, '')](val); 1606 } 1607 else { 1608 // If array notation is used, we just want to strip it and use the property name 1609 // and assign the value. If it isn't used, then we get the result we want anyway 1610 data[aLast.replace(__reArray, '')] = val; 1611 } 1612 }; 1613 1614 return function (data, val) { // meta is also passed in, but not used 1615 return setData(data, val, source); 1616 }; 1617 } 1618 else { 1619 // Array or flat object mapping 1620 return function (data, val) { // meta is also passed in, but not used 1621 data[source] = val; 1622 }; 1623 } 1624 }, 1625 1626 /** 1627 * Create a function that will read nested objects from arrays, based on JSON notation 1628 * @param {*} source JSON notation string 1629 * @returns Value read 1630 */ 1631 get: function (source) { 1632 if ($.isPlainObject(source)) { 1633 // Build an object of get functions, and wrap them in a single call 1634 var o = {}; 1635 $.each(source, function (key, val) { 1636 if (val) { 1637 o[key] = DataTable.util.get(val); 1638 } 1639 }); 1640 1641 return function (data, type, row, meta) { 1642 var t = o[type] || o._; 1643 return t !== undefined ? 1644 t(data, type, row, meta) : 1645 data; 1646 }; 1647 } 1648 else if (source === null) { 1649 // Give an empty string for rendering / sorting etc 1650 return function (data) { // type, row and meta also passed, but not used 1651 return data; 1652 }; 1653 } 1654 else if (typeof source === 'function') { 1655 return function (data, type, row, meta) { 1656 return source(data, type, row, meta); 1657 }; 1658 } 1659 else if ( 1660 typeof source === 'string' && (source.indexOf('.') !== -1 || 1661 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1662 ) { 1663 /* If there is a . in the source string then the data source is in a 1664 * nested object so we loop over the data for each level to get the next 1665 * level down. On each loop we test for undefined, and if found immediately 1666 * return. This allows entire objects to be missing and sDefaultContent to 1667 * be used if defined, rather than throwing an error 1668 */ 1669 var fetchData = function (data, type, src) { 1670 var arrayNotation, funcNotation, out, innerSrc; 1671 1672 if (src !== "") { 1673 var a = _fnSplitObjNotation(src); 1674 1675 for (var i = 0, iLen = a.length; i < iLen; i++) { 1676 // Check if we are dealing with special notation 1677 arrayNotation = a[i].match(__reArray); 1678 funcNotation = a[i].match(__reFn); 1679 1680 if (arrayNotation) { 1681 // Array notation 1682 a[i] = a[i].replace(__reArray, ''); 1683 1684 // Condition allows simply [] to be passed in 1685 if (a[i] !== "") { 1686 data = data[a[i]]; 1687 } 1688 out = []; 1689 1690 // Get the remainder of the nested object to get 1691 a.splice(0, i + 1); 1692 innerSrc = a.join('.'); 1693 1694 // Traverse each entry in the array getting the properties requested 1695 if (Array.isArray(data)) { 1696 for (var j = 0, jLen = data.length; j < jLen; j++) { 1697 out.push(fetchData(data[j], type, innerSrc)); 1698 } 1699 } 1700 1701 // If a string is given in between the array notation indicators, that 1702 // is used to join the strings together, otherwise an array is returned 1703 var join = arrayNotation[0].substring(1, arrayNotation[0].length - 1); 1704 data = (join === "") ? out : out.join(join); 1705 1706 // The inner call to fetchData has already traversed through the remainder 1707 // of the source requested, so we exit from the loop 1708 break; 1709 } 1710 else if (funcNotation) { 1711 // Function call 1712 a[i] = a[i].replace(__reFn, ''); 1713 data = data[a[i]](); 1714 continue; 1715 } 1716 1717 if (data === null || data[a[i]] === null) { 1718 return null; 1719 } 1720 else if (data === undefined || data[a[i]] === undefined) { 1721 return undefined; 1722 } 1723 1724 data = data[a[i]]; 1725 } 1726 } 1727 1728 return data; 1729 }; 1730 1731 return function (data, type) { // row and meta also passed, but not used 1732 return fetchData(data, type, source); 1733 }; 1734 } 1735 else { 1736 // Array or flat object mapping 1737 return function (data) { // row and meta also passed, but not used 1738 return data[source]; 1739 }; 1740 } 1741 }, 1742 1743 stripHtml: function (mixed) { 1744 var type = typeof mixed; 1745 1746 if (type === 'function') { 1747 _stripHtml = mixed; 1748 return; 1749 } 1750 else if (type === 'string') { 1751 return _stripHtml(mixed); 1752 } 1753 return mixed; 1754 }, 1755 1756 escapeHtml: function (mixed) { 1757 var type = typeof mixed; 1758 1759 if (type === 'function') { 1760 _escapeHtml = mixed; 1761 return; 1762 } 1763 else if (type === 'string' || Array.isArray(mixed)) { 1764 return _escapeHtml(mixed); 1765 } 1766 return mixed; 1767 }, 1768 1769 unique: _unique 1770 }; 1771 1772 1773 1774 /** 1775 * Create a mapping object that allows camel case parameters to be looked up 1776 * for their Hungarian counterparts. The mapping is stored in a private 1777 * parameter called `_hungarianMap` which can be accessed on the source object. 1778 * @param {object} o 1779 * @memberof DataTable#oApi 1780 */ 1781 function _fnHungarianMap(o) { 1782 var 1783 hungarian = 'a aa ai ao as b fn i m o s ', 1784 match, 1785 newKey, 1786 map = {}; 1787 1788 $.each(o, function (key) { 1789 match = key.match(/^([^A-Z]+?)([A-Z])/); 1790 1791 if (match && hungarian.indexOf(match[1] + ' ') !== -1) { 1792 newKey = key.replace(match[0], match[2].toLowerCase()); 1793 map[newKey] = key; 1794 1795 if (match[1] === 'o') { 1796 _fnHungarianMap(o[key]); 1797 } 1798 } 1799 }); 1800 1801 o._hungarianMap = map; 1802 } 1803 1804 1805 /** 1806 * Convert from camel case parameters to Hungarian, based on a Hungarian map 1807 * created by _fnHungarianMap. 1808 * @param {object} src The model object which holds all parameters that can be 1809 * mapped. 1810 * @param {object} user The object to convert from camel case to Hungarian. 1811 * @param {boolean} force When set to `true`, properties which already have a 1812 * Hungarian value in the `user` object will be overwritten. Otherwise they 1813 * won't be. 1814 * @memberof DataTable#oApi 1815 */ 1816 function _fnCamelToHungarian(src, user, force) { 1817 if (!src._hungarianMap) { 1818 _fnHungarianMap(src); 1819 } 1820 1821 var hungarianKey; 1822 1823 $.each(user, function (key) { 1824 hungarianKey = src._hungarianMap[key]; 1825 1826 if (hungarianKey !== undefined && (force || user[hungarianKey] === undefined)) { 1827 // For objects, we need to buzz down into the object to copy parameters 1828 if (hungarianKey.charAt(0) === 'o') { 1829 // Copy the camelCase options over to the hungarian 1830 if (!user[hungarianKey]) { 1831 user[hungarianKey] = {}; 1832 } 1833 $.extend(true, user[hungarianKey], user[key]); 1834 1835 _fnCamelToHungarian(src[hungarianKey], user[hungarianKey], force); 1836 } 1837 else { 1838 user[hungarianKey] = user[key]; 1839 } 1840 } 1841 }); 1842 } 1843 1844 /** 1845 * Map one parameter onto another 1846 * @param {object} o Object to map 1847 * @param {*} knew The new parameter name 1848 * @param {*} old The old parameter name 1849 */ 1850 var _fnCompatMap = function (o, knew, old) { 1851 if (o[knew] !== undefined) { 1852 o[old] = o[knew]; 1853 } 1854 }; 1855 1856 1857 /** 1858 * Provide backwards compatibility for the main DT options. Note that the new 1859 * options are mapped onto the old parameters, so this is an external interface 1860 * change only. 1861 * @param {object} init Object to map 1862 */ 1863 function _fnCompatOpts(init) { 1864 _fnCompatMap(init, 'ordering', 'bSort'); 1865 _fnCompatMap(init, 'orderMulti', 'bSortMulti'); 1866 _fnCompatMap(init, 'orderClasses', 'bSortClasses'); 1867 _fnCompatMap(init, 'orderCellsTop', 'bSortCellsTop'); 1868 _fnCompatMap(init, 'order', 'aaSorting'); 1869 _fnCompatMap(init, 'orderFixed', 'aaSortingFixed'); 1870 _fnCompatMap(init, 'paging', 'bPaginate'); 1871 _fnCompatMap(init, 'pagingType', 'sPaginationType'); 1872 _fnCompatMap(init, 'pageLength', 'iDisplayLength'); 1873 _fnCompatMap(init, 'searching', 'bFilter'); 1874 1875 // Boolean initialisation of x-scrolling 1876 if (typeof init.sScrollX === 'boolean') { 1877 init.sScrollX = init.sScrollX ? '100%' : ''; 1878 } 1879 if (typeof init.scrollX === 'boolean') { 1880 init.scrollX = init.scrollX ? '100%' : ''; 1881 } 1882 1883 // Objects for ordering 1884 if (typeof init.bSort === 'object') { 1885 init.orderIndicators = init.bSort.indicators !== undefined ? init.bSort.indicators : true; 1886 init.orderHandler = init.bSort.handler !== undefined ? init.bSort.handler : true; 1887 init.bSort = true; 1888 } 1889 else if (init.bSort === false) { 1890 init.orderIndicators = false; 1891 init.orderHandler = false; 1892 } 1893 else if (init.bSort === true) { 1894 init.orderIndicators = true; 1895 init.orderHandler = true; 1896 } 1897 1898 // Which cells are the title cells? 1899 if (typeof init.bSortCellsTop === 'boolean') { 1900 init.titleRow = init.bSortCellsTop; 1901 } 1902 1903 // Column search objects are in an array, so it needs to be converted 1904 // element by element 1905 var searchCols = init.aoSearchCols; 1906 1907 if (searchCols) { 1908 for (var i = 0, iLen = searchCols.length; i < iLen; i++) { 1909 if (searchCols[i]) { 1910 _fnCamelToHungarian(DataTable.models.oSearch, searchCols[i]); 1911 } 1912 } 1913 } 1914 1915 // Enable search delay if server-side processing is enabled 1916 if (init.serverSide && !init.searchDelay) { 1917 init.searchDelay = 400; 1918 } 1919 } 1920 1921 1922 /** 1923 * Provide backwards compatibility for column options. Note that the new options 1924 * are mapped onto the old parameters, so this is an external interface change 1925 * only. 1926 * @param {object} init Object to map 1927 */ 1928 function _fnCompatCols(init) { 1929 _fnCompatMap(init, 'orderable', 'bSortable'); 1930 _fnCompatMap(init, 'orderData', 'aDataSort'); 1931 _fnCompatMap(init, 'orderSequence', 'asSorting'); 1932 _fnCompatMap(init, 'orderDataType', 'sortDataType'); 1933 1934 // orderData can be given as an integer 1935 var dataSort = init.aDataSort; 1936 if (typeof dataSort === 'number' && !Array.isArray(dataSort)) { 1937 init.aDataSort = [dataSort]; 1938 } 1939 } 1940 1941 1942 /** 1943 * Browser feature detection for capabilities, quirks 1944 * @param {object} settings dataTables settings object 1945 * @memberof DataTable#oApi 1946 */ 1947 function _fnBrowserDetect(settings) { 1948 // We don't need to do this every time DataTables is constructed, the values 1949 // calculated are specific to the browser and OS configuration which we 1950 // don't expect to change between initialisations 1951 if (!DataTable.__browser) { 1952 var browser = {}; 1953 DataTable.__browser = browser; 1954 1955 // Scrolling feature / quirks detection 1956 var n = $('<div/>') 1957 .css({ 1958 position: 'fixed', 1959 top: 0, 1960 left: -1 * window.pageXOffset, // allow for scrolling 1961 height: 1, 1962 width: 1, 1963 overflow: 'hidden' 1964 }) 1965 .append( 1966 $('<div/>') 1967 .css({ 1968 position: 'absolute', 1969 top: 1, 1970 left: 1, 1971 width: 100, 1972 overflow: 'scroll' 1973 }) 1974 .append( 1975 $('<div/>') 1976 .css({ 1977 width: '100%', 1978 height: 10 1979 }) 1980 ) 1981 ) 1982 .appendTo('body'); 1983 1984 var outer = n.children(); 1985 var inner = outer.children(); 1986 1987 // Get scrollbar width 1988 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; 1989 1990 // In rtl text layout, some browsers (most, but not all) will place the 1991 // scrollbar on the left, rather than the right. 1992 browser.bScrollbarLeft = Math.round(inner.offset().left) !== 1; 1993 1994 n.remove(); 1995 } 1996 1997 $.extend(settings.oBrowser, DataTable.__browser); 1998 settings.oScroll.iBarWidth = DataTable.__browser.barWidth; 1999 } 2000 2001 /** 2002 * Add a column to the list used for the table with default values 2003 * @param {object} oSettings dataTables settings object 2004 * @memberof DataTable#oApi 2005 */ 2006 function _fnAddColumn(oSettings) { 2007 // Add column to aoColumns array 2008 var oDefaults = DataTable.defaults.column; 2009 var iCol = oSettings.aoColumns.length; 2010 var oCol = $.extend({}, DataTable.models.oColumn, oDefaults, { 2011 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], 2012 "mData": oDefaults.mData ? oDefaults.mData : iCol, 2013 idx: iCol, 2014 searchFixed: {}, 2015 colEl: $('<col>').attr('data-dt-column', iCol) 2016 }); 2017 oSettings.aoColumns.push(oCol); 2018 2019 // Add search object for column specific search. Note that the `searchCols[ iCol ]` 2020 // passed into extend can be undefined. This allows the user to give a default 2021 // with only some of the parameters defined, and also not give a default 2022 var searchCols = oSettings.aoPreSearchCols; 2023 searchCols[iCol] = $.extend({}, DataTable.models.oSearch, searchCols[iCol]); 2024 } 2025 2026 2027 /** 2028 * Apply options for a column 2029 * @param {object} oSettings dataTables settings object 2030 * @param {int} iCol column index to consider 2031 * @param {object} oOptions object with sType, bVisible and bSearchable etc 2032 * @memberof DataTable#oApi 2033 */ 2034 function _fnColumnOptions(oSettings, iCol, oOptions) { 2035 var oCol = oSettings.aoColumns[iCol]; 2036 2037 /* User specified column options */ 2038 if (oOptions !== undefined && oOptions !== null) { 2039 // Backwards compatibility 2040 _fnCompatCols(oOptions); 2041 2042 // Map camel case parameters to their Hungarian counterparts 2043 _fnCamelToHungarian(DataTable.defaults.column, oOptions, true); 2044 2045 /* Backwards compatibility for mDataProp */ 2046 if (oOptions.mDataProp !== undefined && !oOptions.mData) { 2047 oOptions.mData = oOptions.mDataProp; 2048 } 2049 2050 if (oOptions.sType) { 2051 oCol._sManualType = oOptions.sType; 2052 } 2053 2054 // `class` is a reserved word in JavaScript, so we need to provide 2055 // the ability to use a valid name for the camel case input 2056 if (oOptions.className && !oOptions.sClass) { 2057 oOptions.sClass = oOptions.className; 2058 } 2059 2060 var origClass = oCol.sClass; 2061 2062 $.extend(oCol, oOptions); 2063 _fnMap(oCol, oOptions, "sWidth", "sWidthOrig"); 2064 2065 // Merge class from previously defined classes with this one, rather than just 2066 // overwriting it in the extend above 2067 if (origClass !== oCol.sClass) { 2068 oCol.sClass = origClass + ' ' + oCol.sClass; 2069 } 2070 2071 /* iDataSort to be applied (backwards compatibility), but aDataSort will take 2072 * priority if defined 2073 */ 2074 if (oOptions.iDataSort !== undefined) { 2075 oCol.aDataSort = [oOptions.iDataSort]; 2076 } 2077 _fnMap(oCol, oOptions, "aDataSort"); 2078 } 2079 2080 /* Cache the data get and set functions for speed */ 2081 var mDataSrc = oCol.mData; 2082 var mData = _fnGetObjectDataFn(mDataSrc); 2083 2084 // The `render` option can be given as an array to access the helper rendering methods. 2085 // The first element is the rendering method to use, the rest are the parameters to pass 2086 if (oCol.mRender && Array.isArray(oCol.mRender)) { 2087 var copy = oCol.mRender.slice(); 2088 var name = copy.shift(); 2089 2090 oCol.mRender = DataTable.render[name].apply(window, copy); 2091 } 2092 2093 oCol._render = oCol.mRender ? _fnGetObjectDataFn(oCol.mRender) : null; 2094 2095 var attrTest = function (src) { 2096 return typeof src === 'string' && src.indexOf('@') !== -1; 2097 }; 2098 oCol._bAttrSrc = $.isPlainObject(mDataSrc) && ( 2099 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) 2100 ); 2101 oCol._setter = null; 2102 2103 oCol.fnGetData = function (rowData, type, meta) { 2104 var innerData = mData(rowData, type, undefined, meta); 2105 2106 return oCol._render && type ? 2107 oCol._render(innerData, type, rowData, meta) : 2108 innerData; 2109 }; 2110 oCol.fnSetData = function (rowData, val, meta) { 2111 return _fnSetObjectDataFn(mDataSrc)(rowData, val, meta); 2112 }; 2113 2114 // Indicate if DataTables should read DOM data as an object or array 2115 // Used in _fnGetRowElements 2116 if (typeof mDataSrc !== 'number' && !oCol._isArrayHost) { 2117 oSettings._rowReadObject = true; 2118 } 2119 2120 /* Feature sorting overrides column specific when off */ 2121 if (!oSettings.oFeatures.bSort) { 2122 oCol.bSortable = false;
2123 } 2124 } 2125 2126 2127 /** 2128 * Adjust the table column widths for new data. Note: you would probably want to 2129 * do a redraw after calling this function! 2130 * @param {object} settings dataTables settings object 2131 * @memberof DataTable#oApi 2132 */ 2133 function _fnAdjustColumnSizing(settings) { 2134 _fnCalculateColumnWidths(settings); 2135 _fnColumnSizes(settings); 2136 2137 var scroll = settings.oScroll; 2138 if (scroll.sY !== '' || scroll.sX !== '') { 2139 _fnScrollDraw(settings); 2140 } 2141 2142 _fnCallbackFire(settings, null, 'column-sizing', [settings]); 2143 } 2144 2145 /** 2146 * Apply column sizes 2147 * 2148 * @param {*} settings DataTables settings object 2149 */ 2150 function _fnColumnSizes(settings) { 2151 var cols = settings.aoColumns; 2152 2153 for (var i = 0; i < cols.length; i++) { 2154 var width = _fnColumnsSumWidth(settings, [i], false, false); 2155 2156 cols[i].colEl.css('width', width); 2157 2158 if (settings.oScroll.sX) { 2159 cols[i].colEl.css('min-width', width); 2160 } 2161 } 2162 } 2163 2164 2165 /** 2166 * Convert the index of a visible column to the index in the data array (take account 2167 * of hidden columns) 2168 * @param {object} oSettings dataTables settings object 2169 * @param {int} iMatch Visible column index to lookup 2170 * @returns {int} i the data index 2171 * @memberof DataTable#oApi 2172 */ 2173 function _fnVisibleToColumnIndex(oSettings, iMatch) { 2174 var aiVis = _fnGetColumns(oSettings, 'bVisible'); 2175 2176 return typeof aiVis[iMatch] === 'number' ? 2177 aiVis[iMatch] : 2178 null; 2179 } 2180 2181 2182 /** 2183 * Convert the index of an index in the data array and convert it to the visible 2184 * column index (take account of hidden columns) 2185 * @param {int} iMatch Column index to lookup 2186 * @param {object} oSettings dataTables settings object 2187 * @returns {int} i the data index 2188 * @memberof DataTable#oApi 2189 */ 2190 function _fnColumnIndexToVisible(oSettings, iMatch) { 2191 var aiVis = _fnGetColumns(oSettings, 'bVisible'); 2192 var iPos = aiVis.indexOf(iMatch); 2193 2194 return iPos !== -1 ? iPos : null; 2195 } 2196 2197 2198 /** 2199 * Get the number of visible columns 2200 * @param {object} oSettings dataTables settings object 2201 * @returns {int} i the number of visible columns 2202 * @memberof DataTable#oApi 2203 */ 2204 function _fnVisibleColumns(settings) { 2205 var layout = settings.aoHeader; 2206 var columns = settings.aoColumns; 2207 var vis = 0; 2208 2209 if (layout.length) { 2210 for (var i = 0, iLen = layout[0].length; i < iLen; i++) { 2211 if (columns[i].bVisible && $(layout[0][i].cell).css('display') !== 'none') { 2212 vis++; 2213 } 2214 } 2215 } 2216 2217 return vis; 2218 } 2219 2220 2221 /** 2222 * Get an array of column indexes that match a given property 2223 * @param {object} oSettings dataTables settings object 2224 * @param {string} sParam Parameter in aoColumns to look for - typically 2225 * bVisible or bSearchable 2226 * @returns {array} Array of indexes with matched properties 2227 * @memberof DataTable#oApi 2228 */ 2229 function _fnGetColumns(oSettings, sParam) { 2230 var a = []; 2231 2232 oSettings.aoColumns.map(function (val, i) { 2233 if (val[sParam]) { 2234 a.push(i); 2235 } 2236 }); 2237 2238 return a; 2239 } 2240 2241 /** 2242 * Allow the result from a type detection function to be `true` while 2243 * translating that into a string. Old type detection functions will 2244 * return the type name if it passes. An object store would be better, 2245 * but not backwards compatible. 2246 * 2247 * @param {*} typeDetect Object or function for type detection 2248 * @param {*} res Result from the type detection function 2249 * @returns Type name or false 2250 */ 2251 function _typeResult(typeDetect, res) { 2252 return res === true 2253 ? typeDetect._name 2254 : res; 2255 } 2256 2257 /** 2258 * Calculate the 'type' of a column 2259 * @param {object} settings dataTables settings object 2260 * @memberof DataTable#oApi 2261 */ 2262 function _fnColumnTypes(settings) { 2263 var columns = settings.aoColumns; 2264 var data = settings.aoData; 2265 var types = DataTable.ext.type.detect; 2266 var i, iLen, j, jen, k, ken; 2267 var col, detectedType, cache; 2268 2269 // For each column, spin over the data type detection functions, seeing if one matches 2270 for (i = 0, iLen = columns.length; i < iLen; 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 // With SSP type detection can be unreliable and error prone, so we provide a way 2279 // to turn it off. 2280 if (!settings.typeDetect) { 2281 return; 2282 } 2283 2284 for (j = 0, jen = types.length; j < jen; j++) { 2285 var typeDetect = types[j]; 2286 2287 // There can be either one, or three type detection functions 2288 var oneOf = typeDetect.oneOf; 2289 var allOf = typeDetect.allOf || typeDetect; 2290 var init = typeDetect.init; 2291 var one = false; 2292 2293 detectedType = null; 2294 2295 // Fast detect based on column assignment 2296 if (init) { 2297 detectedType = _typeResult(typeDetect, init(settings, col, i)); 2298 2299 if (detectedType) { 2300 col.sType = detectedType; 2301 break; 2302 } 2303 } 2304 2305 for (k = 0, ken = data.length; k < ken; k++) { 2306 if (!data[k]) { 2307 continue; 2308 } 2309 2310 // Use a cache array so we only need to get the type data 2311 // from the formatter once (when using multiple detectors) 2312 if (cache[k] === undefined) { 2313 cache[k] = _fnGetCellData(settings, k, i, 'type'); 2314 } 2315 2316 // Only one data point in the column needs to match this function 2317 if (oneOf && !one) { 2318 one = _typeResult(typeDetect, oneOf(cache[k], settings)); 2319 } 2320 2321 // All data points need to match this function 2322 detectedType = _typeResult(typeDetect, allOf(cache[k], settings)); 2323 2324 // If null, then this type can't apply to this column, so 2325 // rather than testing all cells, break out. There is an 2326 // exception for the last type which is `html`. We need to 2327 // scan all rows since it is possible to mix string and HTML 2328 // types 2329 if (!detectedType && j !== types.length - 3) { 2330 break; 2331 } 2332 2333 // Only a single match is needed for html type since it is 2334 // bottom of the pile and very similar to string - but it 2335 // must not be empty 2336 if (detectedType === 'html' && !_empty(cache[k])) { 2337 break; 2338 } 2339 } 2340 2341 // Type is valid for all data points in the column - use this 2342 // type 2343 if ((oneOf && one && detectedType) || (!oneOf && detectedType)) { 2344 col.sType = detectedType; 2345 break; 2346 } 2347 } 2348 2349 // Fall back - if no type was detected, always use string 2350 if (!col.sType) { 2351 col.sType = 'string'; 2352 } 2353 } 2354 2355 // Set class names for header / footer for auto type classes 2356 var autoClass = _ext.type.className[col.sType]; 2357 2358 if (autoClass) { 2359 _columnAutoClass(settings.aoHeader, i, autoClass); 2360 _columnAutoClass(settings.aoFooter, i, autoClass); 2361 } 2362 2363 var renderer = _ext.type.render[col.sType]; 2364 2365 // This can only happen once! There is no way to remove 2366 // a renderer. After the first time the renderer has 2367 // already been set so createTr will run the renderer itself. 2368 if (renderer && !col._render) { 2369 col._render = DataTable.util.get(renderer); 2370 2371 _columnAutoRender(settings, i); 2372 } 2373 } 2374 } 2375 2376 /** 2377 * Apply an auto detected renderer to data which doesn't yet have 2378 * a renderer 2379 */ 2380 function _columnAutoRender(settings, colIdx) { 2381 var data = settings.aoData; 2382 2383 for (var i = 0; i < data.length; i++) { 2384 if (data[i].nTr) { 2385 // We have to update the display here since there is no 2386 // invalidation check for the data 2387 var display = _fnGetCellData(settings, i, colIdx, 'display'); 2388 2389 data[i].displayData[colIdx] = display; 2390 _fnWriteCell(data[i].anCells[colIdx], display); 2391 2392 // No need to update sort / filter data since it has 2393 // been invalidated and will be re-read with the 2394 // renderer now applied 2395 } 2396 } 2397 } 2398 2399 /** 2400 * Apply a class name to a column's header cells 2401 */ 2402 function _columnAutoClass(container, colIdx, className) { 2403 container.forEach(function (row) { 2404 if (row[colIdx] && row[colIdx].unique) { 2405 _addClass(row[colIdx].cell, className); 2406 } 2407 }); 2408 } 2409 2410 /** 2411 * Take the column definitions and static columns arrays and calculate how 2412 * they relate to column indexes. The callback function will then apply the 2413 * definition found for a column to a suitable configuration object. 2414 * @param {object} oSettings dataTables settings object 2415 * @param {array} aoColDefs The aoColumnDefs array that is to be applied 2416 * @param {array} aoCols The aoColumns array that defines columns individually 2417 * @param {array} headerLayout Layout for header as it was loaded 2418 * @param {function} fn Callback function - takes two parameters, the calculated 2419 * column index and the definition for that column. 2420 * @memberof DataTable#oApi 2421 */ 2422 function _fnApplyColumnDefs(oSettings, aoColDefs, aoCols, headerLayout, fn) { 2423 var i, iLen, j, jLen, k, kLen, def; 2424 var columns = oSettings.aoColumns; 2425 2426 if (aoCols) { 2427 for (i = 0, iLen = aoCols.length; i < iLen; i++) { 2428 if (aoCols[i] && aoCols[i].name) { 2429 columns[i].sName = aoCols[i].name; 2430 } 2431 } 2432 } 2433 2434 // Column definitions with aTargets 2435 if (aoColDefs) { 2436 /* Loop over the definitions array - loop in reverse so first instance has priority */ 2437 for (i = aoColDefs.length - 1; i >= 0; i--) { 2438 def = aoColDefs[i]; 2439 2440 /* Each definition can target multiple columns, as it is an array */ 2441 var aTargets = def.target !== undefined 2442 ? def.target 2443 : def.targets !== undefined 2444 ? def.targets 2445 : def.aTargets; 2446 2447 if (!Array.isArray(aTargets)) { 2448 aTargets = [aTargets]; 2449 } 2450 2451 for (j = 0, jLen = aTargets.length; j < jLen; j++) { 2452 var target = aTargets[j]; 2453 2454 if (typeof target === 'number' && target >= 0) { 2455 /* Add columns that we don't yet know about */ 2456 while (columns.length <= target) { 2457 _fnAddColumn(oSettings); 2458 } 2459
2460 /* Integer, basic index */ 2461 fn(target, def); 2462 } 2463 else if (typeof target === 'number' && target < 0) { 2464 /* Negative integer, right to left column counting */ 2465 fn(columns.length + target, def); 2466 } 2467 else if (typeof target === 'string') { 2468 for (k = 0, kLen = columns.length; k < kLen; k++) { 2469 if (target === '_all') { 2470 // Apply to all columns 2471 fn(k, def); 2472 } 2473 else if (target.indexOf(':name') !== -1) { 2474 // Column selector 2475 if (columns[k].sName === target.replace(':name', '')) { 2476 fn(k, def); 2477 } 2478 } 2479 else { 2480 // Cell selector 2481 headerLayout.forEach(function (row) { 2482 if (row[k]) { 2483 var cell = $(row[k].cell); 2484 2485 // Legacy support. Note that it means that we don't support 2486 // an element name selector only, since they are treated as 2487 // class names for 1.x compat. 2488 if (target.match(/^[a-z][\w-]*$/i)) { 2489 target = '.' + target; 2490 } 2491 2492 if (cell.is(target)) { 2493 fn(k, def); 2494 } 2495 } 2496 }); 2497 } 2498 } 2499 } 2500 } 2501 } 2502 } 2503 2504 // Statically defined columns array 2505 if (aoCols) { 2506 for (i = 0, iLen = aoCols.length; i < iLen; i++) { 2507 fn(i, aoCols[i]); 2508 } 2509 } 2510 } 2511 2512 2513 /** 2514 * Get the width for a given set of columns 2515 * 2516 * @param {*} settings DataTables settings object 2517 * @param {*} targets Columns - comma separated string or array of numbers 2518 * @param {*} original Use the original width (true) or calculated (false) 2519 * @param {*} incVisible Include visible columns (true) or not (false) 2520 * @returns Combined CSS value 2521 */ 2522 function _fnColumnsSumWidth(settings, targets, original, incVisible) { 2523 if (!Array.isArray(targets)) { 2524 targets = _fnColumnsFromHeader(targets); 2525 } 2526 2527 var sum = 0; 2528 var unit; 2529 var columns = settings.aoColumns; 2530 2531 for (var i = 0, iLen = targets.length; i < iLen; i++) { 2532 var column = columns[targets[i]]; 2533 var definedWidth = original ? 2534 column.sWidthOrig : 2535 column.sWidth; 2536 2537 if (!incVisible && column.bVisible === false) { 2538 continue; 2539 } 2540 2541 if (definedWidth === null || definedWidth === undefined) { 2542 return null; // can't determine a defined width - browser defined 2543 } 2544 else if (typeof definedWidth === 'number') { 2545 unit = 'px'; 2546 sum += definedWidth; 2547 } 2548 else { 2549 var matched = definedWidth.match(/([\d\.]+)([^\d]*)/); 2550 2551 if (matched) { 2552 sum += matched[1] * 1; 2553 unit = matched.length === 3 ? 2554 matched[2] : 2555 'px'; 2556 } 2557 } 2558 } 2559 2560 return sum + unit; 2561 } 2562 2563 function _fnColumnsFromHeader(cell) { 2564 var attr = $(cell).closest('[data-dt-column]').attr('data-dt-column'); 2565 2566 if (!attr) { 2567 return []; 2568 } 2569 2570 return attr.split(',').map(function (val) { 2571 return val * 1; 2572 }); 2573 } 2574 /** 2575 * Add a data array to the table, creating DOM node etc. This is the parallel to 2576 * _fnGatherData, but for adding rows from a JavaScript source, rather than a 2577 * DOM source. 2578 * @param {object} settings dataTables settings object 2579 * @param {array} data data array to be added 2580 * @param {node} [tr] TR element to add to the table - optional. If not given, 2581 * DataTables will create a row automatically 2582 * @param {array} [tds] Array of TD|TH elements for the row - must be given 2583 * if nTr is. 2584 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed 2585 * @memberof DataTable#oApi 2586 */ 2587 function _fnAddData(settings, dataIn, tr, tds) { 2588 /* Create the object for storing information about this new row */ 2589 var rowIdx = settings.aoData.length; 2590 var rowModel = $.extend(true, {}, DataTable.models.oRow, { 2591 src: tr ? 'dom' : 'data', 2592 idx: rowIdx 2593 }); 2594 2595 rowModel._aData = dataIn; 2596 settings.aoData.push(rowModel); 2597 2598 var columns = settings.aoColumns; 2599 2600 for (var i = 0, iLen = columns.length; i < iLen; i++) { 2601 // Invalidate the column types as the new data needs to be revalidated 2602 columns[i].sType = null; 2603 } 2604 2605 /* Add to the display array */ 2606 settings.aiDisplayMaster.push(rowIdx); 2607 2608 var id = settings.rowIdFn(dataIn); 2609 if (id !== undefined) { 2610 settings.aIds[id] = rowModel; 2611 } 2612 2613 /* Create the DOM information, or register it if already present */
vendor: 11,588 bytes, lines 2614-2952
2614 if (tr || !settings.oFeatures.bDeferRender) { 2615 _fnCreateTr(settings, rowIdx, tr, tds); 2616 } 2617 2618 return rowIdx; 2619 } 2620 2621 2622 /** 2623 * Add one or more TR elements to the table. Generally we'd expect to 2624 * use this for reading data from a DOM sourced table, but it could be 2625 * used for an TR element. Note that if a TR is given, it is used (i.e. 2626 * it is not cloned). 2627 * @param {object} settings dataTables settings object 2628 * @param {array|node|jQuery} trs The TR element(s) to add to the table 2629 * @returns {array} Array of indexes for the added rows 2630 * @memberof DataTable#oApi 2631 */ 2632 function _fnAddTr(settings, trs) { 2633 var row; 2634 2635 // Allow an individual node to be passed in 2636 if (!(trs instanceof $)) { 2637 trs = $(trs); 2638 } 2639 2640 return trs.map(function (i, el) { 2641 row = _fnGetRowElements(settings, el); 2642 return _fnAddData(settings, row.data, el, row.cells); 2643 }); 2644 } 2645 2646 2647 /** 2648 * Get the data for a given cell from the internal cache, taking into account data mapping 2649 * @param {object} settings dataTables settings object 2650 * @param {int} rowIdx aoData row id 2651 * @param {int} colIdx Column index 2652 * @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order') 2653 * @returns {*} Cell data 2654 * @memberof DataTable#oApi 2655 */ 2656 function _fnGetCellData(settings, rowIdx, colIdx, type) { 2657 if (type === 'search') { 2658 type = 'filter'; 2659 } 2660 else if (type === 'order') { 2661 type = 'sort'; 2662 } 2663 2664 var row = settings.aoData[rowIdx]; 2665 2666 if (!row) { 2667 return undefined; 2668 } 2669 2670 var draw = settings.iDraw; 2671 var col = settings.aoColumns[colIdx]; 2672 var rowData = row._aData; 2673 var defaultContent = col.sDefaultContent; 2674 var cellData = col.fnGetData(rowData, type, { 2675 settings: settings, 2676 row: rowIdx, 2677 col: colIdx 2678 }); 2679 2680 // Allow for a node being returned for non-display types 2681 if (type !== 'display' && cellData && typeof cellData === 'object' && cellData.nodeName) { 2682 cellData = cellData.innerHTML; 2683 } 2684 2685 if (cellData === undefined) { 2686 if (settings.iDrawError != draw && defaultContent === null) { 2687 _fnLog(settings, 0, "Requested unknown parameter " + 2688 (typeof col.mData == 'function' ? '{function}' : "'" + col.mData + "'") + 2689 " for row " + rowIdx + ", column " + colIdx, 4); 2690 settings.iDrawError = draw; 2691 } 2692 return defaultContent; 2693 } 2694 2695 // When the data source is null and a specific data type is requested (i.e. 2696 // not the original data), we can use default column data 2697 if ((cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined) { 2698 cellData = defaultContent; 2699 } 2700 else if (typeof cellData === 'function') { 2701 // If the data source is a function, then we run it and use the return, 2702 // executing in the scope of the data object (for instances) 2703 return cellData.call(rowData); 2704 } 2705 2706 if (cellData === null && type === 'display') { 2707 return ''; 2708 } 2709 2710 if (type === 'filter') { 2711 var formatters = DataTable.ext.type.search; 2712 2713 if (formatters[col.sType]) { 2714 cellData = formatters[col.sType](cellData); 2715 } 2716 } 2717 2718 return cellData; 2719 } 2720 2721 2722 /** 2723 * Set the value for a specific cell, into the internal data cache 2724 * @param {object} settings dataTables settings object 2725 * @param {int} rowIdx aoData row id 2726 * @param {int} colIdx Column index 2727 * @param {*} val Value to set 2728 * @memberof DataTable#oApi 2729 */ 2730 function _fnSetCellData(settings, rowIdx, colIdx, val) { 2731 var col = settings.aoColumns[colIdx]; 2732 var rowData = settings.aoData[rowIdx]._aData; 2733 2734 col.fnSetData(rowData, val, { 2735 settings: settings, 2736 row: rowIdx, 2737 col: colIdx 2738 }); 2739 } 2740 2741 /** 2742 * Write a value to a cell 2743 * @param {*} td Cell 2744 * @param {*} val Value 2745 */ 2746 function _fnWriteCell(td, val) { 2747 if (val && typeof val === 'object' && val.nodeName) { 2748 $(td) 2749 .empty() 2750 .append(val); 2751 } 2752 else { 2753 td.innerHTML = val; 2754 } 2755 } 2756 2757 2758 // Private variable that is used to match action syntax in the data property object 2759 var __reArray = /\[.*?\]$/; 2760 var __reFn = /\(\)$/; 2761 2762 /** 2763 * Split string on periods, taking into account escaped periods 2764 * @param {string} str String to split 2765 * @return {array} Split string 2766 */ 2767 function _fnSplitObjNotation(str) { 2768 var parts = str.match(/(\\.|[^.])+/g) || ['']; 2769 2770 return parts.map(function (s) { 2771 return s.replace(/\\\./g, '.'); 2772 }); 2773 } 2774 2775 2776 /** 2777 * Return a function that can be used to get data from a source object, taking 2778 * into account the ability to use nested objects as a source 2779 * @param {string|int|function} mSource The data source for the object 2780 * @returns {function} Data get function 2781 * @memberof DataTable#oApi 2782 */ 2783 var _fnGetObjectDataFn = DataTable.util.get; 2784 2785 2786 /** 2787 * Return a function that can be used to set data from a source object, taking 2788 * into account the ability to use nested objects as a source 2789 * @param {string|int|function} mSource The data source for the object 2790 * @returns {function} Data set function 2791 * @memberof DataTable#oApi 2792 */ 2793 var _fnSetObjectDataFn = DataTable.util.set; 2794 2795 2796 /** 2797 * Return an array with the full table data 2798 * @param {object} oSettings dataTables settings object 2799 * @returns array {array} aData Master data array 2800 * @memberof DataTable#oApi 2801 */ 2802 function _fnGetDataMaster(settings) { 2803 return _pluck(settings.aoData, '_aData'); 2804 } 2805 2806 2807 /** 2808 * Nuke the table 2809 * @param {object} oSettings dataTables settings object 2810 * @memberof DataTable#oApi 2811 */ 2812 function _fnClearTable(settings) { 2813 settings.aoData.length = 0; 2814 settings.aiDisplayMaster.length = 0; 2815 settings.aiDisplay.length = 0; 2816 settings.aIds = {}; 2817 } 2818 2819 2820 /** 2821 * Mark cached data as invalid such that a re-read of the data will occur when 2822 * the cached data is next requested. Also update from the data source object. 2823 * 2824 * @param {object} settings DataTables settings object 2825 * @param {int} rowIdx Row index to invalidate 2826 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom' 2827 * or 'data' 2828 * @param {int} [colIdx] Column index to invalidate. If undefined the whole 2829 * row will be invalidated 2830 * @memberof DataTable#oApi 2831 * 2832 * @todo For the modularisation of v1.11 this will need to become a callback, so 2833 * the sort and filter methods can subscribe to it. That will required 2834 * initialisation options for sorting, which is why it is not already baked in 2835 */ 2836 function _fnInvalidate(settings, rowIdx, src, colIdx) { 2837 var row = settings.aoData[rowIdx]; 2838 var i, iLen; 2839 2840 // Remove the cached data for the row 2841 row._aSortData = null; 2842 row._aFilterData = null; 2843 row.displayData = null; 2844 2845 // Are we reading last data from DOM or the data object? 2846 if (src === 'dom' || ((!src || src === 'auto') && row.src === 'dom')) { 2847 // Read the data from the DOM 2848 row._aData = _fnGetRowElements( 2849 settings, row, colIdx, colIdx === undefined ? undefined : row._aData 2850 ) 2851 .data; 2852 } 2853 else { 2854 // Reading from data object, update the DOM 2855 var cells = row.anCells; 2856 var display = _fnGetRowDisplay(settings, rowIdx); 2857 2858 if (cells) { 2859 if (colIdx !== undefined) { 2860 _fnWriteCell(cells[colIdx], display[colIdx]); 2861 } 2862 else { 2863 for (i = 0, iLen = cells.length; i < iLen; i++) { 2864 _fnWriteCell(cells[i], display[i]); 2865 } 2866 } 2867 } 2868 } 2869 2870 // Column specific invalidation 2871 var cols = settings.aoColumns; 2872 if (colIdx !== undefined) { 2873 // Type - the data might have changed 2874 cols[colIdx].sType = null; 2875 2876 // Max length string. Its a fairly cheep recalculation, so not worth 2877 // something more complicated 2878 cols[colIdx].maxLenString = null; 2879 } 2880 else { 2881 for (i = 0, iLen = cols.length; i < iLen; i++) { 2882 cols[i].sType = null; 2883 cols[i].maxLenString = null; 2884 } 2885 2886 // Update DataTables special `DT_*` attributes for the row 2887 _fnRowAttributes(settings, row); 2888 } 2889 } 2890 2891 2892 /** 2893 * Build a data source object from an HTML row, reading the contents of the 2894 * cells that are in the row. 2895 * 2896 * @param {object} settings DataTables settings object 2897 * @param {node|object} TR element from which to read data or existing row 2898 * object from which to re-read the data from the cells 2899 * @param {int} [colIdx] Optional column index 2900 * @param {array|object} [d] Data source object. If `colIdx` is given then this 2901 * parameter should also be given and will be used to write the data into. 2902 * Only the column in question will be written 2903 * @returns {object} Object with two parameters: `data` the data read, in 2904 * document order, and `cells` and array of nodes (they can be useful to the 2905 * caller, so rather than needing a second traversal to get them, just return 2906 * them from here). 2907 * @memberof DataTable#oApi 2908 */ 2909 function _fnGetRowElements(settings, row, colIdx, d) { 2910 var 2911 tds = [], 2912 td = row.firstChild, 2913 name, col, i = 0, contents, 2914 columns = settings.aoColumns, 2915 objectRead = settings._rowReadObject; 2916 2917 // Allow the data object to be passed in, or construct 2918 d = d !== undefined ? 2919 d : 2920 objectRead ? 2921 {} : 2922 []; 2923 2924 var attr = function (str, td) { 2925 if (typeof str === 'string') { 2926 var idx = str.indexOf('@'); 2927 2928 if (idx !== -1) { 2929 var attr = str.substring(idx + 1); 2930 var setter = _fnSetObjectDataFn(str); 2931 setter(d, td.getAttribute(attr)); 2932 } 2933 } 2934 }; 2935 2936 // Read data from a cell and store into the data object 2937 var cellProcess = function (cell) { 2938 if (colIdx === undefined || colIdx === i) { 2939 col = columns[i]; 2940 contents = (cell.innerHTML).trim(); 2941 2942 if (col && col._bAttrSrc) { 2943 var setter = _fnSetObjectDataFn(col.mData._); 2944 setter(d, contents); 2945 2946 attr(col.mData.sort, cell); 2947 attr(col.mData.type, cell); 2948 attr(col.mData.filter, cell); 2949 } 2950 else { 2951 // Depending on the `data` option for the columns the data can 2952 // be read to either an object or an array.
2953 if (objectRead) { 2954 if (!col._setter) { 2955 // Cache the setter function 2956 col._setter = _fnSetObjectDataFn(col.mData); 2957 } 2958 col._setter(d, contents); 2959 } 2960 else { 2961 d[i] = contents; 2962 } 2963 } 2964 } 2965 2966 i++; 2967 }; 2968 2969 if (td) { 2970 // `tr` element was passed in 2971 while (td) { 2972 name = td.nodeName.toUpperCase(); 2973 2974 if (name == "TD" || name == "TH") { 2975 cellProcess(td); 2976 tds.push(td); 2977 } 2978 2979 td = td.nextSibling; 2980 } 2981 } 2982 else { 2983 // Existing row object passed in 2984 tds = row.anCells; 2985 2986 for (var j = 0, jen = tds.length; j < jen; j++) { 2987 cellProcess(tds[j]); 2988 } 2989 } 2990 2991 // Read the ID from the DOM if present 2992 var rowNode = row.firstChild ? row : row.nTr; 2993 2994 if (rowNode) { 2995 var id = rowNode.getAttribute('id'); 2996 2997 if (id) { 2998 _fnSetObjectDataFn(settings.rowId)(d, id); 2999 } 3000 } 3001 3002 return { 3003 data: d, 3004 cells: tds 3005 }; 3006 } 3007 3008 /** 3009 * Render and cache a row's display data for the columns, if required 3010 * @returns 3011 */ 3012 function _fnGetRowDisplay(settings, rowIdx) { 3013 var rowModal = settings.aoData[rowIdx]; 3014 var columns = settings.aoColumns; 3015 3016 if (!rowModal.displayData) { 3017 // Need to render and cache 3018 rowModal.displayData = []; 3019 3020 for (var colIdx = 0, len = columns.length; colIdx < len; colIdx++) { 3021 rowModal.displayData.push( 3022 _fnGetCellData(settings, rowIdx, colIdx, 'display') 3023 ); 3024 } 3025 } 3026 3027 return rowModal.displayData; 3028 } 3029 3030 /** 3031 * Create a new TR element (and it's TD children) for a row 3032 * @param {object} oSettings dataTables settings object 3033 * @param {int} iRow Row to consider 3034 * @param {node} [nTrIn] TR element to add to the table - optional. If not given, 3035 * DataTables will create a row automatically 3036 * @param {array} [anTds] Array of TD|TH elements for the row - must be given 3037 * if nTr is. 3038 * @memberof DataTable#oApi 3039 */ 3040 function _fnCreateTr(oSettings, iRow, nTrIn, anTds) { 3041 var 3042 row = oSettings.aoData[iRow], 3043 rowData = row._aData, 3044 cells = [], 3045 nTr, nTd, oCol, 3046 i, iLen, create, 3047 trClass = oSettings.oClasses.tbody.row; 3048 3049 if (row.nTr === null) { 3050 nTr = nTrIn || document.createElement('tr'); 3051 3052 row.nTr = nTr; 3053 row.anCells = cells; 3054 3055 _addClass(nTr, trClass); 3056 3057 /* Use a private property on the node to allow reserve mapping from the node 3058 * to the aoData array for fast look up 3059 */ 3060 nTr._DT_RowIndex = iRow; 3061 3062 /* Special parameters can be given by the data source to be used on the row */ 3063 _fnRowAttributes(oSettings, row); 3064 3065 /* Process each column */ 3066 for (i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) { 3067 oCol = oSettings.aoColumns[i]; 3068 create = nTrIn && anTds[i] ? false : true; 3069 3070 nTd = create ? document.createElement(oCol.sCellType) : anTds[i]; 3071 3072 if (!nTd) { 3073 _fnLog(oSettings, 0, 'Incorrect column count', 18); 3074 } 3075 3076 nTd._DT_CellIndex = { 3077 row: iRow, 3078 column: i 3079 }; 3080 3081 cells.push(nTd); 3082 3083 var display = _fnGetRowDisplay(oSettings, iRow); 3084 3085 // Need to create the HTML if new, or if a rendering function is defined 3086 if ( 3087 create || 3088 ( 3089 (oCol.mRender || oCol.mData !== i) && 3090 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i + '.display') 3091 ) 3092 ) { 3093 _fnWriteCell(nTd, display[i]); 3094 } 3095 3096 // column class 3097 _addClass(nTd, oCol.sClass); 3098 3099 // Visibility - add or remove as required 3100 if (oCol.bVisible && create) { 3101 nTr.appendChild(nTd); 3102 } 3103 else if (!oCol.bVisible && !create) { 3104 nTd.parentNode.removeChild(nTd); 3105 } 3106 3107 if (oCol.fnCreatedCell) { 3108 oCol.fnCreatedCell.call(oSettings.oInstance,
3109 nTd, _fnGetCellData(oSettings, iRow, i), rowData, iRow, i 3110 ); 3111 } 3112 } 3113 3114 _fnCallbackFire(oSettings, 'aoRowCreatedCallback', 'row-created', [nTr, rowData, iRow, cells]); 3115 } 3116 else { 3117 _addClass(row.nTr, trClass); 3118 } 3119 } 3120 3121 3122 /** 3123 * Add attributes to a row based on the special `DT_*` parameters in a data 3124 * source object. 3125 * @param {object} settings DataTables settings object 3126 * @param {object} DataTables row object for the row to be modified 3127 * @memberof DataTable#oApi 3128 */ 3129 function _fnRowAttributes(settings, row) { 3130 var tr = row.nTr; 3131 var data = row._aData; 3132 3133 if (tr) { 3134 var id = settings.rowIdFn(data); 3135 3136 if (id) { 3137 tr.id = id; 3138 } 3139 3140 if (data.DT_RowClass) { 3141 // Remove any classes added by DT_RowClass before 3142 var a = data.DT_RowClass.split(' '); 3143 row.__rowc = row.__rowc ? 3144 _unique(row.__rowc.concat(a)) : 3145 a; 3146 3147 $(tr) 3148 .removeClass(row.__rowc.join(' ')) 3149 .addClass(data.DT_RowClass); 3150 } 3151 3152 if (data.DT_RowAttr) { 3153 $(tr).attr(data.DT_RowAttr); 3154 } 3155 3156 if (data.DT_RowData) { 3157 $(tr).data(data.DT_RowData); 3158 } 3159 } 3160 } 3161 3162 3163 /** 3164 * Create the HTML header for the table 3165 * @param {object} oSettings dataTables settings object 3166 * @memberof DataTable#oApi 3167 */ 3168 function _fnBuildHead(settings, side) { 3169 var classes = settings.oClasses; 3170 var columns = settings.aoColumns; 3171 var i, iLen, row; 3172 var target = side === 'header' 3173 ? settings.nTHead 3174 : settings.nTFoot; 3175 var titleProp = side === 'header' ? 'sTitle' : side; 3176 3177 // Footer might be defined 3178 if (!target) { 3179 return; 3180 } 3181 3182 // If no cells yet and we have content for them, then create 3183 if (side === 'header' || _pluck(settings.aoColumns, titleProp).join('')) { 3184 row = $('tr', target); 3185 3186 // Add a row if needed 3187 if (!row.length) { 3188 row = $('<tr/>').appendTo(target) 3189 } 3190 3191 // Add the number of cells needed to make up to the number of columns 3192 if (row.length === 1) { 3193 var cellCount = 0; 3194 3195 $('td, th', row).each(function () { 3196 cellCount += this.colSpan; 3197 }); 3198 3199 for (i = cellCount, iLen = columns.length; i < iLen; i++) { 3200 $('<th/>') 3201 .html(columns[i][titleProp] || '') 3202 .appendTo(row); 3203 } 3204 } 3205 } 3206 3207 var detected = _fnDetectHeader(settings, target, true); 3208 3209 if (side === 'header') { 3210 settings.aoHeader = detected;
3211 $('tr', target).addClass(classes.thead.row); 3212 } 3213 else { 3214 settings.aoFooter = detected; 3215 $('tr', target).addClass(classes.tfoot.row); 3216 } 3217 3218 // Every cell needs to be passed through the renderer 3219 $(target).children('tr').children('th, td') 3220 .each(function () { 3221 _fnRenderer(settings, side)( 3222 settings, $(this), classes 3223 ); 3224 }); 3225 } 3226 3227 /** 3228 * Build a layout structure for a header or footer 3229 * 3230 * @param {*} settings DataTables settings 3231 * @param {*} source Source layout array 3232 * @param {*} incColumns What columns should be included 3233 * @returns Layout array in column index order 3234 */ 3235 function _fnHeaderLayout(settings, source, incColumns) { 3236 var row, column, cell; 3237 var local = []; 3238 var structure = []; 3239 var columns = settings.aoColumns; 3240 var columnCount = columns.length; 3241 var rowspan, colspan; 3242 3243 if (!source) { 3244 return; 3245 } 3246 3247 // Default is to work on only visible columns 3248 if (!incColumns) { 3249 incColumns = _range(columnCount) 3250 .filter(function (idx) { 3251 return columns[idx].bVisible; 3252 }); 3253 } 3254 3255 // Make a copy of the master layout array, but with only the columns we want 3256 for (row = 0; row < source.length; row++) { 3257 // Remove any columns we haven't selected 3258 local[row] = source[row].slice().filter(function (cell, i) { 3259 return incColumns.includes(i); 3260 }); 3261 3262 // Prep the structure array - it needs an element for each row 3263 structure.push([]); 3264 } 3265 3266 for (row = 0; row < local.length; row++) { 3267 for (column = 0; column < local[row].length; column++) { 3268 rowspan = 1; 3269 colspan = 1; 3270 3271 // Check to see if there is already a cell (row/colspan) covering our target 3272 // insert point. If there is, then there is nothing to do. 3273 if (structure[row][column] === undefined) { 3274 cell = local[row][column].cell; 3275 3276 // Expand for rowspan 3277 while ( 3278 local[row + rowspan] !== undefined && 3279 local[row][column].cell == local[row + rowspan][column].cell 3280 ) { 3281 structure[row + rowspan][column] = null; 3282 rowspan++; 3283 } 3284 3285 // And for colspan 3286 while ( 3287 local[row][column + colspan] !== undefined && 3288 local[row][column].cell == local[row][column + colspan].cell 3289 ) { 3290 // Which also needs to go over rows 3291 for (var k = 0; k < rowspan; k++) { 3292 structure[row + k][column + colspan] = null; 3293 } 3294 3295 colspan++; 3296 } 3297 3298 var titleSpan = $('span.dt-column-title', cell); 3299 3300 structure[row][column] = { 3301 cell: cell, 3302 colspan: colspan, 3303 rowspan: rowspan, 3304 title: titleSpan.length 3305 ? titleSpan.html() 3306 : $(cell).html() 3307 }; 3308 } 3309 } 3310 } 3311 3312 return structure; 3313 } 3314 3315 3316 /** 3317 * Draw the header (or footer) element based on the column visibility states. 3318 * 3319 * @param object oSettings dataTables settings object 3320 * @param array aoSource Layout array from _fnDetectHeader 3321 * @memberof DataTable#oApi 3322 */ 3323 function _fnDrawHead(settings, source) { 3324 var layout = _fnHeaderLayout(settings, source); 3325 var tr, n; 3326 3327 for (var row = 0; row < source.length; row++) { 3328 tr = source[row].row; 3329 3330 // All cells are going to be replaced, so empty out the row 3331 // Can't use $().empty() as that kills event handlers 3332 if (tr) { 3333 while ((n = tr.firstChild)) { 3334 tr.removeChild(n); 3335 } 3336 } 3337 3338 for (var column = 0; column < layout[row].length; column++) { 3339 var point = layout[row][column]; 3340 3341 if (point) { 3342 $(point.cell) 3343 .appendTo(tr) 3344 .attr('rowspan', point.rowspan) 3345 .attr('colspan', point.colspan); 3346 } 3347 } 3348 } 3349 } 3350 3351 3352 /** 3353 * Insert the required TR nodes into the table for display 3354 * @param {object} oSettings dataTables settings object
3355 * @param ajaxComplete true after ajax call to complete rendering 3356 * @memberof DataTable#oApi 3357 */ 3358 function _fnDraw(oSettings, ajaxComplete) { 3359 // Allow for state saving and a custom start position 3360 _fnStart(oSettings); 3361 3362 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */ 3363 var aPreDraw = _fnCallbackFire(oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings]); 3364 if (aPreDraw.indexOf(false) !== -1) { 3365 _fnProcessingDisplay(oSettings, false); 3366 return; 3367 } 3368 3369 var anRows = []; 3370 var iRowCount = 0; 3371 var bServerSide = _fnDataSource(oSettings) == 'ssp'; 3372 var aiDisplay = oSettings.aiDisplay; 3373 var iDisplayStart = oSettings._iDisplayStart; 3374 var iDisplayEnd = oSettings.fnDisplayEnd(); 3375 var columns = oSettings.aoColumns; 3376 var body = $(oSettings.nTBody); 3377 3378 oSettings.bDrawing = true; 3379 3380 /* Server-side processing draw intercept */ 3381 if (oSettings.deferLoading) { 3382 oSettings.deferLoading = false; 3383 oSettings.iDraw++; 3384 _fnProcessingDisplay(oSettings, false); 3385 } 3386 else if (!bServerSide) { 3387 oSettings.iDraw++; 3388 } 3389 else if (!oSettings.bDestroying && !ajaxComplete) { 3390 // Show loading message for server-side processing 3391 if (oSettings.iDraw === 0) { 3392 body.empty().append(_emptyRow(oSettings)); 3393 } 3394 3395 _fnAjaxUpdate(oSettings); 3396 return; 3397 } 3398 3399 if (aiDisplay.length !== 0) { 3400 var iStart = bServerSide ? 0 : iDisplayStart; 3401 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd; 3402 3403 for (var j = iStart; j < iEnd; j++) { 3404 var iDataIndex = aiDisplay[j]; 3405 var aoData = oSettings.aoData[iDataIndex]; 3406 if (aoData.nTr === null) { 3407 _fnCreateTr(oSettings, iDataIndex); 3408 } 3409 3410 var nRow = aoData.nTr; 3411 3412 // Add various classes as needed 3413 for (var i = 0; i < columns.length; i++) { 3414 var col = columns[i]; 3415 var td = aoData.anCells[i]; 3416 3417 _addClass(td, _ext.type.className[col.sType]); // auto class 3418 _addClass(td, oSettings.oClasses.tbody.cell); // all cells 3419 } 3420 3421 // Row callback functions - might want to manipulate the row 3422 // iRowCount and j are not currently documented. Are they at all 3423 // useful? 3424 _fnCallbackFire(oSettings, 'aoRowCallback', null, 3425 [nRow, aoData._aData, iRowCount, j, iDataIndex]); 3426 3427 anRows.push(nRow); 3428 iRowCount++; 3429 } 3430 } 3431 else { 3432 anRows[0] = _emptyRow(oSettings); 3433 } 3434 3435 /* Header and footer callbacks */ 3436 _fnCallbackFire(oSettings, 'aoHeaderCallback', 'header', [$(oSettings.nTHead).children('tr')[0], 3437 _fnGetDataMaster(oSettings), iDisplayStart, iDisplayEnd, aiDisplay]); 3438 3439 _fnCallbackFire(oSettings, 'aoFooterCallback', 'footer', [$(oSettings.nTFoot).children('tr')[0], 3440 _fnGetDataMaster(oSettings), iDisplayStart, iDisplayEnd, aiDisplay]); 3441 3442 // replaceChildren is faster, but only became widespread in 2020, 3443 // so a fall back in jQuery is provided for older browsers. 3444 if (body[0].replaceChildren) { 3445 body[0].replaceChildren.apply(body[0], anRows); 3446 } 3447 else { 3448 body.children().detach(); 3449 body.append($(anRows)); 3450 } 3451 3452 // Empty table needs a specific class 3453 $(oSettings.nTableWrapper).toggleClass('dt-empty-footer', $('tr', oSettings.nTFoot).length === 0); 3454 3455 /* Call all required callback functions for the end of a draw */ 3456 _fnCallbackFire(oSettings, 'aoDrawCallback', 'draw', [oSettings], true); 3457 3458 /* Draw is complete, sorting and filtering must be as well */ 3459 oSettings.bSorted = false; 3460 oSettings.bFiltered = false;
3461 oSettings.bDrawing = false; 3462 } 3463 3464 3465 /** 3466 * Redraw the table - taking account of the various features which are enabled 3467 * @param {object} oSettings dataTables settings object 3468 * @param {boolean} [holdPosition] Keep the current paging position. By default 3469 * the paging is reset to the first page 3470 * @memberof DataTable#oApi 3471 */ 3472 function _fnReDraw(settings, holdPosition, recompute) { 3473 var 3474 features = settings.oFeatures, 3475 sort = features.bSort, 3476 filter = features.bFilter; 3477 3478 if (recompute === undefined || recompute === true) { 3479 // Resolve any column types that are unknown due to addition or invalidation 3480 _fnColumnTypes(settings); 3481 3482 if (sort) { 3483 _fnSort(settings); 3484 } 3485 3486 if (filter) { 3487 _fnFilterComplete(settings, settings.oPreviousSearch); 3488 } 3489 else { 3490 // No filtering, so we want to just use the display master 3491 settings.aiDisplay = settings.aiDisplayMaster.slice(); 3492 } 3493 } 3494 3495 if (holdPosition !== true) { 3496 settings._iDisplayStart = 0; 3497 } 3498 3499 // Let any modules know about the draw hold position state (used by 3500 // scrolling internally) 3501 settings._drawHold = holdPosition; 3502 3503 _fnDraw(settings); 3504 3505 settings.api.one('draw', function () { 3506 settings._drawHold = false; 3507 }); 3508 } 3509 3510 3511 /* 3512 * Table is empty - create a row with an empty message in it 3513 */ 3514 function _emptyRow(settings) { 3515 var oLang = settings.oLanguage; 3516 var zero = oLang.sZeroRecords; 3517 var dataSrc = _fnDataSource(settings); 3518 3519 // Make use of the fact that settings.json is only set once the initial data has 3520 // been loaded. Show loading when that isn't the case 3521 if ((dataSrc === 'ssp' || dataSrc === 'ajax') && !settings.json) { 3522 zero = oLang.sLoadingRecords; 3523 } 3524 else if (oLang.sEmptyTable && settings.fnRecordsTotal() === 0) { 3525 zero = oLang.sEmptyTable; 3526 } 3527 3528 return $('<tr/>') 3529 .append($('<td />', { 3530 'colSpan': _fnVisibleColumns(settings), 3531 'class': settings.oClasses.empty.row 3532 }).html(zero))[0]; 3533 } 3534 3535 3536 /** 3537 * Expand the layout items into an object for the rendering function 3538 */ 3539 function _layoutItems(row, align, items) { 3540 if (Array.isArray(items)) { 3541 for (var i = 0; i < items.length; i++) { 3542 _layoutItems(row, align, items[i]); 3543 } 3544 3545 return; 3546 } 3547 3548 var rowCell = row[align]; 3549 3550 // If it is an object, then there can be multiple features contained in it 3551 if ($.isPlainObject(items)) { 3552 // A feature plugin cannot be named "features" due to this check 3553 if (items.features) { 3554 if (items.rowId) { 3555 row.id = items.rowId; 3556 } 3557 if (items.rowClass) { 3558 row.className = items.rowClass; 3559 } 3560 3561 rowCell.id = items.id; 3562 rowCell.className = items.className; 3563 3564 _layoutItems(row, align, items.features); 3565 } 3566 else { 3567 Object.keys(items).map(function (key) { 3568 rowCell.contents.push({ 3569 feature: key, 3570 opts: items[key] 3571 }); 3572 }); 3573 } 3574 } 3575 else { 3576 rowCell.contents.push(items); 3577 } 3578 } 3579 3580 /** 3581 * Find, or create a layout row 3582 */ 3583 function _layoutGetRow(rows, rowNum, align) { 3584 var row; 3585 3586 // Find existing rows 3587 for (var i = 0; i < rows.length; i++) { 3588 row = rows[i]; 3589 3590 if (row.rowNum === rowNum) { 3591 // full is on its own, but start and end share a row 3592 if ( 3593 (align === 'full' && row.full) || 3594 ((align === 'start' || align === 'end') && (row.start || row.end)) 3595 ) { 3596 if (!row[align]) { 3597 row[align] = { 3598 contents: [] 3599 }; 3600 } 3601 3602 return row; 3603 } 3604 } 3605 } 3606 3607 // If we get this far, then there was no match, create a new row 3608 row = { 3609 rowNum: rowNum 3610 }; 3611 3612 row[align] = { 3613 contents: [] 3614 }; 3615 3616 rows.push(row); 3617 3618 return row; 3619 } 3620 3621 /** 3622 * Convert a `layout` object given by a user to the object structure needed 3623 * for the renderer. This is done twice, once for above and once for below
3624 * the table. Ordering must also be considered. 3625 * 3626 * @param {*} settings DataTables settings object 3627 * @param {*} layout Layout object to convert 3628 * @param {string} side `top` or `bottom` 3629 * @returns Converted array structure - one item for each row. 3630 */ 3631 function _layoutArray(settings, layout, side) { 3632 var rows = []; 3633 3634 // Split out into an array 3635 $.each(layout, function (pos, items) { 3636 if (items === null) { 3637 return; 3638 } 3639 3640 var parts = pos.match(/^([a-z]+)([0-9]*)([A-Za-z]*)$/); 3641 var rowNum = parts[2] 3642 ? parts[2] * 1 3643 : 0; 3644 var align = parts[3] 3645 ? parts[3].toLowerCase() 3646 : 'full'; 3647 3648 // Filter out the side we aren't interested in 3649 if (parts[1] !== side) { 3650 return; 3651 } 3652 3653 // Get or create the row we should attach to 3654 var row = _layoutGetRow(rows, rowNum, align); 3655 3656 _layoutItems(row, align, items); 3657 }); 3658 3659 // Order by item identifier 3660 rows.sort(function (a, b) { 3661 var order1 = a.rowNum; 3662 var order2 = b.rowNum; 3663 3664 // If both in the same row, then the row with `full` comes first 3665 if (order1 === order2) { 3666 var ret = a.full && !b.full ? -1 : 1; 3667 3668 return side === 'bottom' 3669 ? ret * -1 3670 : ret; 3671 } 3672 3673 return order2 - order1; 3674 }); 3675 3676 // Invert for below the table 3677 if (side === 'bottom') { 3678 rows.reverse(); 3679 } 3680 3681 for (var row = 0; row < rows.length; row++) { 3682 delete rows[row].rowNum; 3683 3684 _layoutResolve(settings, rows[row]); 3685 } 3686 3687 return rows; 3688 } 3689 3690 3691 /** 3692 * Convert the contents of a row's layout object to nodes that can be inserted 3693 * into the document by a renderer. Execute functions, look up plug-ins, etc. 3694 * 3695 * @param {*} settings DataTables settings object 3696 * @param {*} row Layout object for this row 3697 */ 3698 function _layoutResolve(settings, row) { 3699 var getFeature = function (feature, opts) { 3700 if (!_ext.features[feature]) { 3701 _fnLog(settings, 0, 'Unknown feature: ' + feature); 3702 } 3703 3704 return _ext.features[feature].apply(this, [settings, opts]); 3705 }; 3706 3707 var resolve = function (item) { 3708 if (!row[item]) { 3709 return; 3710 } 3711 3712 var line = row[item].contents; 3713 3714 for (var i = 0, iLen = line.length; i < iLen; i++) { 3715 if (!line[i]) { 3716 continue; 3717 } 3718 else if (typeof line[i] === 'string') { 3719 line[i] = getFeature(line[i], null); 3720 } 3721 else if ($.isPlainObject(line[i])) { 3722 // If it's an object, it just has feature and opts properties from 3723 // the transform in _layoutArray 3724 line[i] = getFeature(line[i].feature, line[i].opts); 3725 } 3726 else if (typeof line[i].node === 'function') { 3727 line[i] = line[i].node(settings); 3728 } 3729 else if (typeof line[i] === 'function') { 3730 var inst = line[i](settings); 3731 3732 line[i] = typeof inst.node === 'function' ? 3733 inst.node() : 3734 inst; 3735 } 3736 } 3737 }; 3738 3739 resolve('start'); 3740 resolve('end'); 3741 resolve('full'); 3742 } 3743 3744 3745 /** 3746 * Add the options to the page HTML for the table 3747 * @param {object} settings DataTables settings object 3748 * @memberof DataTable#oApi 3749 */ 3750 function _fnAddOptionsHtml(settings) { 3751 var classes = settings.oClasses; 3752 var table = $(settings.nTable); 3753 3754 // Wrapper div around everything DataTables controls 3755 var insert = $('<div/>') 3756 .attr({ 3757 id: settings.sTableId + '_wrapper', 3758 'class': classes.container 3759 }) 3760 .insertBefore(table); 3761 3762 settings.nTableWrapper = insert[0]; 3763 3764 if (settings.sDom) { 3765 // Legacy 3766 _fnLayoutDom(settings, settings.sDom, insert); 3767 } 3768 else { 3769 var top = _layoutArray(settings, settings.layout, 'top'); 3770 var bottom = _layoutArray(settings, settings.layout, 'bottom'); 3771 var renderer = _fnRenderer(settings, 'layout'); 3772 3773 // Everything above - the renderer will actually insert the contents into the document
3774 top.forEach(function (item) { 3775 renderer(settings, insert, item); 3776 }); 3777 3778 // The table - always the center of attention 3779 renderer(settings, insert, { 3780 full: { 3781 table: true, 3782 contents: [_fnFeatureHtmlTable(settings)] 3783 } 3784 }); 3785 3786 // Everything below 3787 bottom.forEach(function (item) { 3788 renderer(settings, insert, item); 3789 }); 3790 } 3791 3792 // Processing floats on top, so it isn't an inserted feature 3793 _processingHtml(settings); 3794 } 3795 3796 /** 3797 * Draw the table with the legacy DOM property 3798 * @param {*} settings DT settings object 3799 * @param {*} dom DOM string 3800 * @param {*} insert Insert point 3801 */ 3802 function _fnLayoutDom(settings, dom, insert) { 3803 var parts = dom.match(/(".*?")|('.*?')|./g); 3804 var featureNode, option, newNode, next, attr; 3805 3806 for (var i = 0; i < parts.length; i++) { 3807 featureNode = null; 3808 option = parts[i]; 3809 3810 if (option == '<') { 3811 // New container div 3812 newNode = $('<div/>'); 3813 3814 // Check to see if we should append an id and/or a class name to the container 3815 next = parts[i + 1]; 3816 3817 if (next[0] == "'" || next[0] == '"') { 3818 attr = next.replace(/['"]/g, ''); 3819 3820 var id = '', className; 3821 3822 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic 3823 * breaks the string into parts and applies them as needed 3824 */ 3825 if (attr.indexOf('.') != -1) { 3826 var split = attr.split('.'); 3827 3828 id = split[0]; 3829 className = split[1]; 3830 } 3831 else if (attr[0] == "#") { 3832 id = attr; 3833 } 3834 else { 3835 className = attr; 3836 } 3837 3838 newNode 3839 .attr('id', id.substring(1)) 3840 .addClass(className); 3841 3842 i++; // Move along the position array 3843 } 3844 3845 insert.append(newNode); 3846 insert = newNode; 3847 } 3848 else if (option == '>') { 3849 // End container div 3850 insert = insert.parent(); 3851 } 3852 else if (option == 't') { 3853 // Table 3854 featureNode = _fnFeatureHtmlTable(settings); 3855 } 3856 else { 3857 DataTable.ext.feature.forEach(function (feature) { 3858 if (option == feature.cFeature) { 3859 featureNode = feature.fnInit(settings); 3860 } 3861 }); 3862 } 3863 3864 // Add to the display 3865 if (featureNode) { 3866 insert.append(featureNode); 3867 } 3868 } 3869 } 3870 3871 3872 /** 3873 * Use the DOM source to create up an array of header cells. The idea here is to 3874 * create a layout grid (array) of rows x columns, which contains a reference 3875 * to the cell at that point in the grid (regardless of col/rowspan), such that 3876 * any column / row could be removed and the new grid constructed 3877 * @param {node} thead The header/footer element for the table 3878 * @returns {array} Calculated layout array 3879 * @memberof DataTable#oApi 3880 */ 3881 function _fnDetectHeader(settings, thead, write) { 3882 var columns = settings.aoColumns; 3883 var rows = $(thead).children('tr'); 3884 var row, cell; 3885 var i, k, l, iLen, shifted, column, colspan, rowspan; 3886 var titleRow = settings.titleRow; 3887 var isHeader = thead && thead.nodeName.toLowerCase() === 'thead'; 3888 var layout = []; 3889 var unique; 3890 var shift = function (a, i, j) { 3891 var k = a[i]; 3892 while (k[j]) { 3893 j++; 3894 } 3895 return j; 3896 }; 3897 3898 // We know how many rows there are in the layout - so prep it
3899 for (i = 0, iLen = rows.length; i < iLen; i++) { 3900 layout.push([]); 3901 } 3902 3903 for (i = 0, iLen = rows.length; i < iLen; i++) { 3904 row = rows[i]; 3905 column = 0; 3906 3907 // For every cell in the row.. 3908 cell = row.firstChild; 3909 while (cell) { 3910 if ( 3911 cell.nodeName.toUpperCase() == 'TD' || 3912 cell.nodeName.toUpperCase() == 'TH' 3913 ) { 3914 var cols = []; 3915 var jqCell = $(cell); 3916 3917 // Get the col and rowspan attributes from the DOM and sanitise them 3918 colspan = cell.getAttribute('colspan') * 1; 3919 rowspan = cell.getAttribute('rowspan') * 1; 3920 colspan = (!colspan || colspan === 0 || colspan === 1) ? 1 : colspan; 3921 rowspan = (!rowspan || rowspan === 0 || rowspan === 1) ? 1 : rowspan; 3922 3923 // There might be colspan cells already in this row, so shift our target 3924 // accordingly 3925 shifted = shift(layout, i, column); 3926 3927 // Cache calculation for unique columns 3928 unique = colspan === 1 ? 3929 true : 3930 false; 3931 3932 // Perform header setup 3933 if (write) { 3934 if (unique) { 3935 // Allow column options to be set from HTML attributes 3936 _fnColumnOptions(settings, shifted, _fnEscapeObject(jqCell.data())); 3937 3938 // Get the width for the column. This can be defined from the 3939 // width attribute, style attribute or `columns.width` option 3940 var columnDef = columns[shifted]; 3941 var width = cell.getAttribute('width') || null; 3942 var t = cell.style.width.match(/width:\s*(\d+[pxem%]+)/); 3943 if (t) { 3944 width = t[1]; 3945 } 3946 3947 columnDef.sWidthOrig = columnDef.sWidth || width; 3948 3949 if (isHeader) { 3950 // Column title handling - can be user set, or read from the DOM 3951 // This happens before the render, so the original is still in place 3952 if (columnDef.sTitle !== null && !columnDef.autoTitle) { 3953 if ( 3954 (titleRow === true && i === 0) || // top row 3955 (titleRow === false && i === rows.length - 1) || // bottom row 3956 (titleRow === i) || // specific row 3957 (titleRow === null) 3958 ) { 3959 cell.innerHTML = columnDef.sTitle; 3960 } 3961 } 3962 3963 if (!columnDef.sTitle && unique) { 3964 columnDef.sTitle = _stripHtml(cell.innerHTML); 3965 columnDef.autoTitle = true; 3966 } 3967 } 3968 else { 3969 // Footer specific operations 3970 if (columnDef.footer) { 3971 cell.innerHTML = columnDef.footer; 3972 } 3973 } 3974 3975 // Fall back to the aria-label attribute on the table header if no ariaTitle is 3976 // provided. 3977 if (!columnDef.ariaTitle) { 3978 columnDef.ariaTitle = jqCell.attr("aria-label") || columnDef.sTitle; 3979 } 3980 3981 // Column specific class names 3982 if (columnDef.className) { 3983 jqCell.addClass(columnDef.className); 3984 } 3985 } 3986 3987 // Wrap the column title so we can write to it in future 3988 if ($('span.dt-column-title', cell).length === 0) { 3989 $('<span>') 3990 .addClass('dt-column-title') 3991 .append(cell.childNodes) 3992 .appendTo(cell); 3993 } 3994 3995 if ( 3996 settings.orderIndicators && 3997 isHeader && 3998 jqCell.filter(':not([data-dt-order=disable])').length !== 0 && 3999 jqCell.parent(':not([data-dt-order=disable])').length !== 0 && 4000 $('span.dt-column-order', cell).length === 0 4001 ) { 4002 $('<span>') 4003 .addClass('dt-column-order') 4004 .appendTo(cell); 4005 } 4006 4007 // We need to wrap the elements in the header in another element to use flexbox 4008 // layout for those elements 4009 var headerFooter = isHeader ? 'header' : 'footer'; 4010 4011 if ($('span.dt-column-' + headerFooter, cell).length === 0) { 4012 $('<div>') 4013 .addClass('dt-column-' + headerFooter) 4014 .append(cell.childNodes) 4015 .appendTo(cell); 4016 } 4017 } 4018 4019 // If there is col / rowspan, copy the information into the layout grid 4020 for (l = 0; l < colspan; l++) { 4021 for (k = 0; k < rowspan; k++) { 4022 layout[i + k][shifted + l] = { 4023 cell: cell, 4024 unique: unique 4025 }; 4026 4027 layout[i + k].row = row; 4028 } 4029 4030 cols.push(shifted + l); 4031 } 4032 4033 // Assign an attribute so spanning cells can still be identified 4034 // as belonging to a column 4035 cell.setAttribute('data-dt-column', _unique(cols).join(',')); 4036 } 4037 4038 cell = cell.nextSibling; 4039 } 4040 } 4041 4042 return layout; 4043 } 4044 4045 /** 4046 * Set the start position for draw 4047 * @param {object} oSettings dataTables settings object 4048 */ 4049 function _fnStart(oSettings) { 4050 var bServerSide = _fnDataSource(oSettings) == 'ssp'; 4051 var iInitDisplayStart = oSettings.iInitDisplayStart; 4052 4053 // Check and see if we have an initial draw position from state saving 4054 if (iInitDisplayStart !== undefined && iInitDisplayStart !== -1) { 4055 oSettings._iDisplayStart = bServerSide ? 4056 iInitDisplayStart : 4057 iInitDisplayStart >= oSettings.fnRecordsDisplay() ? 4058 0 : 4059 iInitDisplayStart; 4060 4061 oSettings.iInitDisplayStart = -1; 4062 } 4063 } 4064 4065 /** 4066 * Create an Ajax call based on the table's settings, taking into account that 4067 * parameters can have multiple forms, and backwards compatibility. 4068 * 4069 * @param {object} oSettings dataTables settings object 4070 * @param {array} data Data to send to the server, required by 4071 * DataTables - may be augmented by developer callbacks 4072 * @param {function} fn Callback function to run when data is obtained 4073 */ 4074 function _fnBuildAjax(oSettings, data, fn) { 4075 var ajaxData; 4076 var ajax = oSettings.ajax; 4077 var instance = oSettings.oInstance; 4078 var callback = function (json) { 4079 var status = oSettings.jqXHR ? oSettings.jqXHR.status : null; 4080 4081 if (json === null || (typeof status === 'number' && status == 204)) { 4082 json = {}; 4083 _fnAjaxDataSrc(oSettings, json, []); 4084 } 4085 4086 var error = json.error || json.sError; 4087 if (error) { 4088 _fnLog(oSettings, 0, error); 4089 } 4090 4091 // Microsoft often wrap JSON as a string in another JSON object 4092 // Let's handle that automatically 4093 if (json.d && typeof json.d === 'string') { 4094 try { 4095 json = JSON.parse(json.d); 4096 } catch (e) { 4097 // noop 4098 } 4099 } 4100 4101 oSettings.json = json; 4102 4103 _fnCallbackFire(oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true); 4104 fn(json); 4105 }; 4106 4107 if ($.isPlainObject(ajax) && ajax.data) { 4108 ajaxData = ajax.data; 4109 4110 var newData = 4111 typeof ajaxData === 'function' 4112 ? ajaxData(data, oSettings) // fn can manipulate data or return 4113 : ajaxData; // an object or array to merge 4114 4115 // If the function returned something, use that alone 4116 data = typeof ajaxData === 'function' && newData ? newData : $.extend(true, data, newData); 4117
4118 // Remove the data property as we've resolved it already and don't want 4119 // jQuery to do it again (it is restored at the end of the function) 4120 delete ajax.data; 4121 } 4122 4123 var baseAjax = { 4124 url: typeof ajax === 'string' ? ajax : '', 4125 data: data, 4126 success: callback, 4127 dataType: 'json', 4128 cache: false, 4129 type: oSettings.sServerMethod, 4130 error: function (xhr, error) { 4131 var ret = _fnCallbackFire( 4132 oSettings, 4133 null, 4134 'xhr', 4135 [oSettings, null, oSettings.jqXHR], 4136 true 4137 ); 4138 4139 if (ret.indexOf(true) === -1) { 4140 if (error == 'parsererror') { 4141 _fnLog(oSettings, 0, 'Invalid JSON response', 1); 4142 } 4143 else if (xhr.readyState === 4) { 4144 _fnLog(oSettings, 0, 'Ajax error', 7); 4145 } 4146 } 4147 4148 _fnProcessingDisplay(oSettings, false); 4149 } 4150 }; 4151 4152 // If `ajax` option is an object, extend and override our default base 4153 if ($.isPlainObject(ajax)) { 4154 $.extend(baseAjax, ajax); 4155 } 4156 4157 // Store the data submitted for the API 4158 oSettings.oAjaxData = data; 4159 4160 // Allow plug-ins and external processes to modify the data 4161 _fnCallbackFire(oSettings, null, 'preXhr', [oSettings, data, baseAjax], true); 4162 4163 // Custom Ajax option to submit the parameters as a JSON string 4164 if (baseAjax.submitAs === 'json' && typeof data === 'object') { 4165 baseAjax.data = JSON.stringify(data); 4166 } 4167 4168 if (typeof ajax === 'function') { 4169 // Is a function - let the caller define what needs to be done 4170 oSettings.jqXHR = ajax.call(instance, data, callback, oSettings); 4171 } 4172 else if (ajax.url === '') { 4173 // No url, so don't load any data. Just apply an empty data array 4174 // to the object for the callback. 4175 var empty = {}; 4176 4177 _fnAjaxDataSrc(oSettings, empty, []); 4178 callback(empty); 4179 } 4180 else { 4181 // Object to extend the base settings 4182 oSettings.jqXHR = $.ajax(baseAjax); 4183 } 4184 4185 // Restore for next time around 4186 if (ajaxData) { 4187 ajax.data = ajaxData; 4188 } 4189 } 4190 4191 /** 4192 * Update the table using an Ajax call 4193 * @param {object} settings dataTables settings object 4194 * @returns {boolean} Block the table drawing or not 4195 * @memberof DataTable#oApi 4196 */ 4197 function _fnAjaxUpdate(settings) { 4198 settings.iDraw++; 4199 _fnProcessingDisplay(settings, true); 4200 4201 _fnBuildAjax(settings, _fnAjaxParameters(settings), function (json) { 4202 _fnAjaxUpdateDraw(settings, json); 4203 }); 4204 } 4205 4206 /** 4207 * Build up the parameters in an object needed for a server-side processing 4208 * request. 4209 * @param {object} oSettings dataTables settings object 4210 * @returns {bool} block the table drawing or not 4211 * @memberof DataTable#oApi 4212 */ 4213 function _fnAjaxParameters(settings) { 4214 var columns = settings.aoColumns, 4215 features = settings.oFeatures, 4216 preSearch = settings.oPreviousSearch, 4217 preColSearch = settings.aoPreSearchCols, 4218 colData = function (idx, prop) { 4219 return typeof columns[idx][prop] === 'function' ? 'function' : columns[idx][prop]; 4220 }; 4221 4222 return { 4223 draw: settings.iDraw, 4224 columns: columns.map(function (column, i) { 4225 return { 4226 data: colData(i, 'mData'), 4227 name: column.sName, 4228 searchable: column.bSearchable, 4229 orderable: column.bSortable, 4230 search: { 4231 value: preColSearch[i].search, 4232 regex: preColSearch[i].regex, 4233 fixed: Object.keys(column.searchFixed) 4234 .map(function (name) { 4235 return { 4236 name: name, 4237 term: typeof column.searchFixed[name] !== 'function' 4238 ? column.searchFixed[name].toString() 4239 : 'function' 4240 }; 4241 }) 4242 } 4243 }; 4244 }), 4245 order: _fnSortFlatten(settings).map(function (val) { 4246 return { 4247 column: val.col, 4248 dir: val.dir, 4249 name: colData(val.col, 'sName') 4250 }; 4251 }), 4252 start: settings._iDisplayStart, 4253 length: features.bPaginate ? settings._iDisplayLength : -1, 4254 search: { 4255 value: preSearch.search, 4256 regex: preSearch.regex, 4257 fixed: Object.keys(settings.searchFixed) 4258 .map(function (name) { 4259 return { 4260 name: name, 4261 term: typeof settings.searchFixed[name] !== 'function' 4262 ? settings.searchFixed[name].toString() 4263 : 'function' 4264 }; 4265 }) 4266 } 4267 }; 4268 } 4269 4270 /** 4271 * Data the data from the server (nuking the old) and redraw the table 4272 * @param {object} oSettings dataTables settings object 4273 * @param {object} json json data return from the server. 4274 * @param {string} json.sEcho Tracking flag for DataTables to match requests 4275 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering 4276 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering 4277 * @param {array} json.aaData The data to display on this page 4278 * @param {string} [json.sColumns] Column ordering (sName, comma separated) 4279 * @memberof DataTable#oApi 4280 */ 4281 function _fnAjaxUpdateDraw(settings, json) { 4282 var data = _fnAjaxDataSrc(settings, json); 4283 var draw = _fnAjaxDataSrcParam(settings, 'draw', json); 4284 var recordsTotal = _fnAjaxDataSrcParam(settings, 'recordsTotal', json); 4285 var recordsFiltered = _fnAjaxDataSrcParam(settings, 'recordsFiltered', json); 4286 4287 if (draw !== undefined) { 4288 // Protect against out of sequence returns 4289 if (draw * 1 < settings.iDraw) { 4290 return; 4291 } 4292 settings.iDraw = draw * 1; 4293 } 4294 4295 // No data in returned object, so rather than an array, we show an empty table 4296 if (!data) { 4297 data = []; 4298 } 4299
4300 _fnClearTable(settings); 4301 settings._iRecordsTotal = parseInt(recordsTotal, 10); 4302 settings._iRecordsDisplay = parseInt(recordsFiltered, 10); 4303 4304 for (var i = 0, iLen = data.length; i < iLen; i++) { 4305 _fnAddData(settings, data[i]); 4306 } 4307 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4308 4309 _fnColumnTypes(settings); 4310 _fnDraw(settings, true); 4311 _fnInitComplete(settings); 4312 _fnProcessingDisplay(settings, false); 4313 } 4314 4315 /** 4316 * Get the data from the JSON data source to use for drawing a table. Using 4317 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the 4318 * source object, or from a processing function. 4319 * @param {object} settings dataTables settings object 4320 * @param {object} json Data source object / array from the server 4321 * @return {array} Array of data to use 4322 */ 4323 function _fnAjaxDataSrc(settings, json, write) { 4324 var dataProp = 'data'; 4325 4326 if ($.isPlainObject(settings.ajax) && settings.ajax.dataSrc !== undefined) { 4327 // Could in inside a `dataSrc` object, or not! 4328 var dataSrc = settings.ajax.dataSrc; 4329 4330 // string, function and object are valid types 4331 if (typeof dataSrc === 'string' || typeof dataSrc === 'function') { 4332 dataProp = dataSrc; 4333 } 4334 else if (dataSrc.data !== undefined) { 4335 dataProp = dataSrc.data; 4336 } 4337 } 4338 4339 if (!write) { 4340 if (dataProp === 'data') { 4341 // If the default, then we still want to support the old style, and safely ignore 4342 // it if possible 4343 return json.aaData || json[dataProp]; 4344 } 4345 4346 return dataProp !== '' ? _fnGetObjectDataFn(dataProp)(json) : json; 4347 } 4348 4349 // set 4350 _fnSetObjectDataFn(dataProp)(json, write); 4351 } 4352 4353 /** 4354 * Very similar to _fnAjaxDataSrc, but for the other SSP properties 4355 * @param {*} settings DataTables settings object 4356 * @param {*} param Target parameter 4357 * @param {*} json JSON data 4358 * @returns Resolved value 4359 */ 4360 function _fnAjaxDataSrcParam(settings, param, json) { 4361 var dataSrc = $.isPlainObject(settings.ajax) ? settings.ajax.dataSrc : null; 4362 4363 if (dataSrc && dataSrc[param]) { 4364 // Get from custom location 4365 return _fnGetObjectDataFn(dataSrc[param])(json); 4366 } 4367 4368 // else - Default behaviour 4369 var old = ''; 4370 4371 // Legacy support 4372 if (param === 'draw') { 4373 old = 'sEcho'; 4374 } 4375 else if (param === 'recordsTotal') { 4376 old = 'iTotalRecords'; 4377 } 4378 else if (param === 'recordsFiltered') { 4379 old = 'iTotalDisplayRecords'; 4380 } 4381 4382 return json[old] !== undefined ? json[old] : json[param]; 4383 } 4384 4385 4386 /** 4387 * Filter the table using both the global filter and column based filtering 4388 * @param {object} settings dataTables settings object 4389 * @param {object} input search information 4390 * @memberof DataTable#oApi 4391 */ 4392 function _fnFilterComplete(settings, input) { 4393 var columnsSearch = settings.aoPreSearchCols; 4394 4395 // In server-side processing all filtering is done by the server, so no point hanging around here 4396 if (_fnDataSource(settings) != 'ssp') { 4397 // Check if any of the rows were invalidated 4398 _fnFilterData(settings); 4399 4400 // Start from the full data set 4401 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4402 4403 // Global filter first 4404 _fnFilter(settings.aiDisplay, settings, input.search, input); 4405 4406 $.each(settings.searchFixed, function (name, term) { 4407 _fnFilter(settings.aiDisplay, settings, term, {}); 4408 }); 4409 4410 // Then individual column filters 4411 for (var i = 0; i < columnsSearch.length; i++) { 4412 var col = columnsSearch[i]; 4413 4414 _fnFilter( 4415 settings.aiDisplay, 4416 settings, 4417 col.search, 4418 col, 4419 i 4420 ); 4421 4422 $.each(settings.aoColumns[i].searchFixed, function (name, term) { 4423 _fnFilter(settings.aiDisplay, settings, term, {}, i); 4424 }); 4425 } 4426 4427 // And finally global filtering 4428 _fnFilterCustom(settings); 4429 } 4430 4431 // Tell the draw function we have been filtering 4432 settings.bFiltered = true; 4433 4434 _fnCallbackFire(settings, null, 'search', [settings]); 4435 } 4436 4437 4438 /** 4439 * Apply custom filtering functions 4440 * 4441 * This is legacy now that we have named functions, but it is widely used 4442 * from 1.x, so it is not yet deprecated. 4443 * @param {object} oSettings dataTables settings object 4444 * @memberof DataTable#oApi 4445 */ 4446 function _fnFilterCustom(settings) { 4447 var filters = DataTable.ext.search; 4448 var displayRows = settings.aiDisplay; 4449 var row, rowIdx; 4450 4451 for (var i = 0, iLen = filters.length; i < iLen; i++) { 4452 var rows = []; 4453 4454 // Loop over each row and see if it should be included 4455 for (var j = 0, jen = displayRows.length; j < jen; j++) { 4456 rowIdx = displayRows[j]; 4457 row = settings.aoData[rowIdx]; 4458 4459 if (filters[i](settings, row._aFilterData, rowIdx, row._aData, j)) { 4460 rows.push(rowIdx); 4461 } 4462 } 4463 4464 // So the array reference doesn't break set the results into the 4465 // existing array 4466 displayRows.length = 0; 4467 _fnArrayApply(displayRows, rows); 4468 } 4469 } 4470 4471 4472 /** 4473 * Filter the data table based on user input and draw the table 4474 */ 4475 function _fnFilter(searchRows, settings, input, options, column) { 4476 if (input === '') { 4477 return; 4478 } 4479 4480 var i = 0; 4481 var matched = []; 4482 4483 // Search term can be a function, regex or string - if a string we apply our 4484 // smart filtering regex (assuming the options require that) 4485 var searchFunc = typeof input === 'function' ? input : null; 4486 var rpSearch = input instanceof RegExp 4487 ? input 4488 : searchFunc 4489 ? null 4490 : _fnFilterCreateSearch(input, options); 4491 4492 // Then for each row, does the test pass. If not, lop the row from the array 4493 for (i = 0; i < searchRows.length; i++) { 4494 var row = settings.aoData[searchRows[i]]; 4495 var data = column === undefined 4496 ? row._sFilterRow 4497 : row._aFilterData[column]; 4498 4499 if ((searchFunc && searchFunc(data, row._aData, searchRows[i], column)) || (rpSearch && rpSearch.test(data))) { 4500 matched.push(searchRows[i]); 4501 } 4502 } 4503 4504 // Mutate the searchRows array 4505 searchRows.length = matched.length; 4506 4507 for (i = 0; i < matched.length; i++) { 4508 searchRows[i] = matched[i]; 4509 } 4510 } 4511 4512 4513 /** 4514 * Build a regular expression object suitable for searching a table 4515 * @param {string} sSearch string to search for 4516 * @param {bool} bRegex treat as a regular expression or not 4517 * @param {bool} bSmart perform smart filtering or not 4518 * @param {bool} bCaseInsensitive Do case-insensitive matching or not 4519 * @returns {RegExp} constructed object 4520 * @memberof DataTable#oApi 4521 */ 4522 function _fnFilterCreateSearch(search, inOpts) { 4523 var not = []; 4524 var options = $.extend({}, { 4525 boundary: false, 4526 caseInsensitive: true, 4527 exact: false, 4528 regex: false, 4529 smart: true 4530 }, inOpts); 4531 4532 if (typeof search !== 'string') { 4533 search = search.toString(); 4534 } 4535 4536 // Remove diacritics if normalize is set up to do so 4537 search = _normalize(search); 4538 4539 if (options.exact) { 4540 return new RegExp( 4541 '^' + _fnEscapeRegex(search) + '$', 4542 options.caseInsensitive ? 'i' : '' 4543 ); 4544 } 4545 4546 search = options.regex ? 4547 search : 4548 _fnEscapeRegex(search); 4549 4550 if (options.smart) { 4551 /* For smart filtering we want to allow the search to work regardless of 4552 * word order. We also want double quoted text to be preserved, so word 4553 * order is important - a la google. And a negative look around for 4554 * finding rows which don't contain a given string. 4555 * 4556 * So this is the sort of thing we want to generate: 4557 * 4558 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$ 4559 */ 4560 var parts = search.match(/!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g) || ['']; 4561 var a = parts.map(function (word) { 4562 var negative = false;
4563 var m; 4564 4565 // Determine if it is a "does not include" 4566 if (word.charAt(0) === '!') { 4567 negative = true; 4568 word = word.substring(1); 4569 } 4570 4571 // Strip the quotes from around matched phrases 4572 if (word.charAt(0) === '"') { 4573 m = word.match(/^"(.*)"$/); 4574 word = m ? m[1] : word; 4575 } 4576 else if (word.charAt(0) === '\u201C') { 4577 // Smart quote match (iPhone users) 4578 m = word.match(/^\u201C(.*)\u201D$/); 4579 word = m ? m[1] : word; 4580 } 4581 4582 // For our "not" case, we need to modify the string that is 4583 // allowed to match at the end of the expression. 4584 if (negative) { 4585 if (word.length > 1) { 4586 not.push('(?!' + word + ')'); 4587 } 4588 4589 word = ''; 4590 } 4591 4592 return word.replace(/"/g, ''); 4593 }); 4594 4595 var match = not.length 4596 ? not.join('') 4597 : ''; 4598 4599 var boundary = options.boundary 4600 ? '\\b' 4601 : ''; 4602 4603 search = '^(?=.*?' + boundary + a.join(')(?=.*?' + boundary) + ')(' + match + '.)*$'; 4604 } 4605 4606 return new RegExp(search, options.caseInsensitive ? 'i' : ''); 4607 } 4608 4609 4610 /** 4611 * Escape a string such that it can be used in a regular expression 4612 * @param {string} sVal string to escape 4613 * @returns {string} escaped string 4614 * @memberof DataTable#oApi 4615 */ 4616 var _fnEscapeRegex = DataTable.util.escapeRegex; 4617 4618 var __filter_div = $('<div>')[0]; 4619 var __filter_div_textContent = __filter_div.textContent !== undefined; 4620 4621 // Update the filtering data for each row if needed (by invalidation or first run) 4622 function _fnFilterData(settings) { 4623 var columns = settings.aoColumns; 4624 var data = settings.aoData; 4625 var column; 4626 var j, jen, filterData, cellData, row; 4627 var wasInvalidated = false; 4628 4629 for (var rowIdx = 0; rowIdx < data.length; rowIdx++) { 4630 if (!data[rowIdx]) { 4631 continue; 4632 } 4633 4634 row = data[rowIdx]; 4635 4636 if (!row._aFilterData) { 4637 filterData = []; 4638 4639 for (j = 0, jen = columns.length; j < jen; j++) { 4640 column = columns[j]; 4641 4642 if (column.bSearchable) { 4643 cellData = _fnGetCellData(settings, rowIdx, j, 'filter'); 4644 4645 // Search in DataTables is string based 4646 if (cellData === null) { 4647 cellData = ''; 4648 } 4649 4650 if (typeof cellData !== 'string' && cellData.toString) { 4651 cellData = cellData.toString(); 4652 } 4653 } 4654 else { 4655 cellData = ''; 4656 } 4657 4658 // If it looks like there is an HTML entity in the string, 4659 // attempt to decode it so sorting works as expected. Note that 4660 // we could use a single line of jQuery to do this, but the DOM 4661 // method used here is much faster https://jsperf.com/html-decode 4662 if (cellData.indexOf && cellData.indexOf('&') !== -1) { 4663 __filter_div.innerHTML = cellData; 4664 cellData = __filter_div_textContent ? 4665 __filter_div.textContent : 4666 __filter_div.innerText; 4667 } 4668 4669 if (cellData.replace) { 4670 cellData = cellData.replace(/[\r\n\u2028]/g, ''); 4671 } 4672 4673 filterData.push(cellData); 4674 } 4675 4676 row._aFilterData = filterData; 4677 row._sFilterRow = filterData.join(' '); 4678 wasInvalidated = true; 4679 } 4680 } 4681 4682 return wasInvalidated; 4683 } 4684 4685 4686 /** 4687 * Draw the table for the first time, adding all required features 4688 * @param {object} settings dataTables settings object 4689 * @memberof DataTable#oApi 4690 */ 4691 function _fnInitialise(settings) { 4692 var i; 4693 var init = settings.oInit; 4694 var deferLoading = settings.deferLoading; 4695 var dataSrc = _fnDataSource(settings); 4696 4697 // Ensure that the table data is fully initialised 4698 if (!settings.bInitialised) { 4699 setTimeout(function () { _fnInitialise(settings); }, 200); 4700 return; 4701 } 4702 4703 // Build the header / footer for the table 4704 _fnBuildHead(settings, 'header'); 4705 _fnBuildHead(settings, 'footer'); 4706 4707 // Load the table's state (if needed) and then render around it and draw
4708 _fnLoadState(settings, init, function () { 4709 // Then draw the header / footer 4710 _fnDrawHead(settings, settings.aoHeader); 4711 _fnDrawHead(settings, settings.aoFooter); 4712 4713 // Cache the paging start point, as the first redraw will reset it 4714 var iAjaxStart = settings.iInitDisplayStart 4715 4716 // Local data load 4717 // Check if there is data passing into the constructor 4718 if (init.aaData) { 4719 for (i = 0; i < init.aaData.length; i++) { 4720 _fnAddData(settings, init.aaData[i]); 4721 } 4722 } 4723 else if (deferLoading || dataSrc == 'dom') { 4724 // Grab the data from the page 4725 _fnAddTr(settings, $(settings.nTBody).children('tr')); 4726 } 4727 4728 // Filter not yet applied - copy the display master 4729 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4730 4731 // Enable features 4732 _fnAddOptionsHtml(settings); 4733 _fnSortInit(settings); 4734 4735 _colGroup(settings); 4736 4737 /* Okay to show that something is going on now */ 4738 _fnProcessingDisplay(settings, true); 4739 4740 _fnCallbackFire(settings, null, 'preInit', [settings], true); 4741 4742 // If there is default sorting required - let's do it. The sort function 4743 // will do the drawing for us. Otherwise we draw the table regardless of the 4744 // Ajax source - this allows the table to look initialised for Ajax sourcing 4745 // data (show 'loading' message possibly) 4746 _fnReDraw(settings); 4747 4748 // Server-side processing init complete is done by _fnAjaxUpdateDraw 4749 if (dataSrc != 'ssp' || deferLoading) { 4750 // if there is an ajax source load the data 4751 if (dataSrc == 'ajax') { 4752 _fnBuildAjax(settings, {}, function (json) { 4753 var aData = _fnAjaxDataSrc(settings, json); 4754 4755 // Got the data - add it to the table 4756 for (i = 0; i < aData.length; i++) { 4757 _fnAddData(settings, aData[i]); 4758 } 4759 4760 // Reset the init display for cookie saving. We've already done 4761 // a filter, and therefore cleared it before. So we need to make 4762 // it appear 'fresh' 4763 settings.iInitDisplayStart = iAjaxStart; 4764 4765 _fnReDraw(settings); 4766 _fnProcessingDisplay(settings, false); 4767 _fnInitComplete(settings); 4768 }, settings); 4769 } 4770 else { 4771 _fnInitComplete(settings); 4772 _fnProcessingDisplay(settings, false); 4773 } 4774 } 4775 }); 4776 } 4777 4778 4779 /** 4780 * Draw the table for the first time, adding all required features 4781 * @param {object} settings dataTables settings object 4782 * @memberof DataTable#oApi 4783 */ 4784 function _fnInitComplete(settings) { 4785 if (settings._bInitComplete) { 4786 return; 4787 } 4788 4789 var args = [settings, settings.json]; 4790 4791 settings._bInitComplete = true; 4792 4793 // Table is fully set up and we have data, so calculate the 4794 // column widths 4795 _fnAdjustColumnSizing(settings); 4796 4797 _fnCallbackFire(settings, null, 'plugin-init', args, true); 4798 _fnCallbackFire(settings, 'aoInitComplete', 'init', args, true); 4799 } 4800 4801 function _fnLengthChange(settings, val) { 4802 var len = parseInt(val, 10); 4803 settings._iDisplayLength = len; 4804 4805 _fnLengthOverflow(settings); 4806 4807 // Fire length change event 4808 _fnCallbackFire(settings, null, 'length', [settings, len]); 4809 } 4810 4811 /** 4812 * Alter the display settings to change the page 4813 * @param {object} settings DataTables settings object 4814 * @param {string|int} action Paging action to take: "first", "previous", 4815 * "next" or "last" or page number to jump to (integer) 4816 * @param [bool] redraw Automatically draw the update or not 4817 * @returns {bool} true page has changed, false - no change 4818 * @memberof DataTable#oApi 4819 */
4820 function _fnPageChange(settings, action, redraw) { 4821 var 4822 start = settings._iDisplayStart, 4823 len = settings._iDisplayLength, 4824 records = settings.fnRecordsDisplay(); 4825 4826 if (records === 0 || len === -1) { 4827 start = 0; 4828 } 4829 else if (typeof action === "number") { 4830 start = action * len; 4831 4832 if (start > records) { 4833 start = 0; 4834 } 4835 } 4836 else if (action == "first") { 4837 start = 0; 4838 } 4839 else if (action == "previous") { 4840 start = len >= 0 ? 4841 start - len : 4842 0; 4843 4844 if (start < 0) { 4845 start = 0; 4846 } 4847 } 4848 else if (action == "next") { 4849 if (start + len < records) { 4850 start += len; 4851 } 4852 } 4853 else if (action == "last") { 4854 start = Math.floor((records - 1) / len) * len; 4855 } 4856 else if (action === 'ellipsis') { 4857 return; 4858 } 4859 else { 4860 _fnLog(settings, 0, "Unknown paging action: " + action, 5); 4861 } 4862 4863 var changed = settings._iDisplayStart !== start; 4864 settings._iDisplayStart = start; 4865 4866 _fnCallbackFire(settings, null, changed ? 'page' : 'page-nc', [settings]); 4867 4868 if (changed && redraw) { 4869 _fnDraw(settings); 4870 } 4871 4872 return changed; 4873 } 4874 4875 4876 /** 4877 * Generate the node required for the processing node 4878 * @param {object} settings DataTables settings object 4879 */
4880 function _processingHtml(settings) { 4881 var table = settings.nTable; 4882 var scrolling = settings.oScroll.sX !== '' || settings.oScroll.sY !== ''; 4883 4884 if (settings.oFeatures.bProcessing) { 4885 var n = $('<div/>', { 4886 'id': settings.sTableId + '_processing', 4887 'class': settings.oClasses.processing.container, 4888 'role': 'status' 4889 }) 4890 .html(settings.oLanguage.sProcessing) 4891 .append('<div><div></div><div></div><div></div><div></div></div>'); 4892 4893 // Different positioning depending on if scrolling is enabled or not 4894 if (scrolling) { 4895 n.prependTo($('div.dt-scroll', settings.nTableWrapper)); 4896 } 4897 else { 4898 n.insertBefore(table); 4899 } 4900 4901 $(table).on('processing.dt.DT', function (e, s, show) { 4902 n.css('display', show ? 'block' : 'none'); 4903 }); 4904 } 4905 } 4906 4907 4908 /** 4909 * Display or hide the processing indicator 4910 * @param {object} settings DataTables settings object 4911 * @param {bool} show Show the processing indicator (true) or not (false) 4912 */ 4913 function _fnProcessingDisplay(settings, show) { 4914 // Ignore cases when we are still redrawing 4915 if (settings.bDrawing && show === false) { 4916 return; 4917 } 4918 4919 _fnCallbackFire(settings, null, 'processing', [settings, show]); 4920 } 4921 4922 /** 4923 * Show the processing element if an action takes longer than a given time 4924 * 4925 * @param {*} settings DataTables settings object 4926 * @param {*} enable Do (true) or not (false) async processing (local feature enablement) 4927 * @param {*} run Function to run 4928 */ 4929 function _fnProcessingRun(settings, enable, run) { 4930 if (!enable) { 4931 // Immediate execution, synchronous 4932 run(); 4933 } 4934 else { 4935 _fnProcessingDisplay(settings, true); 4936 4937 // Allow the processing display to show if needed 4938 setTimeout(function () { 4939 run(); 4940 4941 _fnProcessingDisplay(settings, false); 4942 }, 0); 4943 } 4944 } 4945 /** 4946 * Add any control elements for the table - specifically scrolling 4947 * @param {object} settings dataTables settings object 4948 * @returns {node} Node to add to the DOM 4949 * @memberof DataTable#oApi 4950 */ 4951 function _fnFeatureHtmlTable(settings) { 4952 var table = $(settings.nTable); 4953 4954 // Scrolling from here on in 4955 var scroll = settings.oScroll; 4956 4957 if (scroll.sX === '' && scroll.sY === '') { 4958 return settings.nTable; 4959 } 4960 4961 var scrollX = scroll.sX; 4962 var scrollY = scroll.sY; 4963 var classes = settings.oClasses.scrolling; 4964 var caption = settings.captionNode; 4965 var captionSide = caption ? caption._captionSide : null; 4966 var headerClone = $(table[0].cloneNode(false)); 4967 var footerClone = $(table[0].cloneNode(false)); 4968 var footer = table.children('tfoot'); 4969 var _div = '<div/>'; 4970 var size = function (s) { 4971 return !s ? null : _fnStringToCss(s); 4972 }; 4973 4974 if (!footer.length) { 4975 footer = null; 4976 } 4977 4978 /* 4979 * The HTML structure that we want to generate in this function is: 4980 * div - scroller 4981 * div - scroll head 4982 * div - scroll head inner 4983 * table - scroll head table 4984 * thead - thead 4985 * div - scroll body 4986 * table - table (master table) 4987 * thead - thead clone for sizing 4988 * tbody - tbody 4989 * div - scroll foot 4990 * div - scroll foot inner 4991 * table - scroll foot table 4992 * tfoot - tfoot 4993 */ 4994 var scroller = $(_div, { 'class': classes.container }) 4995 .append( 4996 $(_div, { 'class': classes.header.self }) 4997 .css({ 4998 overflow: 'hidden', 4999 position: 'relative', 5000 border: 0, 5001 width: scrollX ? size(scrollX) : '100%' 5002 }) 5003 .append( 5004 $(_div, { 'class': classes.header.inner }) 5005 .css({ 5006 'box-sizing': 'content-box', 5007 width: scroll.sXInner || '100%' 5008 }) 5009 .append( 5010 headerClone 5011 .removeAttr('id') 5012 .css('margin-left', 0) 5013 .append(captionSide === 'top' ? caption : null) 5014 .append( 5015 table.children('thead') 5016 ) 5017 ) 5018 ) 5019 ) 5020 .append( 5021 $(_div, { 'class': classes.body }) 5022 .css({ 5023 position: 'relative', 5024 overflow: 'auto', 5025 width: size(scrollX) 5026 }) 5027 .append(table) 5028 ); 5029 5030 if (footer) { 5031 scroller.append( 5032 $(_div, { 'class': classes.footer.self }) 5033 .css({ 5034 overflow: 'hidden', 5035 border: 0, 5036 width: scrollX ? size(scrollX) : '100%' 5037 }) 5038 .append( 5039 $(_div, { 'class': classes.footer.inner }) 5040 .append( 5041 footerClone 5042 .removeAttr('id') 5043 .css('margin-left', 0) 5044 .append(captionSide === 'bottom' ? caption : null) 5045 .append( 5046 table.children('tfoot') 5047 ) 5048 ) 5049 ) 5050 ); 5051 } 5052 5053 var children = scroller.children(); 5054 var scrollHead = children[0]; 5055 var scrollBody = children[1]; 5056 var scrollFoot = footer ? children[2] : null; 5057 5058 // When the body is scrolled, then we also want to scroll the headers 5059 $(scrollBody).on('scroll.DT', function () { 5060 var scrollLeft = this.scrollLeft; 5061 5062 scrollHead.scrollLeft = scrollLeft; 5063 5064 if (footer) { 5065 scrollFoot.scrollLeft = scrollLeft; 5066 } 5067 }); 5068 5069 // When focus is put on the header cells, we might need to scroll the body 5070 $('th, td', scrollHead).on('focus', function () { 5071 var scrollLeft = scrollHead.scrollLeft; 5072 5073 scrollBody.scrollLeft = scrollLeft; 5074 5075 if (footer) { 5076 scrollBody.scrollLeft = scrollLeft; 5077 } 5078 }); 5079 5080 $(scrollBody).css('max-height', scrollY); 5081 if (!scroll.bCollapse) { 5082 $(scrollBody).css('height', scrollY); 5083 } 5084 5085 settings.nScrollHead = scrollHead; 5086 settings.nScrollBody = scrollBody; 5087 settings.nScrollFoot = scrollFoot; 5088 5089 // On redraw - align columns 5090 settings.aoDrawCallback.push(_fnScrollDraw); 5091 5092 return scroller[0]; 5093 } 5094 5095 5096 5097 /** 5098 * Update the header, footer and body tables for resizing - i.e. column 5099 * alignment. 5100 * 5101 * Welcome to the most horrible function DataTables. The process that this 5102 * function follows is basically: 5103 * 1. Re-create the table inside the scrolling div 5104 * 2. Correct colgroup > col values if needed 5105 * 3. Copy colgroup > col over to header and footer 5106 * 4. Clean up 5107 * 5108 * @param {object} settings dataTables settings object 5109 * @memberof DataTable#oApi 5110 */ 5111 function _fnScrollDraw(settings) { 5112 // Given that this is such a monster function, a lot of variables are use 5113 // to try and keep the minimised size as small as possible 5114 var 5115 scroll = settings.oScroll, 5116 barWidth = scroll.iBarWidth, 5117 divHeader = $(settings.nScrollHead), 5118 divHeaderInner = divHeader.children('div'), 5119 divHeaderTable = divHeaderInner.children('table'), 5120 divBodyEl = settings.nScrollBody,
5121 divBody = $(divBodyEl), 5122 divFooter = $(settings.nScrollFoot), 5123 divFooterInner = divFooter.children('div'), 5124 divFooterTable = divFooterInner.children('table'), 5125 header = $(settings.nTHead), 5126 table = $(settings.nTable), 5127 footer = settings.nTFoot && $('th, td', settings.nTFoot).length ? $(settings.nTFoot) : null, 5128 browser = settings.oBrowser, 5129 headerCopy, footerCopy; 5130 5131 // If the scrollbar visibility has changed from the last draw, we need to 5132 // adjust the column sizes as the table width will have changed to account 5133 // for the scrollbar 5134 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight; 5135 5136 if (settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined) { 5137 settings.scrollBarVis = scrollBarVis; 5138 _fnAdjustColumnSizing(settings); 5139 return; // adjust column sizing will call this function again 5140 } 5141 else { 5142 settings.scrollBarVis = scrollBarVis; 5143 } 5144 5145 // 1. Re-create the table inside the scrolling div 5146 // Remove the old minimised thead and tfoot elements in the inner table 5147 table.children('thead, tfoot').remove(); 5148 5149 // Clone the current header and footer elements and then place it into the inner table 5150 headerCopy = header.clone().prependTo(table); 5151 headerCopy.find('th, td').removeAttr('tabindex'); 5152 headerCopy.find('[id]').removeAttr('id'); 5153 5154 if (footer) { 5155 footerCopy = footer.clone().prependTo(table); 5156 footerCopy.find('[id]').removeAttr('id'); 5157 } 5158 5159 // 2. Correct colgroup > col values if needed 5160 // It is possible that the cell sizes are smaller than the content, so we need to 5161 // correct colgroup>col for such cases. This can happen if the auto width detection 5162 // uses a cell which has a longer string, but isn't the widest! For example 5163 // "Chief Executive Officer (CEO)" is the longest string in the demo, but 5164 // "Systems Administrator" is actually the widest string since it doesn't collapse. 5165 // Note the use of translating into a column index to get the `col` element. This 5166 // is because of Responsive which might remove `col` elements, knocking the alignment 5167 // of the indexes out. 5168 if (settings.aiDisplay.length) { 5169 // Get the column sizes from the first row in the table. This should really be a 5170 // [].find, but it wasn't supported in Chrome until Sept 2015, and DT has 10 year 5171 // browser support 5172 var firstTr = null; 5173 var start = _fnDataSource(settings) !== 'ssp' 5174 ? settings._iDisplayStart 5175 : 0; 5176 5177 for (i = start; i < start + settings.aiDisplay.length; i++) { 5178 var idx = settings.aiDisplay[i]; 5179 var tr = settings.aoData[idx].nTr; 5180 5181 if (tr) { 5182 firstTr = tr; 5183 break; 5184 } 5185 } 5186 5187 if (firstTr) { 5188 var colSizes = $(firstTr).children('th, td').map(function (vis) { 5189 return { 5190 idx: _fnVisibleToColumnIndex(settings, vis), 5191 width: $(this).outerWidth() 5192 }; 5193 }); 5194 5195 // Check against what the colgroup > col is set to and correct if needed 5196 for (var i = 0; i < colSizes.length; i++) { 5197 var colEl = settings.aoColumns[colSizes[i].idx].colEl[0]; 5198 var colWidth = colEl.style.width.replace('px', ''); 5199 5200 if (colWidth !== colSizes[i].width) { 5201 colEl.style.width = colSizes[i].width + 'px'; 5202 5203 if (scroll.sX) { 5204 colEl.style.minWidth = colSizes[i].width + 'px'; 5205 } 5206 } 5207 } 5208 } 5209 } 5210 5211 // 3. Copy the colgroup over to the header and footer 5212 divHeaderTable 5213 .find('colgroup') 5214 .remove(); 5215 5216 divHeaderTable.append(settings.colgroup.clone()); 5217 5218 if (footer) { 5219 divFooterTable 5220 .find('colgroup') 5221 .remove(); 5222 5223 divFooterTable.append(settings.colgroup.clone()); 5224 } 5225 5226 // "Hide" the header and footer that we used for the sizing. We need to keep 5227 // the content of the cell so that the width applied to the header and body 5228 // both match, but we want to hide it completely. 5229 $('th, td', headerCopy).each(function () { 5230 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5231 }); 5232 5233 if (footer) { 5234 $('th, td', footerCopy).each(function () { 5235 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5236 }); 5237 } 5238 5239 // 4. Clean up 5240 // Figure out if there are scrollbar present - if so then we need the header and footer to 5241 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) 5242 var isScrolling = Math.floor(table.height()) > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll"; 5243 var paddingSide = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right'); 5244 5245 // Set the width's of the header and footer tables 5246 var outerWidth = table.outerWidth(); 5247 5248 divHeaderTable.css('width', _fnStringToCss(outerWidth)); 5249 divHeaderInner 5250 .css('width', _fnStringToCss(outerWidth)) 5251 .css(paddingSide, isScrolling ? barWidth + "px" : "0px"); 5252 5253 if (footer) { 5254 divFooterTable.css('width', _fnStringToCss(outerWidth)); 5255 divFooterInner 5256 .css('width', _fnStringToCss(outerWidth)) 5257 .css(paddingSide, isScrolling ? barWidth + "px" : "0px"); 5258 } 5259 5260 // Correct DOM ordering for colgroup - comes before the thead 5261 table.children('colgroup').prependTo(table); 5262 5263 // Adjust the position of the header in case we loose the y-scrollbar 5264 divBody.trigger('scroll'); 5265 5266 // If sorting or filtering has occurred, jump the scrolling back to the top 5267 // only if we aren't holding the position
5268 if ((settings.bSorted || settings.bFiltered) && !settings._drawHold) { 5269 divBodyEl.scrollTop = 0; 5270 } 5271 } 5272 5273 /** 5274 * Calculate the width of columns for the table 5275 * @param {object} settings dataTables settings object 5276 * @memberof DataTable#oApi 5277 */ 5278 function _fnCalculateColumnWidths(settings) { 5279 // Not interested in doing column width calculation if auto-width is disabled 5280 if (!settings.oFeatures.bAutoWidth) { 5281 return; 5282 } 5283 5284 var 5285 table = settings.nTable, 5286 columns = settings.aoColumns, 5287 scroll = settings.oScroll, 5288 scrollY = scroll.sY, 5289 scrollX = scroll.sX, 5290 scrollXInner = scroll.sXInner, 5291 visibleColumns = _fnGetColumns(settings, 'bVisible'), 5292 tableWidthAttr = table.getAttribute('width'), // from DOM element 5293 tableContainer = table.parentNode, 5294 i, column, columnIdx; 5295 5296 var styleWidth = table.style.width; 5297 var containerWidth = _fnWrapperWidth(settings); 5298 5299 // Don't re-run for the same width as the last time 5300 if (containerWidth === settings.containerWidth) { 5301 return false; 5302 } 5303 5304 settings.containerWidth = containerWidth; 5305 5306 // If there is no width applied as a CSS style or as an attribute, we assume that 5307 // the width is intended to be 100%, which is usually is in CSS, but it is very 5308 // difficult to correctly parse the rules to get the final result. 5309 if (!styleWidth && !tableWidthAttr) { 5310 table.style.width = '100%'; 5311 styleWidth = '100%'; 5312 } 5313 5314 if (styleWidth && styleWidth.indexOf('%') !== -1) { 5315 tableWidthAttr = styleWidth; 5316 } 5317 5318 // Let plug-ins know that we are doing a recalc, in case they have changed any of the 5319 // visible columns their own way (e.g. Responsive uses display:none). 5320 _fnCallbackFire( 5321 settings, 5322 null, 5323 'column-calc', 5324 { visible: visibleColumns }, 5325 false 5326 ); 5327 5328 // Construct a single row, worst case, table with the widest 5329 // node in the data, assign any user defined widths, then insert it into 5330 // the DOM and allow the browser to do all the hard work of calculating 5331 // table widths 5332 var tmpTable = $(table.cloneNode()) 5333 .css('visibility', 'hidden') 5334 .removeAttr('id'); 5335 5336 // Clean up the table body 5337 tmpTable.append('<tbody/>') 5338 var tr = $('<tr/>').appendTo(tmpTable.find('tbody')); 5339 5340 // Clone the table header and footer - we can't use the header / footer 5341 // from the cloned table, since if scrolling is active, the table's 5342 // real header and footer are contained in different table tags 5343 tmpTable 5344 .append($(settings.nTHead).clone()) 5345 .append($(settings.nTFoot).clone()); 5346 5347 // Remove any assigned widths from the footer (from scrolling) 5348 tmpTable.find('tfoot th, tfoot td').css('width', ''); 5349 5350 // Apply custom sizing to the cloned header 5351 tmpTable.find('thead th, thead td').each(function () { 5352 // Get the `width` from the header layout 5353 var width = _fnColumnsSumWidth(settings, this, true, false); 5354 5355 if (width) { 5356 this.style.width = width; 5357 5358 // For scrollX we need to force the column width otherwise the 5359 // browser will collapse it. If this width is smaller than the 5360 // width the column requires, then it will have no effect 5361 if (scrollX) { 5362 this.style.minWidth = width; 5363 5364 $(this).append($('<div/>').css({ 5365 width: width, 5366 margin: 0, 5367 padding: 0, 5368 border: 0, 5369 height: 1 5370 })); 5371 } 5372 } 5373 else { 5374 this.style.width = ''; 5375 } 5376 }); 5377 5378 // Find the widest piece of data for each column and put it into the table 5379 for (i = 0; i < visibleColumns.length; i++) { 5380 columnIdx = visibleColumns[i]; 5381 column = columns[columnIdx]; 5382 5383 var longest = _fnGetMaxLenString(settings, columnIdx); 5384 var autoClass = _ext.type.className[column.sType]; 5385 var text = longest + column.sContentPadding; 5386 var insert = longest.indexOf('<') === -1 5387 ? document.createTextNode(text) 5388 : text 5389 5390 $('<td/>') 5391 .addClass(autoClass) 5392 .addClass(column.sClass) 5393 .append(insert) 5394 .appendTo(tr); 5395 } 5396 5397 // Tidy the temporary table - remove name attributes so there aren't 5398 // duplicated in the dom (radio elements for example) 5399 $('[name]', tmpTable).removeAttr('name'); 5400 5401 // Table has been built, attach to the document so we can work with it. 5402 // A holding element is used, positioned at the top of the container 5403 // with minimal height, so it has no effect on if the container scrolls 5404 // or not. Otherwise it might trigger scrolling when it actually isn't 5405 // needed
5406 var holder = $('<div/>').css(scrollX || scrollY ? 5407 { 5408 position: 'absolute', 5409 top: 0, 5410 left: 0, 5411 height: 1, 5412 right: 0, 5413 overflow: 'hidden' 5414 } : 5415 {} 5416 ) 5417 .append(tmpTable) 5418 .appendTo(tableContainer); 5419 5420 // When scrolling (X or Y) we want to set the width of the table as 5421 // appropriate. However, when not scrolling leave the table width as it 5422 // is. This results in slightly different, but I think correct behaviour 5423 if (scrollX && scrollXInner) { 5424 tmpTable.width(scrollXInner); 5425 } 5426 else if (scrollX) { 5427 tmpTable.css('width', 'auto'); 5428 tmpTable.removeAttr('width'); 5429 5430 // If there is no width attribute or style, then allow the table to 5431 // collapse 5432 if (tmpTable.outerWidth() < tableContainer.clientWidth && tableWidthAttr) { 5433 tmpTable.outerWidth(tableContainer.clientWidth); 5434 } 5435 } 5436 else if (scrollY) { 5437 tmpTable.outerWidth(tableContainer.clientWidth); 5438 } 5439 else if (tableWidthAttr) { 5440 tmpTable.outerWidth(tableWidthAttr); 5441 } 5442 5443 // Get the width of each column in the constructed table 5444 var total = 0; 5445 var bodyCells = tmpTable.find('tbody tr').eq(0).children(); 5446 5447 for (i = 0; i < visibleColumns.length; i++) { 5448 // Use getBounding for sub-pixel accuracy, which we then want to round up! 5449 var bounding = bodyCells[i].getBoundingClientRect().width; 5450 5451 // Total is tracked to remove any sub-pixel errors as the outerWidth 5452 // of the table might not equal the total given here 5453 total += bounding; 5454
5455 // Width for each column to use 5456 columns[visibleColumns[i]].sWidth = _fnStringToCss(bounding); 5457 } 5458 5459 table.style.width = _fnStringToCss(total); 5460 5461 // Finished with the table - ditch it 5462 holder.remove(); 5463 5464 // If there is a width attr, we want to attach an event listener which 5465 // allows the table sizing to automatically adjust when the window is 5466 // resized. Use the width attr rather than CSS, since we can't know if the 5467 // CSS is a relative value or absolute - DOM read is always px. 5468 if (tableWidthAttr) { 5469 table.style.width = _fnStringToCss(tableWidthAttr); 5470 } 5471 5472 if ((tableWidthAttr || scrollX) && !settings._reszEvt) { 5473 var resize = DataTable.util.throttle(function () { 5474 var newWidth = _fnWrapperWidth(settings); 5475 5476 // Don't do it if destroying or the container width is 0 5477 if (!settings.bDestroying && newWidth !== 0) { 5478 _fnAdjustColumnSizing(settings); 5479 } 5480 }); 5481 5482 // For browsers that support it (~2020 onwards for wide support) we can watch for the 5483 // container changing width. 5484 if (window.ResizeObserver) { 5485 // This is a tricky beast - if the element is visible when `.observe()` is called, 5486 // then the callback is immediately run. Which we don't want. If the element isn't 5487 // visible, then it isn't run, but we want it to run when it is then made visible. 5488 // This flag allows the above to be satisfied. 5489 var first = $(settings.nTableWrapper).is(':visible'); 5490 5491 // Use an empty div to attach the observer so it isn't impacted by height changes 5492 var resizer = $('<div>') 5493 .css({ 5494 width: '100%', 5495 height: 0 5496 }) 5497 .addClass('dt-autosize') 5498 .appendTo(settings.nTableWrapper); 5499 5500 settings.resizeObserver = new ResizeObserver(function (e) { 5501 if (first) { 5502 first = false; 5503 } 5504 else { 5505 resize(); 5506 } 5507 }); 5508 5509 settings.resizeObserver.observe(resizer[0]); 5510 } 5511 else { 5512 // For old browsers, the best we can do is listen for a window resize 5513 $(window).on('resize.DT-' + settings.sInstance, resize); 5514 } 5515 5516 settings._reszEvt = true; 5517 } 5518 } 5519 5520 /** 5521 * Get the width of the DataTables wrapper element 5522 * 5523 * @param {*} settings DataTables settings object 5524 * @returns Width 5525 */ 5526 function _fnWrapperWidth(settings) { 5527 return $(settings.nTableWrapper).is(':visible') 5528 ? $(settings.nTableWrapper).width() 5529 : 0; 5530 } 5531 5532 /** 5533 * Get the maximum strlen for each data column 5534 * @param {object} settings dataTables settings object 5535 * @param {int} colIdx column of interest 5536 * @returns {string} string of the max length 5537 * @memberof DataTable#oApi 5538 */ 5539 function _fnGetMaxLenString(settings, colIdx) { 5540 var column = settings.aoColumns[colIdx]; 5541 5542 if (!column.maxLenString) { 5543 var s, max = '', maxLen = -1; 5544 5545 for (var i = 0, iLen = settings.aiDisplayMaster.length; i < iLen; i++) { 5546 var rowIdx = settings.aiDisplayMaster[i]; 5547 var data = _fnGetRowDisplay(settings, rowIdx)[colIdx]; 5548 5549 var cellString = data && typeof data === 'object' && data.nodeType 5550 ? data.innerHTML 5551 : data + ''; 5552 5553 // Remove id / name attributes from elements so they 5554 // don't interfere with existing elements 5555 cellString = cellString 5556 .replace(/id=".*?"/g, '') 5557 .replace(/name=".*?"/g, ''); 5558 5559 s = _stripHtml(cellString) 5560 .replace(/ /g, ' '); 5561 5562 if (s.length > maxLen) { 5563 // We want the HTML in the string, but the length that 5564 // is important is the stripped string 5565 max = cellString; 5566 maxLen = s.length; 5567 } 5568 } 5569 5570 column.maxLenString = max; 5571 } 5572 5573 return column.maxLenString; 5574 } 5575 5576 5577 /** 5578 * Append a CSS unit (only if required) to a string 5579 * @param {string} value to css-ify 5580 * @returns {string} value with css unit 5581 * @memberof DataTable#oApi 5582 */ 5583 function _fnStringToCss(s) { 5584 if (s === null) { 5585 return '0px'; 5586 } 5587 5588 if (typeof s == 'number') { 5589 return s < 0 ? 5590 '0px' : 5591 s + 'px'; 5592 } 5593 5594 // Check it has a unit character already 5595 return s.match(/\d$/) ? 5596 s + 'px' : 5597 s; 5598 } 5599 5600 /** 5601 * Re-insert the `col` elements for current visibility 5602 * 5603 * @param {*} settings DT settings 5604 */ 5605 function _colGroup(settings) { 5606 var cols = settings.aoColumns; 5607 5608 settings.colgroup.empty(); 5609 5610 for (i = 0; i < cols.length; i++) { 5611 if (cols[i].bVisible) { 5612 settings.colgroup.append(cols[i].colEl); 5613 } 5614 } 5615 } 5616 5617 5618 function _fnSortInit(settings) { 5619 var target = settings.nTHead; 5620 var headerRows = target.querySelectorAll('tr'); 5621 var titleRow = settings.titleRow; 5622 var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])'; 5623 5624 // Legacy support for `orderCellsTop` 5625 if (titleRow === true) { 5626 target = headerRows[0]; 5627 } 5628 else if (titleRow === false) { 5629 target = headerRows[headerRows.length - 1]; 5630 } 5631 else if (titleRow !== null) { 5632 target = headerRows[titleRow]; 5633 } 5634 // else - all rows 5635 5636 if (settings.orderHandler) { 5637 _fnSortAttachListener( 5638 settings, 5639 target, 5640 target === settings.nTHead 5641 ? 'tr' + notSelector + ' th' + notSelector + ', tr' + notSelector + ' td' + notSelector 5642 : 'th' + notSelector + ', td' + notSelector 5643 ); 5644 } 5645 5646 // Need to resolve the user input array into our internal structure 5647 var order = []; 5648 _fnSortResolve(settings, order, settings.aaSorting); 5649 5650 settings.aaSorting = order; 5651 } 5652 5653 5654 function _fnSortAttachListener(settings, node, selector, column, callback) { 5655 _fnBindAction(node, selector, function (e) { 5656 var run = false;
5657 var columns = column === undefined 5658 ? _fnColumnsFromHeader(e.target) 5659 : typeof column === 'function' 5660 ? column() 5661 : Array.isArray(column) 5662 ? column 5663 : [column]; 5664 5665 if (columns.length) { 5666 for (var i = 0, iLen = columns.length; i < iLen; i++) { 5667 var ret = _fnSortAdd(settings, columns[i], i, e.shiftKey); 5668 5669 if (ret !== false) { 5670 run = true; 5671 } 5672 5673 // If the first entry is no sort, then subsequent 5674 // sort columns are ignored 5675 if (settings.aaSorting.length === 1 && settings.aaSorting[0][1] === '') { 5676 break; 5677 } 5678 } 5679 5680 if (run) { 5681 _fnProcessingRun(settings, true, function () { 5682 _fnSort(settings); 5683 _fnSortDisplay(settings, settings.aiDisplay); 5684 5685 _fnReDraw(settings, false, false); 5686 5687 if (callback) { 5688 callback(); 5689 } 5690 }); 5691 } 5692 } 5693 }); 5694 } 5695 5696 /** 5697 * Sort the display array to match the master's order 5698 * @param {*} settings 5699 */ 5700 function _fnSortDisplay(settings, display) { 5701 if (display.length < 2) { 5702 return; 5703 } 5704 5705 var master = settings.aiDisplayMaster; 5706 var masterMap = {}; 5707 var map = {}; 5708 var i; 5709 5710 // Rather than needing an `indexOf` on master array, we can create a map 5711 for (i = 0; i < master.length; i++) { 5712 masterMap[master[i]] = i; 5713 } 5714 5715 // And then cache what would be the indexOf from the display 5716 for (i = 0; i < display.length; i++) { 5717 map[display[i]] = masterMap[display[i]]; 5718 } 5719 5720 display.sort(function (a, b) { 5721 // Short version of this function is simply `master.indexOf(a) - master.indexOf(b);` 5722 return map[a] - map[b]; 5723 }); 5724 } 5725 5726 5727 function _fnSortResolve(settings, nestedSort, sort) { 5728 var push = function (a) { 5729 if ($.isPlainObject(a)) { 5730 if (a.idx !== undefined) { 5731 // Index based ordering 5732 nestedSort.push([a.idx, a.dir]); 5733 } 5734 else if (a.name) { 5735 // Name based ordering 5736 var cols = _pluck(settings.aoColumns, 'sName'); 5737 var idx = cols.indexOf(a.name); 5738 5739 if (idx !== -1) { 5740 nestedSort.push([idx, a.dir]); 5741 } 5742 } 5743 } 5744 else { 5745 // Plain column index and direction pair 5746 nestedSort.push(a); 5747 } 5748 }; 5749 5750 if ($.isPlainObject(sort)) { 5751 // Object 5752 push(sort); 5753 } 5754 else if (sort.length && typeof sort[0] === 'number') { 5755 // 1D array 5756 push(sort); 5757 } 5758 else if (sort.length) { 5759 // 2D array 5760 for (var z = 0; z < sort.length; z++) { 5761 push(sort[z]); // Object or array 5762 } 5763 } 5764 } 5765 5766 5767 function _fnSortFlatten(settings) { 5768 var 5769 i, k, kLen, 5770 aSort = [], 5771 extSort = DataTable.ext.type.order, 5772 aoColumns = settings.aoColumns, 5773 aDataSort, iCol, sType, srcCol, 5774 fixed = settings.aaSortingFixed, 5775 fixedObj = $.isPlainObject(fixed), 5776 nestedSort = []; 5777 5778 if (!settings.oFeatures.bSort) { 5779 return aSort; 5780 } 5781 5782 // Build the sort array, with pre-fix and post-fix options if they have been 5783 // specified 5784 if (Array.isArray(fixed)) { 5785 _fnSortResolve(settings, nestedSort, fixed); 5786 } 5787 5788 if (fixedObj && fixed.pre) { 5789 _fnSortResolve(settings, nestedSort, fixed.pre); 5790 } 5791 5792 _fnSortResolve(settings, nestedSort, settings.aaSorting); 5793 5794 if (fixedObj && fixed.post) { 5795 _fnSortResolve(settings, nestedSort, fixed.post); 5796 } 5797 5798 for (i = 0; i < nestedSort.length; i++) { 5799 srcCol = nestedSort[i][0]; 5800 5801 if (aoColumns[srcCol]) { 5802 aDataSort = aoColumns[srcCol].aDataSort; 5803 5804 for (k = 0, kLen = aDataSort.length; k < kLen; k++) { 5805 iCol = aDataSort[k]; 5806 sType = aoColumns[iCol].sType || 'string'; 5807 5808 if (nestedSort[i]._idx === undefined) { 5809 nestedSort[i]._idx = aoColumns[iCol].asSorting.indexOf(nestedSort[i][1]); 5810 } 5811 5812 if (nestedSort[i][1]) { 5813 aSort.push({ 5814 src: srcCol, 5815 col: iCol, 5816 dir: nestedSort[i][1], 5817 index: nestedSort[i]._idx, 5818 type: sType, 5819 formatter: extSort[sType + "-pre"], 5820 sorter: extSort[sType + "-" + nestedSort[i][1]] 5821 }); 5822 } 5823 } 5824 } 5825 } 5826 5827 return aSort; 5828 } 5829 5830 /** 5831 * Change the order of the table 5832 * @param {object} oSettings dataTables settings object 5833 * @memberof DataTable#oApi 5834 */ 5835 function _fnSort(oSettings, col, dir) { 5836 var 5837 i, iLen, 5838 aiOrig = [], 5839 extSort = DataTable.ext.type.order, 5840 aoData = oSettings.aoData, 5841 sortCol, 5842 displayMaster = oSettings.aiDisplayMaster, 5843 aSort; 5844 5845 // Make sure the columns all have types defined 5846 _fnColumnTypes(oSettings); 5847 5848 // Allow a specific column to be sorted, which will _not_ alter the display 5849 // master 5850 if (col !== undefined) { 5851 var srcCol = oSettings.aoColumns[col]; 5852 5853 aSort = [{ 5854 src: col, 5855 col: col, 5856 dir: dir, 5857 index: 0, 5858 type: srcCol.sType, 5859 formatter: extSort[srcCol.sType + "-pre"], 5860 sorter: extSort[srcCol.sType + "-" + dir] 5861 }]; 5862 displayMaster = displayMaster.slice(); 5863 } 5864 else { 5865 aSort = _fnSortFlatten(oSettings); 5866 } 5867 5868 for (i = 0, iLen = aSort.length; i < iLen; i++) { 5869 sortCol = aSort[i]; 5870 5871 // Load the data needed for the sort, for each cell
5872 _fnSortData(oSettings, sortCol.col); 5873 } 5874 5875 /* No sorting required if server-side or no sorting array */ 5876 if (_fnDataSource(oSettings) != 'ssp' && aSort.length !== 0) { 5877 // Reset the initial positions on each pass so we get a stable sort 5878 for (i = 0, iLen = displayMaster.length; i < iLen; i++) { 5879 aiOrig[i] = i; 5880 } 5881 5882 // If the first sort is desc, then reverse the array to preserve original 5883 // order, just in reverse 5884 if (aSort.length && aSort[0].dir === 'desc' && oSettings.orderDescReverse) { 5885 aiOrig.reverse(); 5886 } 5887 5888 /* Do the sort - here we want multi-column sorting based on a given data source (column) 5889 * and sorting function (from oSort) in a certain direction. It's reasonably complex to 5890 * follow on its own, but this is what we want (example two column sorting): 5891 * fnLocalSorting = function(a,b){ 5892 * var test; 5893 * test = oSort['string-asc']('data11', 'data12'); 5894 * if (test !== 0) 5895 * return test; 5896 * test = oSort['numeric-desc']('data21', 'data22'); 5897 * if (test !== 0) 5898 * return test; 5899 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); 5900 * } 5901 * Basically we have a test for each sorting column, if the data in that column is equal, 5902 * test the next column. If all columns match, then we use a numeric sort on the row 5903 * positions in the original data array to provide a stable sort. 5904 */ 5905 displayMaster.sort(function (a, b) { 5906 var 5907 x, y, k, test, sort, 5908 len = aSort.length, 5909 dataA = aoData[a]._aSortData, 5910 dataB = aoData[b]._aSortData; 5911 5912 for (k = 0; k < len; k++) { 5913 sort = aSort[k]; 5914 5915 // Data, which may have already been through a `-pre` function 5916 x = dataA[sort.col]; 5917 y = dataB[sort.col]; 5918 5919 if (sort.sorter) { 5920 // If there is a custom sorter (`-asc` or `-desc`) for this 5921 // data type, use it 5922 test = sort.sorter(x, y); 5923 5924 if (test !== 0) { 5925 return test; 5926 } 5927 } 5928 else { 5929 // Otherwise, use generic sorting 5930 test = x < y ? -1 : x > y ? 1 : 0; 5931 5932 if (test !== 0) { 5933 return sort.dir === 'asc' ? test : -test; 5934 } 5935 } 5936 } 5937 5938 x = aiOrig[a]; 5939 y = aiOrig[b]; 5940 5941 return x < y ? -1 : x > y ? 1 : 0; 5942 }); 5943 } 5944 else if (aSort.length === 0) { 5945 // Apply index order 5946 displayMaster.sort(function (x, y) { 5947 return x < y ? -1 : x > y ? 1 : 0; 5948 }); 5949 } 5950 5951 if (col === undefined) { 5952 // Tell the draw function that we have sorted the data 5953 oSettings.bSorted = true; 5954 oSettings.sortDetails = aSort; 5955 5956 _fnCallbackFire(oSettings, null, 'order', [oSettings, aSort]); 5957 } 5958 5959 return displayMaster; 5960 } 5961 5962 5963 /** 5964 * Function to run on user sort request 5965 * @param {object} settings dataTables settings object 5966 * @param {node} attachTo node to attach the handler to 5967 * @param {int} colIdx column sorting index 5968 * @param {int} addIndex Counter 5969 * @param {boolean} [shift=false] Shift click add 5970 * @param {function} [callback] callback function 5971 * @memberof DataTable#oApi 5972 */ 5973 function _fnSortAdd(settings, colIdx, addIndex, shift) { 5974 var col = settings.aoColumns[colIdx]; 5975 var sorting = settings.aaSorting; 5976 var asSorting = col.asSorting; 5977 var nextSortIdx; 5978 var next = function (a, overflow) { 5979 var idx = a._idx; 5980 if (idx === undefined) { 5981 idx = asSorting.indexOf(a[1]); 5982 } 5983 5984 return idx + 1 < asSorting.length ? 5985 idx + 1 : 5986 overflow ? 5987 null : 5988 0; 5989 }; 5990 5991 if (!col.bSortable) { 5992 return false;
5993 } 5994 5995 // Convert to 2D array if needed 5996 if (typeof sorting[0] === 'number') { 5997 sorting = settings.aaSorting = [sorting]; 5998 } 5999 6000 // If appending the sort then we are multi-column sorting 6001 if ((shift || addIndex) && settings.oFeatures.bSortMulti) { 6002 // Are we already doing some kind of sort on this column? 6003 var sortIdx = _pluck(sorting, '0').indexOf(colIdx); 6004 6005 if (sortIdx !== -1) { 6006 // Yes, modify the sort 6007 nextSortIdx = next(sorting[sortIdx], true); 6008 6009 if (nextSortIdx === null && sorting.length === 1) { 6010 nextSortIdx = 0; // can't remove sorting completely 6011 } 6012 6013 if (nextSortIdx === null || asSorting[nextSortIdx] === '') { 6014 sorting.splice(sortIdx, 1); 6015 } 6016 else { 6017 sorting[sortIdx][1] = asSorting[nextSortIdx]; 6018 sorting[sortIdx]._idx = nextSortIdx; 6019 } 6020 } 6021 else if (shift) { 6022 // No sort on this column yet, being added by shift click 6023 // add it as itself 6024 sorting.push([colIdx, asSorting[0], 0]); 6025 sorting[sorting.length - 1]._idx = 0; 6026 } 6027 else { 6028 // No sort on this column yet, being added from a colspan 6029 // so add with same direction as first column 6030 sorting.push([colIdx, sorting[0][1], 0]); 6031 sorting[sorting.length - 1]._idx = 0; 6032 } 6033 } 6034 else if (sorting.length && sorting[0][0] == colIdx) { 6035 // Single column - already sorting on this column, modify the sort 6036 nextSortIdx = next(sorting[0]); 6037 6038 sorting.length = 1; 6039 sorting[0][1] = asSorting[nextSortIdx]; 6040 sorting[0]._idx = nextSortIdx; 6041 } 6042 else { 6043 // Single column - sort only on this column 6044 sorting.length = 0; 6045 sorting.push([colIdx, asSorting[0]]); 6046 sorting[0]._idx = 0; 6047 } 6048 } 6049 6050 6051 /** 6052 * Set the sorting classes on table's body, Note: it is safe to call this function 6053 * when bSort and bSortClasses are false 6054 * @param {object} oSettings dataTables settings object 6055 * @memberof DataTable#oApi 6056 */ 6057 function _fnSortingClasses(settings) { 6058 var oldSort = settings.aLastSort; 6059 var sortClass = settings.oClasses.order.position; 6060 var sort = _fnSortFlatten(settings); 6061 var features = settings.oFeatures; 6062 var i, iLen, colIdx; 6063 6064 if (features.bSort && features.bSortClasses) { 6065 // Remove old sorting classes 6066 for (i = 0, iLen = oldSort.length; i < iLen; i++) { 6067 colIdx = oldSort[i].src; 6068 6069 // Remove column sorting 6070 $(_pluck(settings.aoData, 'anCells', colIdx)) 6071 .removeClass(sortClass + (i < 2 ? i + 1 : 3)); 6072 } 6073 6074 // Add new column sorting 6075 for (i = 0, iLen = sort.length; i < iLen; i++) { 6076 colIdx = sort[i].src; 6077 6078 $(_pluck(settings.aoData, 'anCells', colIdx)) 6079 .addClass(sortClass + (i < 2 ? i + 1 : 3)); 6080 } 6081 } 6082 6083 settings.aLastSort = sort; 6084 } 6085 6086 6087 // Get the data to sort a column, be it from cache, fresh (populating the 6088 // cache), or from a sort formatter 6089 function _fnSortData(settings, colIdx) { 6090 // Custom sorting function - provided by the sort data type 6091 var column = settings.aoColumns[colIdx]; 6092 var customSort = DataTable.ext.order[column.sSortDataType]; 6093 var customData; 6094 6095 if (customSort) { 6096 customData = customSort.call(settings.oInstance, settings, colIdx, 6097 _fnColumnIndexToVisible(settings, colIdx) 6098 ); 6099 } 6100 6101 // Use / populate cache 6102 var row, cellData; 6103 var formatter = DataTable.ext.type.order[column.sType + "-pre"]; 6104 var data = settings.aoData; 6105 6106 for (var rowIdx = 0; rowIdx < data.length; rowIdx++) { 6107 // Sparse array 6108 if (!data[rowIdx]) { 6109 continue; 6110 } 6111 6112 row = data[rowIdx]; 6113 6114 if (!row._aSortData) { 6115 row._aSortData = []; 6116 } 6117 6118 if (!row._aSortData[colIdx] || customSort) { 6119 cellData = customSort ? 6120 customData[rowIdx] : // If there was a custom sort function, use data from there 6121 _fnGetCellData(settings, rowIdx, colIdx, 'sort'); 6122 6123 row._aSortData[colIdx] = formatter ? 6124 formatter(cellData, settings) : 6125 cellData; 6126 } 6127 } 6128 } 6129 6130 6131 /** 6132 * State information for a table 6133 * 6134 * @param {*} settings 6135 * @returns State object 6136 */
6137 function _fnSaveState(settings) { 6138 if (settings._bLoadingState) { 6139 return; 6140 } 6141 6142 // Sort state saving uses [[idx, order]] structure. 6143 var sorting = []; 6144 _fnSortResolve(settings, sorting, settings.aaSorting); 6145 6146 /* Store the interesting variables */ 6147 var columns = settings.aoColumns; 6148 var state = { 6149 time: +new Date(), 6150 start: settings._iDisplayStart, 6151 length: settings._iDisplayLength, 6152 order: sorting.map(function (sort) { 6153 // If a column name is available, use it 6154 return columns[sort[0]] && columns[sort[0]].sName 6155 ? [columns[sort[0]].sName, sort[1]] 6156 : sort.slice(); 6157 }), 6158 search: $.extend({}, settings.oPreviousSearch), 6159 columns: settings.aoColumns.map(function (col, i) { 6160 return { 6161 name: col.sName, 6162 visible: col.bVisible, 6163 search: $.extend({}, settings.aoPreSearchCols[i]) 6164 }; 6165 }) 6166 }; 6167 6168 settings.oSavedState = state; 6169 _fnCallbackFire(settings, "aoStateSaveParams", 'stateSaveParams', [settings, state]); 6170 6171 if (settings.oFeatures.bStateSave && !settings.bDestroying) { 6172 settings.fnStateSaveCallback.call(settings.oInstance, settings, state); 6173 } 6174 } 6175 6176 6177 /** 6178 * Attempt to load a saved table state 6179 * @param {object} oSettings dataTables settings object 6180 * @param {object} oInit DataTables init object so we can override settings 6181 * @param {function} callback Callback to execute when the state has been loaded 6182 * @memberof DataTable#oApi 6183 */ 6184 function _fnLoadState(settings, init, callback) { 6185 if (!settings.oFeatures.bStateSave) { 6186 callback(); 6187 return; 6188 } 6189 6190 var loaded = function (state) { 6191 _fnImplementState(settings, state, callback); 6192 } 6193 6194 var state = settings.fnStateLoadCallback.call(settings.oInstance, settings, loaded); 6195 6196 if (state !== undefined) { 6197 _fnImplementState(settings, state, callback); 6198 } 6199 // otherwise, wait for the loaded callback to be executed 6200 6201 return true; 6202 } 6203 6204 function _fnImplementState(settings, s, callback) { 6205 var i, iLen; 6206 var columns = settings.aoColumns; 6207 var currentNames = _pluck(settings.aoColumns, 'sName'); 6208 6209 settings._bLoadingState = true; 6210 6211 // When StateRestore was introduced the state could now be implemented at any time 6212 // Not just initialisation. To do this an api instance is required in some places 6213 var api = settings._bInitComplete ? new DataTable.Api(settings) : null; 6214 6215 if (!s || !s.time) { 6216 settings._bLoadingState = false; 6217 callback(); 6218 return; 6219 } 6220 6221 // Reject old data 6222 var duration = settings.iStateDuration; 6223 if (duration > 0 && s.time < +new Date() - (duration * 1000)) { 6224 settings._bLoadingState = false; 6225 callback(); 6226 return; 6227 } 6228 6229 // Allow custom and plug-in manipulation functions to alter the saved data set and 6230 // cancelling of loading by returning false 6231 var abStateLoad = _fnCallbackFire(settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s]); 6232 if (abStateLoad.indexOf(false) !== -1) { 6233 settings._bLoadingState = false; 6234 callback(); 6235 return; 6236 } 6237 6238 // Store the saved state so it might be accessed at any time 6239 settings.oLoadedState = $.extend(true, {}, s); 6240 6241 // This is needed for ColReorder, which has to happen first to allow all 6242 // the stored indexes to be usable. It is not publicly documented. 6243 _fnCallbackFire(settings, null, 'stateLoadInit', [settings, s], true); 6244 6245 // Page Length 6246 if (s.length !== undefined) { 6247 // If already initialised just set the value directly so that the select element is also updated 6248 if (api) { 6249 api.page.len(s.length) 6250 } 6251 else { 6252 settings._iDisplayLength = s.length; 6253 } 6254 } 6255 6256 // Restore key features 6257 if (s.start !== undefined) { 6258 if (api === null) { 6259 settings._iDisplayStart = s.start; 6260 settings.iInitDisplayStart = s.start; 6261 } 6262 else {
6263 _fnPageChange(settings, s.start / settings._iDisplayLength); 6264 } 6265 } 6266 6267 // Order 6268 if (s.order !== undefined) { 6269 settings.aaSorting = []; 6270 $.each(s.order, function (i, col) { 6271 var set = [col[0], col[1]]; 6272 6273 // A column name was stored and should be used for restore 6274 if (typeof col[0] === 'string') { 6275 // Find the name from the current list of column names 6276 var idx = currentNames.indexOf(col[0]); 6277 6278 if (idx < 0) { 6279 // If the column was not found ignore it and continue 6280 return; 6281 } 6282 6283 set[0] = idx; 6284 } 6285 else if (set[0] >= columns.length) { 6286 // If the column index is out of bounds ignore it and continue 6287 return; 6288 } 6289 6290 settings.aaSorting.push(set); 6291 }); 6292 } 6293 6294 // Search 6295 if (s.search !== undefined) { 6296 $.extend(settings.oPreviousSearch, s.search); 6297 } 6298 6299 // Columns 6300 if (s.columns) { 6301 var set = s.columns; 6302 var incoming = _pluck(s.columns, 'name'); 6303 6304 // Check if it is a 2.2 style state object with a `name` property for the columns, and if 6305 // the name was defined. If so, then create a new array that will map the state object 6306 // given, to the current columns (don't bother if they are already matching tho). 6307 if (incoming.join('').length && incoming.join('') !== currentNames.join('')) { 6308 set = []; 6309 6310 // For each column, try to find the name in the incoming array 6311 for (i = 0; i < currentNames.length; i++) { 6312 if (currentNames[i] != '') { 6313 var idx = incoming.indexOf(currentNames[i]); 6314 6315 if (idx >= 0) { 6316 set.push(s.columns[idx]); 6317 } 6318 else { 6319 // No matching column name in the state's columns, so this might be a new 6320 // column and thus can't have a state already. 6321 set.push({}); 6322 } 6323 } 6324 else { 6325 // If no name, but other columns did have a name, then there is no knowing 6326 // where this one came from originally so it can't be restored. 6327 set.push({}); 6328 } 6329 } 6330 } 6331 6332 // If the number of columns to restore is different from current, then all bets are off. 6333 if (set.length === columns.length) { 6334 for (i = 0, iLen = set.length; i < iLen; i++) { 6335 var col = set[i]; 6336 6337 // Visibility 6338 if (col.visible !== undefined) { 6339 // If the api is defined, the table has been initialised so we need to use it rather than internal settings 6340 if (api) { 6341 // Don't redraw the columns on every iteration of this loop, we will do this at the end instead 6342 api.column(i).visible(col.visible, false); 6343 } 6344 else { 6345 columns[i].bVisible = col.visible; 6346 } 6347 } 6348 6349 // Search 6350 if (col.search !== undefined) { 6351 $.extend(settings.aoPreSearchCols[i], col.search); 6352 } 6353 } 6354 6355 // If the api is defined then we need to adjust the columns once the visibility has been changed 6356 if (api) { 6357 api.one('draw', function () { 6358 api.columns.adjust(); 6359 }); 6360 } 6361 } 6362 } 6363 6364 settings._bLoadingState = false; 6365 _fnCallbackFire(settings, 'aoStateLoaded', 'stateLoaded', [settings, s]); 6366 callback(); 6367 } 6368 6369 /** 6370 * Log an error message 6371 * @param {object} settings dataTables settings object 6372 * @param {int} level log error messages, or display them to the user 6373 * @param {string} msg error message 6374 * @param {int} tn Technical note id to get more information about the error. 6375 * @memberof DataTable#oApi 6376 */ 6377 function _fnLog(settings, level, msg, tn) { 6378 msg = 'DataTables warning: ' +
vendor: 4,620 bytes, lines 6379-6509
6379 (settings ? 'table id=' + settings.sTableId + ' - ' : '') + msg; 6380 6381 if (tn) { 6382 msg += '. For more information about this error, please see ' + 6383 'https://datatables.net/tn/' + tn; 6384 } 6385 6386 if (!level) { 6387 // Backwards compatibility pre 1.10 6388 var ext = DataTable.ext; 6389 var type = ext.sErrMode || ext.errMode; 6390 6391 if (settings) { 6392 _fnCallbackFire(settings, null, 'dt-error', [settings, tn, msg], true); 6393 } 6394 6395 if (type == 'alert') { 6396 alert(msg); 6397 } 6398 else if (type == 'throw') { 6399 throw new Error(msg); 6400 } 6401 else if (typeof type == 'function') { 6402 type(settings, tn, msg); 6403 } 6404 } 6405 else if (window.console && console.log) { 6406 console.log(msg); 6407 } 6408 } 6409 6410 6411 /** 6412 * See if a property is defined on one object, if so assign it to the other object 6413 * @param {object} ret target object 6414 * @param {object} src source object 6415 * @param {string} name property 6416 * @param {string} [mappedName] name to map too - optional, name used if not given 6417 * @memberof DataTable#oApi 6418 */ 6419 function _fnMap(ret, src, name, mappedName) { 6420 if (Array.isArray(name)) { 6421 $.each(name, function (i, val) { 6422 if (Array.isArray(val)) { 6423 _fnMap(ret, src, val[0], val[1]); 6424 } 6425 else { 6426 _fnMap(ret, src, val); 6427 } 6428 }); 6429 6430 return; 6431 } 6432 6433 if (mappedName === undefined) { 6434 mappedName = name; 6435 } 6436 6437 if (src[name] !== undefined) { 6438 ret[mappedName] = src[name]; 6439 } 6440 } 6441 6442 6443 /** 6444 * Extend objects - very similar to jQuery.extend, but deep copy objects, and 6445 * shallow copy arrays. The reason we need to do this, is that we don't want to 6446 * deep copy array init values (such as aaSorting) since the dev wouldn't be 6447 * able to override them, but we do want to deep copy arrays. 6448 * @param {object} out Object to extend 6449 * @param {object} extender Object from which the properties will be applied to 6450 * out 6451 * @param {boolean} breakRefs If true, then arrays will be sliced to take an 6452 * independent copy with the exception of the `data` or `aaData` parameters 6453 * if they are present. This is so you can pass in a collection to 6454 * DataTables and have that used as your data source without breaking the 6455 * references 6456 * @returns {object} out Reference, just for convenience - out === the return. 6457 * @memberof DataTable#oApi 6458 * @todo This doesn't take account of arrays inside the deep copied objects. 6459 */ 6460 function _fnExtend(out, extender, breakRefs) { 6461 var val; 6462 6463 for (var prop in extender) { 6464 if (Object.prototype.hasOwnProperty.call(extender, prop)) { 6465 val = extender[prop]; 6466 6467 if ($.isPlainObject(val)) { 6468 if (!$.isPlainObject(out[prop])) { 6469 out[prop] = {}; 6470 } 6471 $.extend(true, out[prop], val); 6472 } 6473 else if (breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val)) { 6474 out[prop] = val.slice(); 6475 } 6476 else { 6477 out[prop] = val; 6478 } 6479 } 6480 } 6481 6482 return out; 6483 } 6484 6485 6486 /** 6487 * Bind an event handler to allow a click or return key to activate the callback. 6488 * This is good for accessibility since a return on the keyboard will have the 6489 * same effect as a click, if the element has focus. 6490 * @param {element} n Element to bind the action to 6491 * @param {object|string} selector Selector (for delegated events) or data object 6492 * to pass to the triggered function 6493 * @param {function} fn Callback function for when the event is triggered 6494 * @memberof DataTable#oApi 6495 */ 6496 function _fnBindAction(n, selector, fn) { 6497 $(n) 6498 .on('click.DT', selector, function (e) { 6499 fn(e); 6500 }) 6501 .on('keypress.DT', selector, function (e) { 6502 if (e.which === 13) { 6503 e.preventDefault(); 6504 fn(e); 6505 } 6506 }) 6507 .on('selectstart.DT', selector, function () { 6508 // Don't want a double click resulting in text selection 6509 return false;
6510 }); 6511 } 6512 6513 6514 /** 6515 * Register a callback function. Easily allows a callback function to be added to 6516 * an array store of callback functions that can then all be called together. 6517 * @param {object} settings dataTables settings object 6518 * @param {string} store Name of the array storage for the callbacks in oSettings 6519 * @param {function} fn Function to be called back 6520 * @memberof DataTable#oApi 6521 */ 6522 function _fnCallbackReg(settings, store, fn) { 6523 if (fn) { 6524 settings[store].push(fn); 6525 } 6526 } 6527 6528 6529 /** 6530 * Fire callback functions and trigger events. Note that the loop over the 6531 * callback array store is done backwards! Further note that you do not want to 6532 * fire off triggers in time sensitive applications (for example cell creation) 6533 * as its slow. 6534 * @param {object} settings dataTables settings object 6535 * @param {string} callbackArr Name of the array storage for the callbacks in 6536 * oSettings 6537 * @param {string} eventName Name of the jQuery custom event to trigger. If 6538 * null no trigger is fired 6539 * @param {array} args Array of arguments to pass to the callback function / 6540 * trigger 6541 * @param {boolean} [bubbles] True if the event should bubble 6542 * @memberof DataTable#oApi 6543 */ 6544 function _fnCallbackFire(settings, callbackArr, eventName, args, bubbles) { 6545 var ret = []; 6546 6547 if (callbackArr) { 6548 ret = settings[callbackArr].slice().reverse().map(function (val) { 6549 return val.apply(settings.oInstance, args); 6550 }); 6551 } 6552 6553 if (eventName !== null) { 6554 var e = $.Event(eventName + '.dt'); 6555 var table = $(settings.nTable); 6556 6557 // Expose the DataTables API on the event object for easy access 6558 e.dt = settings.api; 6559 6560 table[bubbles ? 'trigger' : 'triggerHandler'](e, args); 6561 6562 // If not yet attached to the document, trigger the event 6563 // on the body directly to sort of simulate the bubble 6564 if (bubbles && table.parents('body').length === 0) { 6565 $('body').trigger(e, args); 6566 } 6567 6568 ret.push(e.result); 6569 } 6570 6571 return ret; 6572 } 6573 6574 6575 function _fnLengthOverflow(settings) { 6576 var 6577 start = settings._iDisplayStart, 6578 end = settings.fnDisplayEnd(), 6579 len = settings._iDisplayLength; 6580 6581 /* If we have space to show extra rows (backing up from the end point - then do so */ 6582 if (start >= end) { 6583 start = end - len; 6584 } 6585 6586 // Keep the start record on the current page 6587 start -= (start % len); 6588 6589 if (len === -1 || start < 0) { 6590 start = 0; 6591 } 6592 6593 settings._iDisplayStart = start; 6594 } 6595 6596 6597 function _fnRenderer(settings, type) { 6598 var renderer = settings.renderer; 6599 var host = DataTable.ext.renderer[type]; 6600 6601 if ($.isPlainObject(renderer) && renderer[type]) { 6602 // Specific renderer for this type. If available use it, otherwise use 6603 // the default. 6604 return host[renderer[type]] || host._; 6605 } 6606 else if (typeof renderer === 'string') { 6607 // Common renderer - if there is one available for this type use it, 6608 // otherwise use the default 6609 return host[renderer] || host._; 6610 } 6611 6612 // Use the default 6613 return host._; 6614 } 6615 6616 6617 /** 6618 * Detect the data source being used for the table. Used to simplify the code 6619 * a little (ajax) and to make it compress a little smaller. 6620 * 6621 * @param {object} settings dataTables settings object 6622 * @returns {string} Data source 6623 * @memberof DataTable#oApi 6624 */ 6625 function _fnDataSource(settings) { 6626 if (settings.oFeatures.bServerSide) { 6627 return 'ssp'; 6628 } 6629 else if (settings.ajax) { 6630 return 'ajax'; 6631 } 6632 return 'dom'; 6633 } 6634 6635 /** 6636 * Common replacement for language strings 6637 * 6638 * @param {*} settings DT settings object 6639 * @param {*} str String with values to replace 6640 * @param {*} entries Plural number for _ENTRIES_ - can be undefined 6641 * @returns String 6642 */ 6643 function _fnMacros(settings, str, entries) { 6644 // When infinite scrolling, we are always starting at 1. _iDisplayStart is 6645 // used only internally 6646 var 6647 formatter = settings.fnFormatNumber, 6648 start = settings._iDisplayStart + 1, 6649 len = settings._iDisplayLength, 6650 vis = settings.fnRecordsDisplay(), 6651 max = settings.fnRecordsTotal(), 6652 all = len === -1; 6653 6654 return str. 6655 replace(/_START_/g, formatter.call(settings, start)). 6656 replace(/_END_/g, formatter.call(settings, settings.fnDisplayEnd())). 6657 replace(/_MAX_/g, formatter.call(settings, max)). 6658 replace(/_TOTAL_/g, formatter.call(settings, vis)). 6659 replace(/_PAGE_/g, formatter.call(settings, all ? 1 : Math.ceil(start / len))). 6660 replace(/_PAGES_/g, formatter.call(settings, all ? 1 : Math.ceil(vis / len))). 6661 replace(/_ENTRIES_/g, settings.api.i18n('entries', '', entries)). 6662 replace(/_ENTRIES-MAX_/g, settings.api.i18n('entries', '', max)). 6663 replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis)); 6664 } 6665 6666 /** 6667 * Add elements to an array as quickly as possible, but stack safe. 6668 * 6669 * @param {*} arr Array to add the data to 6670 * @param {*} data Data array that is to be added 6671 * @returns 6672 */ 6673 function _fnArrayApply(arr, data) { 6674 if (!data) { 6675 return; 6676 } 6677 6678 // Chrome can throw a max stack error if apply is called with 6679 // too large an array, but apply is faster. 6680 if (data.length < 10000) { 6681 arr.push.apply(arr, data); 6682 } 6683 else { 6684 for (i = 0; i < data.length; i++) { 6685 arr.push(data[i]); 6686 } 6687 } 6688 } 6689 6690 /** 6691 * Add one or more listeners to the table 6692 * 6693 * @param {*} that JQ for the table 6694 * @param {*} name Event name 6695 * @param {*} src Listener(s) 6696 */ 6697 function _fnListener(that, name, src) { 6698 if (!Array.isArray(src)) { 6699 src = [src]; 6700 } 6701 6702 for (i = 0; i < src.length; i++) { 6703 that.on(name + '.dt', src[i]); 6704 } 6705 } 6706 6707 /** 6708 * Escape HTML entities in strings, in an object 6709 */ 6710 function _fnEscapeObject(obj) { 6711 if (DataTable.ext.escape.attributes) { 6712 $.each(obj, function (key, val) { 6713 obj[key] = _escapeHtml(val); 6714 }) 6715 } 6716 6717 return obj; 6718 } 6719 6720 6721 6722 /** 6723 * Computed structure of the DataTables API, defined by the options passed to 6724 * `DataTable.Api.register()` when building the API. 6725 * 6726 * The structure is built in order to speed creation and extension of the Api 6727 * objects since the extensions are effectively pre-parsed. 6728 * 6729 * The array is an array of objects with the following structure, where this 6730 * base array represents the Api prototype base: 6731 * 6732 * [ 6733 * { 6734 * name: 'data' -- string - Property name 6735 * val: function () {}, -- function - Api method (or undefined if just an object 6736 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6737 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6738 * }, 6739 * { 6740 * name: 'row' 6741 * val: {}, 6742 * methodExt: [ ... ], 6743 * propExt: [ 6744 * { 6745 * name: 'data' 6746 * val: function () {}, 6747 * methodExt: [ ... ], 6748 * propExt: [ ... ] 6749 * }, 6750 * ... 6751 * ] 6752 * } 6753 * ] 6754 * 6755 * @type {Array} 6756 * @ignore 6757 */ 6758 var __apiStruct = []; 6759 6760 6761 /** 6762 * `Array.prototype` reference. 6763 * 6764 * @type object 6765 * @ignore 6766 */ 6767 var __arrayProto = Array.prototype; 6768 6769 6770 /** 6771 * Abstraction for `context` parameter of the `Api` constructor to allow it to 6772 * take several different forms for ease of use. 6773 * 6774 * Each of the input parameter types will be converted to a DataTables settings 6775 * object where possible. 6776 * 6777 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one 6778 * of: 6779 * 6780 * * `string` - jQuery selector. Any DataTables' matching the given selector 6781 * with be found and used. 6782 * * `node` - `TABLE` node which has already been formed into a DataTable. 6783 * * `jQuery` - A jQuery object of `TABLE` nodes. 6784 * * `object` - DataTables settings object 6785 * * `DataTables.Api` - API instance 6786 * @return {array|null} Matching DataTables settings objects. `null` or 6787 * `undefined` is returned if no matching DataTable is found. 6788 * @ignore 6789 */ 6790 var _toSettings = function (mixed) { 6791 var idx, jq; 6792 var settings = DataTable.settings; 6793 var tables = _pluck(settings, 'nTable'); 6794 6795 if (!mixed) { 6796 return []; 6797 } 6798 else if (mixed.nTable && mixed.oFeatures) { 6799 // DataTables settings object 6800 return [mixed]; 6801 } 6802 else if (mixed.nodeName && mixed.nodeName.toLowerCase() === 'table') { 6803 // Table node 6804 idx = tables.indexOf(mixed); 6805 return idx !== -1 ? [settings[idx]] : null; 6806 } 6807 else if (mixed && typeof mixed.settings === 'function') { 6808 return mixed.settings().toArray(); 6809 } 6810 else if (typeof mixed === 'string') { 6811 // jQuery selector 6812 jq = $(mixed).get(); 6813 } 6814 else if (mixed instanceof $) { 6815 // jQuery object (also DataTables instance) 6816 jq = mixed.get(); 6817 } 6818 6819 if (jq) { 6820 return settings.filter(function (v, idx) { 6821 return jq.includes(tables[idx]); 6822 }); 6823 } 6824 }; 6825 6826 6827 /** 6828 * DataTables API class - used to control and interface with one or more 6829 * DataTables enhanced tables. 6830 * 6831 * The API class is heavily based on jQuery, presenting a chainable interface 6832 * that you can use to interact with tables. Each instance of the API class has 6833 * a "context" - i.e. the tables that it will operate on. This could be a single 6834 * table, all tables on a page or a sub-set thereof. 6835 * 6836 * Additionally the API is designed to allow you to easily work with the data in 6837 * the tables, retrieving and manipulating it as required. This is done by 6838 * presenting the API class as an array like interface. The contents of the 6839 * array depend upon the actions requested by each method (for example 6840 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6841 * return an array of objects or arrays depending upon your table's 6842 * configuration). The API object has a number of array like methods (`push`, 6843 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`, 6844 * `unique` etc) to assist your working with the data held in a table. 6845 * 6846 * Most methods (those which return an Api instance) are chainable, which means 6847 * the return from a method call also has all of the methods available that the 6848 * top level object had. For example, these two calls are equivalent: 6849 * 6850 * // Not chained 6851 * api.row.add( {...} ); 6852 * api.draw(); 6853 * 6854 * // Chained 6855 * api.row.add( {...} ).draw(); 6856 * 6857 * @class DataTable.Api 6858 * @param {array|object|string|jQuery} context DataTable identifier. This is 6859 * used to define which DataTables enhanced tables this API will operate on. 6860 * Can be one of: 6861 * 6862 * * `string` - jQuery selector. Any DataTables' matching the given selector 6863 * with be found and used. 6864 * * `node` - `TABLE` node which has already been formed into a DataTable. 6865 * * `jQuery` - A jQuery object of `TABLE` nodes. 6866 * * `object` - DataTables settings object 6867 * @param {array} [data] Data to initialise the Api instance with. 6868 * 6869 * @example 6870 * // Direct initialisation during DataTables construction 6871 * var api = $('#example').DataTable(); 6872 * 6873 * @example 6874 * // Initialisation using a DataTables jQuery object 6875 * var api = $('#example').dataTable().api(); 6876 * 6877 * @example 6878 * // Initialisation as a constructor 6879 * var api = new DataTable.Api( 'table.dataTable' ); 6880 */ 6881 _Api = function (context, data) { 6882 if (!(this instanceof _Api)) { 6883 return new _Api(context, data); 6884 } 6885 6886 var i; 6887 var settings = []; 6888 var ctxSettings = function (o) { 6889 var a = _toSettings(o); 6890 if (a) { 6891 settings.push.apply(settings, a); 6892 } 6893 }; 6894 6895 if (Array.isArray(context)) { 6896 for (i = 0; i < context.length; i++) { 6897 ctxSettings(context[i]); 6898 } 6899 } 6900 else { 6901 ctxSettings(context); 6902 } 6903 6904 // Remove duplicates 6905 this.context = settings.length > 1 6906 ? _unique(settings) 6907 : settings; 6908 6909 // Initial data 6910 _fnArrayApply(this, data); 6911 6912 // selector 6913 this.selector = { 6914 rows: null, 6915 cols: null, 6916 opts: null 6917 }; 6918 6919 _Api.extend(this, this, __apiStruct); 6920 }; 6921 6922 DataTable.Api = _Api; 6923 6924 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's 6925 // isPlainObject. 6926 $.extend(_Api.prototype, { 6927 any: function () { 6928 return this.count() !== 0; 6929 }, 6930 6931 context: [], // array of table settings objects 6932 6933 count: function () { 6934 return this.flatten().length; 6935 }, 6936 6937 each: function (fn) { 6938 for (var i = 0, iLen = this.length; i < iLen; i++) { 6939 fn.call(this, this[i], i, this); 6940 } 6941 6942 return this; 6943 }, 6944 6945 eq: function (idx) { 6946 var ctx = this.context; 6947 6948 return ctx.length > idx ? 6949 new _Api(ctx[idx], this[idx]) : 6950 null; 6951 }, 6952 6953 filter: function (fn) { 6954 var a = __arrayProto.filter.call(this, fn, this); 6955 6956 return new _Api(this.context, a); 6957 }, 6958 6959 flatten: function () { 6960 var a = []; 6961 6962 return new _Api(this.context, a.concat.apply(a, this.toArray())); 6963 }, 6964 6965 get: function (idx) { 6966 return this[idx]; 6967 }, 6968 6969 join: __arrayProto.join, 6970 6971 includes: function (find) { 6972 return this.indexOf(find) === -1 ? false : true; 6973 }, 6974 6975 indexOf: __arrayProto.indexOf, 6976 6977 iterator: function (flatten, type, fn, alwaysNew) { 6978 var 6979 a = [], ret, 6980 i, iLen, j, jen, 6981 context = this.context, 6982 rows, items, item, 6983 selector = this.selector; 6984 6985 // Argument shifting 6986 if (typeof flatten === 'string') { 6987 alwaysNew = fn; 6988 fn = type; 6989 type = flatten; 6990 flatten = false;
vendor: 5,743 bytes, lines 6991-7186
6991 } 6992 6993 for (i = 0, iLen = context.length; i < iLen; i++) { 6994 var apiInst = new _Api(context[i]); 6995 6996 if (type === 'table') { 6997 ret = fn.call(apiInst, context[i], i); 6998 6999 if (ret !== undefined) { 7000 a.push(ret); 7001 } 7002 } 7003 else if (type === 'columns' || type === 'rows') { 7004 // this has same length as context - one entry for each table 7005 ret = fn.call(apiInst, context[i], this[i], i); 7006 7007 if (ret !== undefined) { 7008 a.push(ret); 7009 } 7010 } 7011 else if (type === 'every' || type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell') { 7012 // columns and rows share the same structure. 7013 // 'this' is an array of column indexes for each context 7014 items = this[i]; 7015 7016 if (type === 'column-rows') { 7017 rows = _selector_row_indexes(context[i], selector.opts); 7018 } 7019 7020 for (j = 0, jen = items.length; j < jen; j++) { 7021 item = items[j]; 7022 7023 if (type === 'cell') { 7024 ret = fn.call(apiInst, context[i], item.row, item.column, i, j); 7025 } 7026 else { 7027 ret = fn.call(apiInst, context[i], item, i, j, rows); 7028 } 7029 7030 if (ret !== undefined) { 7031 a.push(ret); 7032 } 7033 } 7034 } 7035 } 7036 7037 if (a.length || alwaysNew) { 7038 var api = new _Api(context, flatten ? a.concat.apply([], a) : a); 7039 var apiSelector = api.selector; 7040 apiSelector.rows = selector.rows; 7041 apiSelector.cols = selector.cols; 7042 apiSelector.opts = selector.opts; 7043 return api; 7044 } 7045 return this; 7046 }, 7047 7048 lastIndexOf: __arrayProto.lastIndexOf, 7049 7050 length: 0, 7051 7052 map: function (fn) { 7053 var a = __arrayProto.map.call(this, fn, this); 7054 7055 return new _Api(this.context, a); 7056 }, 7057 7058 pluck: function (prop) { 7059 var fn = DataTable.util.get(prop); 7060 7061 return this.map(function (el) { 7062 return fn(el); 7063 }); 7064 }, 7065 7066 pop: __arrayProto.pop, 7067 7068 push: __arrayProto.push, 7069 7070 reduce: __arrayProto.reduce, 7071 7072 reduceRight: __arrayProto.reduceRight, 7073 7074 reverse: __arrayProto.reverse, 7075 7076 // Object with rows, columns and opts 7077 selector: null, 7078 7079 shift: __arrayProto.shift, 7080 7081 slice: function () { 7082 return new _Api(this.context, this); 7083 }, 7084 7085 sort: __arrayProto.sort, 7086 7087 splice: __arrayProto.splice, 7088 7089 toArray: function () { 7090 return __arrayProto.slice.call(this); 7091 }, 7092 7093 to$: function () { 7094 return $(this); 7095 }, 7096 7097 toJQuery: function () { 7098 return $(this); 7099 }, 7100 7101 unique: function () { 7102 return new _Api(this.context, _unique(this.toArray())); 7103 }, 7104 7105 unshift: __arrayProto.unshift 7106 }); 7107 7108 7109 function _api_scope(scope, fn, struct) { 7110 return function () { 7111 var ret = fn.apply(scope || this, arguments); 7112 7113 // Method extension 7114 _Api.extend(ret, ret, struct.methodExt); 7115 return ret; 7116 }; 7117 } 7118 7119 function _api_find(src, name) { 7120 for (var i = 0, iLen = src.length; i < iLen; i++) { 7121 if (src[i].name === name) { 7122 return src[i]; 7123 } 7124 } 7125 return null; 7126 } 7127 7128 window.__apiStruct = __apiStruct; 7129 7130 _Api.extend = function (scope, obj, ext) { 7131 // Only extend API instances and static properties of the API 7132 if (!ext.length || !obj || (!(obj instanceof _Api) && !obj.__dt_wrapper)) { 7133 return; 7134 } 7135 7136 var 7137 i, iLen, 7138 struct; 7139 7140 for (i = 0, iLen = ext.length; i < iLen; i++) { 7141 struct = ext[i]; 7142 7143 if (struct.name === '__proto__') { 7144 continue; 7145 } 7146 7147 // Value 7148 obj[struct.name] = struct.type === 'function' ? 7149 _api_scope(scope, struct.val, struct) : 7150 struct.type === 'object' ? 7151 {} : 7152 struct.val; 7153 7154 obj[struct.name].__dt_wrapper = true; 7155 7156 // Property extension 7157 _Api.extend(scope, obj[struct.name], struct.propExt); 7158 } 7159 }; 7160 7161 // [ 7162 // { 7163 // name: 'data' -- string - Property name 7164 // val: function () {}, -- function - Api method (or undefined if just an object 7165 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 7166 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property 7167 // }, 7168 // { 7169 // name: 'row' 7170 // val: {}, 7171 // methodExt: [ ... ], 7172 // propExt: [ 7173 // { 7174 // name: 'data' 7175 // val: function () {}, 7176 // methodExt: [ ... ], 7177 // propExt: [ ... ] 7178 // }, 7179 // ... 7180 // ] 7181 // } 7182 // ] 7183 7184 7185 _Api.register = _api_register = function (name, val) { 7186 if (Array.isArray(name)) {
7187 for (var j = 0, jen = name.length; j < jen; j++) { 7188 _Api.register(name[j], val); 7189 } 7190 return; 7191 } 7192 7193 var 7194 i, iLen, 7195 heir = name.split('.'), 7196 struct = __apiStruct, 7197 key, method; 7198 7199 for (i = 0, iLen = heir.length; i < iLen; i++) { 7200 method = heir[i].indexOf('()') !== -1; 7201 key = method ? 7202 heir[i].replace('()', '') : 7203 heir[i]; 7204 7205 var src = _api_find(struct, key); 7206 if (!src) { 7207 src = { 7208 name: key, 7209 val: {}, 7210 methodExt: [], 7211 propExt: [], 7212 type: 'object' 7213 }; 7214 struct.push(src); 7215 } 7216 7217 if (i === iLen - 1) { 7218 src.val = val; 7219 src.type = typeof val === 'function' ? 7220 'function' : 7221 $.isPlainObject(val) ? 7222 'object' : 7223 'other'; 7224 } 7225 else { 7226 struct = method ? 7227 src.methodExt : 7228 src.propExt; 7229 } 7230 } 7231 }; 7232 7233 _Api.registerPlural = _api_registerPlural = function (pluralName, singularName, val) { 7234 _Api.register(pluralName, val); 7235 7236 _Api.register(singularName, function () { 7237 var ret = val.apply(this, arguments); 7238 7239 if (ret === this) { 7240 // Returned item is the API instance that was passed in, return it 7241 return this; 7242 } 7243 else if (ret instanceof _Api) { 7244 // New API instance returned, want the value from the first item 7245 // in the returned array for the singular result. 7246 return ret.length ? 7247 Array.isArray(ret[0]) ? 7248 new _Api(ret.context, ret[0]) : // Array results are 'enhanced' 7249 ret[0] : 7250 undefined; 7251 } 7252 7253 // Non-API return - just fire it back 7254 return ret; 7255 }); 7256 }; 7257 7258 7259 /** 7260 * Selector for HTML tables. Apply the given selector to the give array of 7261 * DataTables settings objects. 7262 * 7263 * @param {string|integer} [selector] jQuery selector string or integer 7264 * @param {array} Array of DataTables settings objects to be filtered 7265 * @return {array} 7266 * @ignore 7267 */ 7268 var __table_selector = function (selector, a) { 7269 if (Array.isArray(selector)) { 7270 var result = []; 7271 7272 selector.forEach(function (sel) { 7273 var inner = __table_selector(sel, a); 7274 7275 _fnArrayApply(result, inner); 7276 }); 7277 7278 return result.filter(function (item) { 7279 return item; 7280 }); 7281 } 7282 7283 // Integer is used to pick out a table by index 7284 if (typeof selector === 'number') { 7285 return [a[selector]]; 7286 } 7287 7288 // Perform a jQuery selector on the table nodes 7289 var nodes = a.map(function (el) { 7290 return el.nTable; 7291 }); 7292 7293 return $(nodes) 7294 .filter(selector) 7295 .map(function () { 7296 // Need to translate back from the table node to the settings 7297 var idx = nodes.indexOf(this); 7298 return a[idx]; 7299 }) 7300 .toArray(); 7301 }; 7302 7303 7304 7305 /** 7306 * Context selector for the API's context (i.e. the tables the API instance 7307 * refers to. 7308 * 7309 * @name DataTable.Api#tables 7310 * @param {string|integer} [selector] Selector to pick which tables the iterator 7311 * should operate on. If not given, all tables in the current context are 7312 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to 7313 * select multiple tables or as an integer to select a single table. 7314 * @returns {DataTable.Api} Returns a new API instance if a selector is given. 7315 */ 7316 _api_register('tables()', function (selector) { 7317 // A new instance is created if there was a selector specified 7318 return selector !== undefined && selector !== null ? 7319 new _Api(__table_selector(selector, this.context)) : 7320 this; 7321 }); 7322 7323 7324 _api_register('table()', function (selector) { 7325 var tables = this.tables(selector); 7326 var ctx = tables.context; 7327 7328 // Truncate to the first matched table 7329 return ctx.length ? 7330 new _Api(ctx[0]) : 7331 tables; 7332 }); 7333 7334 // Common methods, combined to reduce size 7335 [ 7336 ['nodes', 'node', 'nTable'], 7337 ['body', 'body', 'nTBody'], 7338 ['header', 'header', 'nTHead'], 7339 ['footer', 'footer', 'nTFoot'], 7340 ].forEach(function (item) { 7341 _api_registerPlural( 7342 'tables().' + item[0] + '()', 7343 'table().' + item[1] + '()', 7344 function () { 7345 return this.iterator('table', function (ctx) { 7346 return ctx[item[2]]; 7347 }, 1); 7348 } 7349 ); 7350 }); 7351 7352 // Structure methods 7353 [ 7354 ['header', 'aoHeader'], 7355 ['footer', 'aoFooter'], 7356 ].forEach(function (item) { 7357 _api_register('table().' + item[0] + '.structure()', function (selector) { 7358 var indexes = this.columns(selector).indexes().flatten().toArray(); 7359 var ctx = this.context[0]; 7360 var structure = _fnHeaderLayout(ctx, ctx[item[1]], indexes); 7361 7362 // The structure is in column index order - but from this method we want the return to be 7363 // in the columns() selector API order. In order to do that we need to map from one form 7364 // to the other 7365 var orderedIndexes = indexes.slice().sort(function (a, b) { 7366 return a - b; 7367 }); 7368 7369 return structure.map(function (row) { 7370 return indexes.map(function (colIdx) { 7371 return row[orderedIndexes.indexOf(colIdx)]; 7372 }); 7373 }); 7374 }); 7375 }); 7376 7377 7378 _api_registerPlural('tables().containers()', 'table().container()', function () { 7379 return this.iterator('table', function (ctx) { 7380 return ctx.nTableWrapper; 7381 }, 1); 7382 }); 7383 7384 _api_register('tables().every()', function (fn) { 7385 var that = this; 7386 7387 return this.iterator('table', function (s, i) { 7388 fn.call(that.table(i), i); 7389 }); 7390 }); 7391 7392 _api_register('caption()', function (value, side) { 7393 var context = this.context; 7394 7395 // Getter - return existing node's content 7396 if (value === undefined) { 7397 var caption = context[0].captionNode; 7398 7399 return caption && context.length ? 7400 caption.innerHTML : 7401 null; 7402 } 7403 7404 return this.iterator('table', function (ctx) { 7405 var table = $(ctx.nTable); 7406 var caption = $(ctx.captionNode); 7407 var container = $(ctx.nTableWrapper); 7408 7409 // Create the node if it doesn't exist yet 7410 if (!caption.length) { 7411 caption = $('<caption/>').html(value); 7412 ctx.captionNode = caption[0]; 7413 7414 // If side isn't set, we need to insert into the document to let the 7415 // CSS decide so we can read it back, otherwise there is no way to 7416 // know if the CSS would put it top or bottom for scrolling 7417 if (!side) { 7418 table.prepend(caption); 7419 7420 side = caption.css('caption-side'); 7421 } 7422 } 7423 7424 caption.html(value); 7425 7426 if (side) { 7427 caption.css('caption-side', side); 7428 caption[0]._captionSide = side; 7429 } 7430 7431 if (container.find('div.dataTables_scroll').length) { 7432 var selector = (side === 'top' ? 'Head' : 'Foot'); 7433 7434 container.find('div.dataTables_scroll' + selector + ' table').prepend(caption); 7435 } 7436 else { 7437 table.prepend(caption); 7438 } 7439 }, 1); 7440 }); 7441 7442 _api_register('caption.node()', function () { 7443 var ctx = this.context; 7444 7445 return ctx.length ? ctx[0].captionNode : null; 7446 }); 7447 7448 7449 /** 7450 * Redraw the tables in the current context. 7451 */ 7452 _api_register('draw()', function (paging) { 7453 return this.iterator('table', function (settings) { 7454 if (paging === 'page') { 7455 _fnDraw(settings); 7456 } 7457 else { 7458 if (typeof paging === 'string') { 7459 paging = paging === 'full-hold' ? 7460 false : 7461 true; 7462 } 7463 7464 _fnReDraw(settings, paging === false); 7465 } 7466 }); 7467 }); 7468 7469 7470 7471 /** 7472 * Get the current page index. 7473 * 7474 * @return {integer} Current page index (zero based) 7475 *//** 7476 * Set the current page. 7477 * 7478 * Note that if you attempt to show a page which does not exist, DataTables will 7479 * not throw an error, but rather reset the paging. 7480 * 7481 * @param {integer|string} action The paging action to take. This can be one of:
vendor: 8,952 bytes, lines 7482-7762
7482 * * `integer` - The page index to jump to 7483 * * `string` - An action to take: 7484 * * `first` - Jump to first page. 7485 * * `next` - Jump to the next page 7486 * * `previous` - Jump to previous page 7487 * * `last` - Jump to the last page. 7488 * @returns {DataTables.Api} this 7489 */ 7490 _api_register('page()', function (action) { 7491 if (action === undefined) { 7492 return this.page.info().page; // not an expensive call 7493 } 7494 7495 // else, have an action to take on all tables 7496 return this.iterator('table', function (settings) { 7497 _fnPageChange(settings, action); 7498 }); 7499 }); 7500 7501 7502 /** 7503 * Paging information for the first table in the current context. 7504 * 7505 * If you require paging information for another table, use the `table()` method 7506 * with a suitable selector. 7507 * 7508 * @return {object} Object with the following properties set: 7509 * * `page` - Current page index (zero based - i.e. the first page is `0`) 7510 * * `pages` - Total number of pages 7511 * * `start` - Display index for the first record shown on the current page 7512 * * `end` - Display index for the last record shown on the current page 7513 * * `length` - Display length (number of records). Note that generally `start 7514 * + length = end`, but this is not always true, for example if there are 7515 * only 2 records to show on the final page, with a length of 10. 7516 * * `recordsTotal` - Full data set length 7517 * * `recordsDisplay` - Data set length once the current filtering criterion 7518 * are applied. 7519 */ 7520 _api_register('page.info()', function () { 7521 if (this.context.length === 0) { 7522 return undefined; 7523 } 7524 7525 var 7526 settings = this.context[0], 7527 start = settings._iDisplayStart, 7528 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1, 7529 visRecords = settings.fnRecordsDisplay(), 7530 all = len === -1; 7531 7532 return { 7533 "page": all ? 0 : Math.floor(start / len), 7534 "pages": all ? 1 : Math.ceil(visRecords / len), 7535 "start": start, 7536 "end": settings.fnDisplayEnd(), 7537 "length": len, 7538 "recordsTotal": settings.fnRecordsTotal(), 7539 "recordsDisplay": visRecords, 7540 "serverSide": _fnDataSource(settings) === 'ssp' 7541 }; 7542 }); 7543 7544 7545 /** 7546 * Get the current page length. 7547 * 7548 * @return {integer} Current page length. Note `-1` indicates that all records 7549 * are to be shown. 7550 *//** 7551 * Set the current page length. 7552 * 7553 * @param {integer} Page length to set. Use `-1` to show all records. 7554 * @returns {DataTables.Api} this 7555 */ 7556 _api_register('page.len()', function (len) { 7557 // Note that we can't call this function 'length()' because `length` 7558 // is a JavaScript property of functions which defines how many arguments 7559 // the function expects. 7560 if (len === undefined) { 7561 return this.context.length !== 0 ? 7562 this.context[0]._iDisplayLength : 7563 undefined; 7564 } 7565 7566 // else, set the page length 7567 return this.iterator('table', function (settings) { 7568 _fnLengthChange(settings, len); 7569 }); 7570 }); 7571 7572 7573 7574 var __reload = function (settings, holdPosition, callback) { 7575 // Use the draw event to trigger a callback 7576 if (callback) { 7577 var api = new _Api(settings); 7578 7579 api.one('draw', function () { 7580 callback(api.ajax.json()); 7581 }); 7582 } 7583 7584 if (_fnDataSource(settings) == 'ssp') { 7585 _fnReDraw(settings, holdPosition); 7586 } 7587 else { 7588 _fnProcessingDisplay(settings, true); 7589 7590 // Cancel an existing request 7591 var xhr = settings.jqXHR; 7592 if (xhr && xhr.readyState !== 4) { 7593 xhr.abort(); 7594 } 7595 7596 // Trigger xhr 7597 _fnBuildAjax(settings, {}, function (json) { 7598 _fnClearTable(settings); 7599 7600 var data = _fnAjaxDataSrc(settings, json); 7601 for (var i = 0, iLen = data.length; i < iLen; i++) { 7602 _fnAddData(settings, data[i]); 7603 } 7604 7605 _fnReDraw(settings, holdPosition); 7606 _fnInitComplete(settings); 7607 _fnProcessingDisplay(settings, false); 7608 }); 7609 } 7610 }; 7611 7612 7613 /** 7614 * Get the JSON response from the last Ajax request that DataTables made to the 7615 * server. Note that this returns the JSON from the first table in the current 7616 * context. 7617 * 7618 * @return {object} JSON received from the server. 7619 */ 7620 _api_register('ajax.json()', function () { 7621 var ctx = this.context; 7622 7623 if (ctx.length > 0) { 7624 return ctx[0].json; 7625 } 7626 7627 // else return undefined; 7628 }); 7629 7630 7631 /** 7632 * Get the data submitted in the last Ajax request 7633 */ 7634 _api_register('ajax.params()', function () { 7635 var ctx = this.context; 7636 7637 if (ctx.length > 0) { 7638 return ctx[0].oAjaxData; 7639 } 7640 7641 // else return undefined; 7642 }); 7643 7644 7645 /** 7646 * Reload tables from the Ajax data source. Note that this function will 7647 * automatically re-draw the table when the remote data has been loaded. 7648 * 7649 * @param {boolean} [reset=true] Reset (default) or hold the current paging 7650 * position. A full re-sort and re-filter is performed when this method is 7651 * called, which is why the pagination reset is the default action. 7652 * @returns {DataTables.Api} this 7653 */ 7654 _api_register('ajax.reload()', function (callback, resetPaging) { 7655 return this.iterator('table', function (settings) { 7656 __reload(settings, resetPaging === false, callback); 7657 }); 7658 }); 7659 7660 7661 /** 7662 * Get the current Ajax URL. Note that this returns the URL from the first 7663 * table in the current context. 7664 * 7665 * @return {string} Current Ajax source URL 7666 *//** 7667 * Set the Ajax URL. Note that this will set the URL for all tables in the 7668 * current context. 7669 * 7670 * @param {string} url URL to set. 7671 * @returns {DataTables.Api} this 7672 */ 7673 _api_register('ajax.url()', function (url) { 7674 var ctx = this.context; 7675 7676 if (url === undefined) { 7677 // get 7678 if (ctx.length === 0) { 7679 return undefined; 7680 } 7681 ctx = ctx[0]; 7682 7683 return $.isPlainObject(ctx.ajax) ? 7684 ctx.ajax.url : 7685 ctx.ajax; 7686 } 7687 7688 // set 7689 return this.iterator('table', function (settings) { 7690 if ($.isPlainObject(settings.ajax)) { 7691 settings.ajax.url = url; 7692 } 7693 else { 7694 settings.ajax = url; 7695 } 7696 }); 7697 }); 7698 7699 7700 /** 7701 * Load data from the newly set Ajax URL. Note that this method is only 7702 * available when `ajax.url()` is used to set a URL. Additionally, this method 7703 * has the same effect as calling `ajax.reload()` but is provided for 7704 * convenience when setting a new URL. Like `ajax.reload()` it will 7705 * automatically redraw the table once the remote data has been loaded. 7706 * 7707 * @returns {DataTables.Api} this 7708 */ 7709 _api_register('ajax.url().load()', function (callback, resetPaging) { 7710 // Same as a reload, but makes sense to present it for easy access after a 7711 // url change 7712 return this.iterator('table', function (ctx) { 7713 __reload(ctx, resetPaging === false, callback); 7714 }); 7715 }); 7716 7717 7718 7719 7720 var _selector_run = function (type, selector, selectFn, settings, opts) { 7721 var 7722 out = [], res, 7723 i, iLen, 7724 selectorType = typeof selector; 7725 7726 // Can't just check for isArray here, as an API or jQuery instance might be 7727 // given with their array like look 7728 if (!selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined) { 7729 selector = [selector]; 7730 } 7731 7732 for (i = 0, iLen = selector.length; i < iLen; i++) { 7733 res = selectFn(typeof selector[i] === 'string' ? selector[i].trim() : selector[i]); 7734 7735 // Remove empty items 7736 res = res.filter(function (item) { 7737 return item !== null && item !== undefined; 7738 }); 7739 7740 if (res && res.length) { 7741 out = out.concat(res); 7742 } 7743 } 7744 7745 // selector extensions 7746 var ext = _ext.selector[type]; 7747 if (ext.length) { 7748 for (i = 0, iLen = ext.length; i < iLen; i++) { 7749 out = ext[i](settings, opts, out); 7750 } 7751 } 7752 7753 return _unique(out); 7754 }; 7755 7756 7757 var _selector_opts = function (opts) { 7758 if (!opts) { 7759 opts = {}; 7760 } 7761 7762 // Backwards compatibility for 1.9- which used the terminology f
7762ilter rather 7763 // than search 7764 if (opts.filter && opts.search === undefined) { 7765 opts.search = opts.filter; 7766 } 7767 7768 return $.extend({ 7769 columnOrder: 'implied', 7770 search: 'none', 7771 order: 'current', 7772 page: 'all' 7773 }, opts); 7774 }; 7775 7776 7777 // Reduce the API instance to the first item found 7778 var _selector_first = function (old) { 7779 var inst = new _Api(old.context[0]); 7780 7781 // Use a push rather than passing to the constructor, since it will 7782 // merge arrays down automatically, which isn't what is wanted here 7783 if (old.length) { 7784 inst.push(old[0]); 7785 } 7786 7787 inst.selector = old.selector; 7788 7789 // Limit to a single row / column / cell 7790 if (inst.length && inst[0].length > 1) { 7791 inst[0].splice(1); 7792 } 7793 7794 return inst; 7795 }; 7796 7797 7798 var _selector_row_indexes = function (settings, opts) { 7799 var 7800 i, iLen, tmp, a = [], 7801 displayFiltered = settings.aiDisplay, 7802 displayMaster = settings.aiDisplayMaster; 7803 7804 var 7805 search = opts.search, // none, applied, removed 7806 order = opts.order, // applied, current, index (original - compatibility with 1.9) 7807 page = opts.page; // all, current 7808 7809 if (_fnDataSource(settings) == 'ssp') { 7810 // In server-side processing mode, most options are irrelevant since 7811 // rows not shown don't exist and the index order is the applied order 7812 // Removed is a special case - for consistency just return an empty 7813 // array 7814 return search === 'removed' ? 7815 [] : 7816 _range(0, displayMaster.length); 7817 } 7818 7819 if (page == 'current') { 7820 // Current page implies that order=current and filter=applied, since it is 7821 // fairly senseless otherwise, regardless of what order and search actually 7822 // are 7823 for (i = settings._iDisplayStart, iLen = settings.fnDisplayEnd(); i < iLen; i++) { 7824 a.push(displayFiltered[i]); 7825 } 7826 } 7827 else if (order == 'current' || order == 'applied') { 7828 if (search == 'none') { 7829 a = displayMaster.slice(); 7830 } 7831 else if (search == 'applied') { 7832 a = displayFiltered.slice(); 7833 } 7834 else if (search == 'removed') { 7835 // O(n+m) solution by creating a hash map 7836 var displayFilteredMap = {}; 7837 7838 for (i = 0, iLen = displayFiltered.length; i < iLen; i++) { 7839 displayFilteredMap[displayFiltered[i]] = null; 7840 } 7841 7842 displayMaster.forEach(function (item) { 7843 if (!Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) { 7844 a.push(item); 7845 } 7846 }); 7847 } 7848 } 7849 else if (order == 'index' || order == 'original') { 7850 for (i = 0, iLen = settings.aoData.length; i < iLen; i++) { 7851 if (!settings.aoData[i]) { 7852 continue; 7853 } 7854 7855 if (search == 'none') { 7856 a.push(i); 7857 } 7858 else { // applied | removed 7859 tmp = displayFiltered.indexOf(i); 7860 7861 if ((tmp === -1 && search == 'removed') || 7862 (tmp >= 0 && search == 'applied')) { 7863 a.push(i); 7864 } 7865 } 7866 } 7867 } 7868 else if (typeof order === 'number') { 7869 // Order the rows by the given column 7870 var ordered = _fnSort(settings, order, 'asc'); 7871 7872 if (search === 'none') { 7873 a = ordered; 7874 } 7875 else { // applied | removed 7876 for (i = 0; i < ordered.length; i++) { 7877 tmp = displayFiltered.indexOf(ordered[i]); 7878 7879 if ((tmp === -1 && search == 'removed') || 7880 (tmp >= 0 && search == 'applied')) { 7881 a.push(ordered[i]); 7882 } 7883 } 7884 } 7885 } 7886 7887 return a; 7888 }; 7889 7890 7891 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 7892 * Rows 7893 * 7894 * {} - no selector - use all available rows 7895 * {integer} - row aoData index 7896 * {node} - TR node 7897 * {string} - jQuery selector to apply to the TR elements 7898 * {array} - jQuery array of nodes, or simply an array of TR nodes 7899 * 7900 */ 7901 var __row_selector = function (settings, selector, opts) { 7902 var rows; 7903 var run = function (sel) { 7904 var selInt = _intVal(sel); 7905 var aoData = settings.aoData; 7906 7907 // Short cut - selector is a number and no options provided (default is 7908 // all records, so no need to check if the index is in there, since it 7909 // must be - dev error if the index doesn't exist). 7910 if (selInt !== null && !opts) { 7911 return [selInt]; 7912 } 7913 7914 if (!rows) { 7915 rows = _selector_row_indexes(settings, opts); 7916 } 7917 7918 if (selInt !== null && rows.indexOf(selInt) !== -1) { 7919 // Selector - integer 7920 return [selInt]; 7921 } 7922 else if (sel === null || sel === undefined || sel === '') { 7923 // Selector - none 7924 return rows; 7925 } 7926 7927 // Selector - function 7928 if (typeof sel === 'function') { 7929 return rows.map(function (idx) { 7930 var row = aoData[idx]; 7931 return sel(idx, row._aData, row.nTr) ? idx : null; 7932 }); 7933 } 7934 7935 // Selector - node 7936 if (sel.nodeName) { 7937 var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup 7938 var cellIdx = sel._DT_CellIndex; 7939 7940 if (rowIdx !== undefined) { 7941 // Make sure that the row is actually still present in the table 7942 return aoData[rowIdx] && aoData[rowIdx].nTr === sel ? 7943 [rowIdx] : 7944 []; 7945 } 7946 else if (cellIdx) { 7947 return aoData[cellIdx.row] && aoData[cellIdx.row].nTr === sel.parentNode ? 7948 [cellIdx.row] : 7949 []; 7950 } 7951 else { 7952 var host = $(sel).closest('*[data-dt-row]'); 7953 return host.length ? 7954 [host.data('dt-row')] : 7955 []; 7956 } 7957 } 7958 7959 // ID selector. Want to always be able to select rows by id, regardless 7960 // of if the tr element has been created or not, so can't rely upon 7961 // jQuery here - hence a custom implementation. This does not match 7962 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything, 7963 // but to select it using a CSS selector engine (like Sizzle or 7964 // querySelect) it would need to need to be escaped for some characters. 7965 // DataTables simplifies this for row selectors since you can select 7966 // only a row. A # indicates an id any anything that follows is the id - 7967 // unescaped. 7968 if (typeof sel === 'string' && sel.charAt(0) === '#') { 7969 // get row index from id 7970 var rowObj = settings.aIds[sel.replace(/^#/, '')]; 7971 if (rowObj !== undefined) { 7972 return [rowObj.idx]; 7973 } 7974 7975 // need to fall through to jQuery in case there is DOM id that 7976 // matches 7977 } 7978 7979 // Get nodes in the order from the `rows` array with
vendor: 13,430 bytes, lines 7979-8417
7979null values removed 7980 var nodes = _removeEmpty( 7981 _pluck_order(settings.aoData, rows, 'nTr') 7982 ); 7983 7984 // Selector - jQuery selector string, array of nodes or jQuery object/ 7985 // As jQuery's .filter() allows jQuery objects to be passed in filter, 7986 // it also allows arrays, so this will cope with all three options 7987 return $(nodes) 7988 .filter(sel) 7989 .map(function () { 7990 return this._DT_RowIndex; 7991 }) 7992 .toArray(); 7993 }; 7994 7995 var matched = _selector_run('row', selector, run, settings, opts); 7996 7997 if (opts.order === 'current' || opts.order === 'applied') { 7998 _fnSortDisplay(settings, matched); 7999 } 8000 8001 return matched; 8002 }; 8003 8004 8005 _api_register('rows()', function (selector, opts) { 8006 // argument shifting 8007 if (selector === undefined) { 8008 selector = ''; 8009 } 8010 else if ($.isPlainObject(selector)) { 8011 opts = selector; 8012 selector = ''; 8013 } 8014 8015 opts = _selector_opts(opts); 8016 8017 var inst = this.iterator('table', function (settings) { 8018 return __row_selector(settings, selector, opts); 8019 }, 1); 8020 8021 // Want argument shifting here and in __row_selector? 8022 inst.selector.rows = selector; 8023 inst.selector.opts = opts; 8024 8025 return inst; 8026 }); 8027 8028 _api_register('rows().nodes()', function () { 8029 return this.iterator('row', function (settings, row) { 8030 return settings.aoData[row].nTr || undefined; 8031 }, 1); 8032 }); 8033 8034 _api_register('rows().data()', function () { 8035 return this.iterator(true, 'rows', function (settings, rows) { 8036 return _pluck_order(settings.aoData, rows, '_aData'); 8037 }, 1); 8038 }); 8039 8040 _api_registerPlural('rows().cache()', 'row().cache()', function (type) { 8041 return this.iterator('row', function (settings, row) { 8042 var r = settings.aoData[row]; 8043 return type === 'search' ? r._aFilterData : r._aSortData; 8044 }, 1); 8045 }); 8046 8047 _api_registerPlural('rows().invalidate()', 'row().invalidate()', function (src) { 8048 return this.iterator('row', function (settings, row) { 8049 _fnInvalidate(settings, row, src); 8050 }); 8051 }); 8052 8053 _api_registerPlural('rows().indexes()', 'row().index()', function () { 8054 return this.iterator('row', function (settings, row) { 8055 return row; 8056 }, 1); 8057 }); 8058 8059 _api_registerPlural('rows().ids()', 'row().id()', function (hash) { 8060 var a = []; 8061 var context = this.context; 8062 8063 // `iterator` will drop undefined values, but in this case we want them 8064 for (var i = 0, iLen = context.length; i < iLen; i++) { 8065 for (var j = 0, jen = this[i].length; j < jen; j++) { 8066 var id = context[i].rowIdFn(context[i].aoData[this[i][j]]._aData); 8067 a.push((hash === true ? '#' : '') + id); 8068 } 8069 } 8070 8071 return new _Api(context, a); 8072 }); 8073 8074 _api_registerPlural('rows().remove()', 'row().remove()', function () { 8075 this.iterator('row', function (settings, row) { 8076 var data = settings.aoData; 8077 var rowData = data[row]; 8078 8079 // Delete from the display arrays 8080 var idx = settings.aiDisplayMaster.indexOf(row); 8081 if (idx !== -1) { 8082 settings.aiDisplayMaster.splice(idx, 1); 8083 } 8084 8085 // For server-side processing tables - subtract the deleted row from the count 8086 if (settings._iRecordsDisplay > 0) { 8087 settings._iRecordsDisplay--; 8088 } 8089 8090 // Check for an 'overflow' they case for displaying the table 8091 _fnLengthOverflow(settings); 8092 8093 // Remove the row's ID reference if there is one 8094 var id = settings.rowIdFn(rowData._aData); 8095 if (id !== undefined) { 8096 delete settings.aIds[id]; 8097 } 8098 8099 data[row] = null; 8100 }); 8101 8102 return this; 8103 }); 8104 8105 8106 _api_register('rows.add()', function (rows) { 8107 var newRows = this.iterator('table', function (settings) { 8108 var row, i, iLen; 8109 var out = []; 8110 8111 for (i = 0, iLen = rows.length; i < iLen; i++) { 8112 row = rows[i]; 8113 8114 if (row.nodeName && row.nodeName.toUpperCase() === 'TR') { 8115 out.push(_fnAddTr(settings, row)[0]); 8116 } 8117 else { 8118 out.push(_fnAddData(settings, row)); 8119 } 8120 } 8121 8122 return out; 8123 }, 1); 8124 8125 // Return an Api.rows() extended instance, so rows().nodes() etc can be used 8126 var modRows = this.rows(-1); 8127 modRows.pop(); 8128 _fnArrayApply(modRows, newRows); 8129 8130 return modRows; 8131 }); 8132 8133 8134 8135 8136 8137 /** 8138 * 8139 */ 8140 _api_register('row()', function (selector, opts) { 8141 return _selector_first(this.rows(selector, opts)); 8142 }); 8143 8144 8145 _api_register('row().data()', function (data) { 8146 var ctx = this.context; 8147 8148 if (data === undefined) { 8149 // Get 8150 return ctx.length && this.length && this[0].length ? 8151 ctx[0].aoData[this[0]]._aData : 8152 undefined; 8153 } 8154 8155 // Set 8156 var row = ctx[0].aoData[this[0]]; 8157 row._aData = data; 8158 8159 // If the DOM has an id, and the data source is an array 8160 if (Array.isArray(data) && row.nTr && row.nTr.id) { 8161 _fnSetObjectDataFn(ctx[0].rowId)(data, row.nTr.id); 8162 } 8163 8164 // Automatically invalidate 8165 _fnInvalidate(ctx[0], this[0], 'data'); 8166 8167 return this; 8168 }); 8169 8170 8171 _api_register('row().node()', function () { 8172 var ctx = this.context; 8173 8174 if (ctx.length && this.length && this[0].length) { 8175 var row = ctx[0].aoData[this[0]]; 8176 8177 if (row && row.nTr) { 8178 return row.nTr; 8179 } 8180 } 8181 8182 return null; 8183 }); 8184 8185 8186 _api_register('row.add()', function (row) { 8187 // Allow a jQuery object to be passed in - only a single row is added from 8188 // it though - the first element in the set 8189 if (row instanceof $ && row.length) { 8190 row = row[0]; 8191 } 8192 8193 var rows = this.iterator('table', function (settings) { 8194 if (row.nodeName && row.nodeName.toUpperCase() === 'TR') { 8195 return _fnAddTr(settings, row)[0]; 8196 } 8197 return _fnAddData(settings, row); 8198 }); 8199 8200 // Return an Api.rows() extended instance, with the newly added row selected 8201 return this.row(rows[0]); 8202 }); 8203 8204 8205 $(document).on('plugin-init.dt', function (e, context) { 8206 var api = new _Api(context); 8207 8208 api.on('stateSaveParams.DT', function (e, settings, d) { 8209 // This could be more compact with the API, but it is a lot faster as a simple 8210 // internal loop 8211 var idFn = settings.rowIdFn; 8212 var rows = settings.aiDisplayMaster; 8213 var ids = []; 8214 8215 for (var i = 0; i < rows.length; i++) { 8216 var rowIdx = rows[i]; 8217 var data = settings.aoData[rowIdx]; 8218 8219 if (data._detailsShow) { 8220 ids.push('#' + idFn(data._aData)); 8221 } 8222 } 8223 8224 d.childRows = ids; 8225 }); 8226 8227 // For future state loads (e.g. with StateRestore) 8228 api.on('stateLoaded.DT', function (e, settings, state) { 8229 __details_state_load(api, state); 8230 }); 8231 8232 // And the initial load state 8233 __details_state_load(api, api.state.loaded()); 8234 }); 8235 8236 var __details_state_load = function (api, state) { 8237 if (state && state.childRows) { 8238 api 8239 .rows(state.childRows.map(function (id) { 8240 // Escape any `:` characters from the row id. Accounts for 8241 // already escaped characters. 8242 return id.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g, "$1\\:"); 8243 })) 8244 .every(function () { 8245 _fnCallbackFire(api.settings()[0], null, 'requestChild', [this]) 8246 }); 8247 } 8248 } 8249 8250 var __details_add = function (ctx, row, data, klass) { 8251 // Convert to array of TR elements 8252 var rows = []; 8253 var addRow = function (r, k) { 8254 // Recursion to allow for arrays of jQuery objects 8255 if (Array.isArray(r) || r instanceof $) { 8256 for (var i = 0, iLen = r.length; i < iLen; i++) { 8257 addRow(r[i], k); 8258 } 8259 return; 8260 } 8261 8262 // If we get a TR element, then just add it directly - up to the dev 8263 // to add the correct number of columns etc 8264 if (r.nodeName && r.nodeName.toLowerCase() === 'tr') { 8265 r.setAttribute('data-dt-row', row.idx); 8266 rows.push(r); 8267 } 8268 else { 8269 // Otherwise create a row with a wrapper 8270 var created = $('<tr><td></td></tr>') 8271 .attr('data-dt-row', row.idx) 8272 .addClass(k); 8273 8274 $('td', created) 8275 .addClass(k) 8276 .html(r)[0].colSpan = _fnVisibleColumns(ctx); 8277 8278 rows.push(created[0]); 8279 } 8280 }; 8281 8282 addRow(data, klass); 8283 8284 if (row._details) { 8285 row._details.detach(); 8286 } 8287 8288 row._details = $(rows); 8289 8290 // If the children were already shown, that state should be retained 8291 if (row._detailsShow) { 8292 row._details.insertAfter(row.nTr); 8293 } 8294 }; 8295 8296 8297 // Make state saving of child row details async to allow them to be batch processed 8298 var __details_state = DataTable.util.throttle( 8299 function (ctx) { 8300 _fnSaveState(ctx[0]) 8301 }, 8302 500 8303 ); 8304 8305 8306 var __details_remove = function (api, idx) { 8307 var ctx = api.context; 8308 8309 if (ctx.length) { 8310 var row = ctx[0].aoData[idx !== undefined ? idx : api[0]]; 8311 8312 if (row && row._details) { 8313 row._details.detach(); 8314 8315 row._detailsShow = undefined; 8316 row._details = undefined; 8317 $(row.nTr).removeClass('dt-hasChild'); 8318 __details_state(ctx); 8319 } 8320 } 8321 }; 8322 8323 8324 var __details_display = function (api, show) { 8325 var ctx = api.context; 8326 8327 if (ctx.length && api.length) { 8328 var row = ctx[0].aoData[api[0]]; 8329 8330 if (row._details) { 8331 row._detailsShow = show; 8332 8333 if (show) { 8334 row._details.insertAfter(row.nTr); 8335 $(row.nTr).addClass('dt-hasChild'); 8336 } 8337 else { 8338 row._details.detach(); 8339 $(row.nTr).removeClass('dt-hasChild'); 8340 } 8341 8342 _fnCallbackFire(ctx[0], null, 'childRow', [show, api.row(api[0])]) 8343 8344 __details_events(ctx[0]); 8345 __details_state(ctx); 8346 } 8347 } 8348 }; 8349 8350 8351 var __details_events = function (settings) { 8352 var api = new _Api(settings); 8353 var namespace = '.dt.DT_details'; 8354 var drawEvent = 'draw' + namespace; 8355 var colvisEvent = 'column-sizing' + namespace; 8356 var destroyEvent = 'destroy' + namespace; 8357 var data = settings.aoData; 8358 8359 api.off(drawEvent + ' ' + colvisEvent + ' ' + destroyEvent); 8360 8361 if (_pluck(data, '_details').length > 0) { 8362 // On each draw, insert the required elements into the document 8363 api.on(drawEvent, function (e, ctx) { 8364 if (settings !== ctx) { 8365 return; 8366 } 8367 8368 api.rows({ page: 'current' }).eq(0).each(function (idx) { 8369 // Internal data grab 8370 var row = data[idx]; 8371 8372 if (row._detailsShow) { 8373 row._details.insertAfter(row.nTr); 8374 } 8375 }); 8376 }); 8377 8378 // Column visibility change - update the colspan 8379 api.on(colvisEvent, function (e, ctx) { 8380 if (settings !== ctx) { 8381 return; 8382 } 8383 8384 // Update the colspan for the details rows (note, only if it already has 8385 // a colspan) 8386 var row, visible = _fnVisibleColumns(ctx); 8387 8388 for (var i = 0, iLen = data.length; i < iLen; i++) { 8389 row = data[i]; 8390 8391 if (row && row._details) { 8392 row._details.each(function () { 8393 var el = $(this).children('td'); 8394 8395 if (el.length == 1) { 8396 el.attr('colspan', visible); 8397 } 8398 }); 8399 } 8400 } 8401 }); 8402 8403 // Table destroyed - nuke any child rows 8404 api.on(destroyEvent, function (e, ctx) { 8405 if (settings !== ctx) { 8406 return; 8407 } 8408 8409 for (var i = 0, iLen = data.length; i < iLen; i++) { 8410 if (data[i] && data[i]._details) { 8411 __details_remove(api, i); 8412 } 8413 } 8414 }); 8415 } 8416 }; 8417
8418 // Strings for the method names to help minification 8419 var _emp = ''; 8420 var _child_obj = _emp + 'row().child'; 8421 var _child_mth = _child_obj + '()'; 8422 8423 // data can be: 8424 // tr 8425 // string 8426 // jQuery or array of any of the above 8427 _api_register(_child_mth, function (data, klass) { 8428 var ctx = this.context; 8429 8430 if (data === undefined) { 8431 // get 8432 return ctx.length && this.length && ctx[0].aoData[this[0]] 8433 ? ctx[0].aoData[this[0]]._details 8434 : undefined; 8435 } 8436 else if (data === true) { 8437 // show 8438 this.child.show(); 8439 } 8440 else if (data === false) { 8441 // remove 8442 __details_remove(this); 8443 } 8444 else if (ctx.length && this.length) { 8445 // set 8446 __details_add(ctx[0], ctx[0].aoData[this[0]], data, klass); 8447 } 8448 8449 return this; 8450 }); 8451 8452 8453 _api_register([ 8454 _child_obj + '.show()', 8455 _child_mth + '.show()' // only when `child()` was called with parameters (without 8456 ], function () { // it returns an object and this method is not executed) 8457 __details_display(this, true); 8458 return this; 8459 }); 8460 8461 8462 _api_register([ 8463 _child_obj + '.hide()', 8464 _child_mth + '.hide()' // only when `child()` was called with parameters (without 8465 ], function () { // it returns an object and this method is not executed) 8466 __details_display(this, false); 8467 return this; 8468 }); 8469 8470 8471 _api_register([ 8472 _child_obj + '.remove()', 8473 _child_mth + '.remove()' // only when `child()` was called with parameters (without 8474 ], function () { // it returns an object and this method is not executed) 8475 __details_remove(this); 8476 return this; 8477 }); 8478 8479 8480 _api_register(_child_obj + '.isShown()', function () { 8481 var ctx = this.context; 8482 8483 if (ctx.length && this.length && ctx[0].aoData[this[0]]) { 8484 // _detailsShown as false or undefined will fall through to return false 8485 return ctx[0].aoData[this[0]]._detailsShow || false; 8486 } 8487 return false; 8488 }); 8489 8490 8491 8492 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8493 * Columns 8494 * 8495 * {integer} - column index (>=0 count from left, <0 count from right) 8496 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) 8497 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) 8498 * "{string}:name" - column name 8499 * "{string}" - jQuery selector on column header nodes 8500 * 8501 */ 8502 8503 // can be an array of these items, comma separated list, or an array of comma 8504 // separated lists 8505 8506 var __re_column_selector = /^([^:]+)?:(name|title|visIdx|visible)$/; 8507 8508 8509 // r1 and r2 are redundant - but it means that the parameters match for the 8510 // iterator callback in columns().data() 8511 var __columnData = function (settings, column, r1, r2, rows, type) { 8512 var a = []; 8513 for (var row = 0, iLen = rows.length; row < iLen; row++) { 8514 a.push(_fnGetCellData(settings, rows[row], column, type)); 8515 } 8516 return a; 8517 }; 8518 8519 8520 var __column_header = function (settings, column, row) { 8521 var header = settings.aoHeader; 8522 var titleRow = settings.titleRow; 8523 var target = null; 8524 8525 if (row !== undefined) { 8526 target = row; 8527 } 8528 else if (titleRow === true) { // legacy orderCellsTop support 8529 target = 0; 8530 } 8531 else if (titleRow === false) { 8532 target = header.length - 1; 8533 } 8534 else if (titleRow !== null) { 8535 target = titleRow; 8536 } 8537 else { 8538 // Automatic - find the _last_ unique cell from the top that is not empty (last for 8539 // backwards compatibility) 8540 for (var i = 0; i < header.length; i++) { 8541 if (header[i][column].unique && $('span.dt-column-title', header[i][column].cell).text()) { 8542 target = i; 8543 } 8544 } 8545 8546 if (target === null) { 8547 target = 0; 8548 } 8549 } 8550 8551 return header[target][column].cell; 8552 }; 8553 8554 var __column_header_cells = function (header) { 8555 var out = []; 8556 8557 for (var i = 0; i < header.length; i++) { 8558 for (var j = 0; j < header[i].length; j++) { 8559 var cell = header[i][j].cell; 8560 8561 if (!out.includes(cell)) { 8562 out.push(cell); 8563 } 8564 } 8565 } 8566 8567 return out; 8568 } 8569 8570 var __column_selector = function (settings, selector, opts) { 8571 var 8572 columns = settings.aoColumns, 8573 names, titles, 8574 nodes = __column_header_cells(settings.aoHeader); 8575 8576 var run = function (s) { 8577 var selInt = _intVal(s); 8578 8579 // Selector - all 8580 if (s === '') { 8581 return _range(columns.length); 8582 } 8583 8584 // Selector - index 8585 if (selInt !== null) { 8586 return [selInt >= 0 ? 8587 selInt : // Count from left 8588 columns.length + selInt // Count from right (+ because its a negative value) 8589 ]; 8590 } 8591 8592 // Selector = function 8593 if (typeof s === 'function') { 8594 var rows = _selector_row_indexes(settings, opts); 8595 8596 return columns.map(function (col, idx) { 8597 return s( 8598 idx, 8599 __columnData(settings, idx, 0, 0, rows), 8600 __column_header(settings, idx) 8601 ) ? idx : null; 8602 }); 8603 } 8604 8605 // jQuery or string selector 8606 var match = typeof s === 'string' ? 8607 s.match(__re_column_selector) : 8608 ''; 8609 8610 if (match) { 8611 switch (match[2]) { 8612 case 'visIdx': 8613 case 'visible': 8614 // Selector is a column index 8615 if (match[1] && match[1].match(/^\d+$/)) { 8616 var idx = parseInt(match[1], 10); 8617 8618 // Visible index given, convert to column index 8619 if (idx < 0) { 8620 // Counting from the right 8621 var visColumns = columns.map(function (col, i) { 8622 return col.bVisible ? i : null; 8623 }); 8624 return [visColumns[visColumns.length + idx]]; 8625 } 8626 // Counting from the left 8627 return [_fnVisibleToColumnIndex(settings, idx)]; 8628 } 8629 8630 return columns.map(function (col, idx) { 8631 // Not visible, can't match 8632 if (!col.bVisible) { 8633 return null; 8634 } 8635 8636 // Selector 8637 if (match[1]) { 8638 return $(nodes[idx]).filter(match[1]).length > 0 ? idx : null; 8639 } 8640 8641 // `:visible` on its own 8642 return idx; 8643 }); 8644 8645 case 'name': 8646 // Don't get names, unless needed, and only get once if it is 8647 if (!names) { 8648 names = _pluck(columns, 'sName'); 8649 } 8650 8651 // match by name. `names` is column index complete and in order 8652 return names.map(function (name, i) { 8653 return name === match[1] ? i : null; 8654 }); 8655 8656 case 'title': 8657 if (!titles) { 8658 titles = _pluck(columns, 'sTitle'); 8659 } 8660 8661 // match by column title 8662 return titles.map(function (title, i) { 8663 return title === match[1] ? i : null; 8664 }); 8665 8666 default: 8667 return []; 8668 } 8669 } 8670 8671 // Cell in the table body 8672 if (s.nodeName && s._DT_CellIndex) { 8673 return [s._DT_CellIndex.column]; 8674 } 8675 8676 // jQuery selector on the TH elements for the columns 8677 var jqResult = $(nodes) 8678 .filter(s) 8679 .map(function () { 8680 return _fnColumnsFromHeader(this); // `nodes` is column index complete and in order 8681 }) 8682 .toArray() 8683 .sort(function (a, b) { 8684 return a - b; 8685 }); 8686 8687 if (jqResult.length || !s.nodeName) {
8688 return jqResult; 8689 } 8690 8691 // Otherwise a node which might have a `dt-column` data attribute, or be 8692 // a child or such an element 8693 var host = $(s).closest('*[data-dt-column]'); 8694 return host.length ? 8695 [host.data('dt-column')] : 8696 []; 8697 }; 8698 8699 var selected = _selector_run('column', selector, run, settings, opts); 8700 8701 return opts.columnOrder && opts.columnOrder === 'index' 8702 ? selected.sort(function (a, b) { return a - b; }) 8703 : selected; // implied 8704 }; 8705 8706 8707 var __setColumnVis = function (settings, column, vis) { 8708 var 8709 cols = settings.aoColumns, 8710 col = cols[column], 8711 data = settings.aoData, 8712 cells, i, iLen, tr; 8713 8714 // Get 8715 if (vis === undefined) { 8716 return col.bVisible; 8717 } 8718 8719 // Set 8720 // No change 8721 if (col.bVisible === vis) { 8722 return false; 8723 } 8724 8725 if (vis) { 8726 // Insert column 8727 // Need to decide if we should use appendChild or insertBefore 8728 var insertBefore = _pluck(cols, 'bVisible').indexOf(true, column + 1); 8729 8730 for (i = 0, iLen = data.length; i < iLen; i++) { 8731 if (data[i]) { 8732 tr = data[i].nTr; 8733 cells = data[i].anCells; 8734 8735 if (tr) { 8736 // insertBefore can act like appendChild if 2nd arg is null 8737 tr.insertBefore(cells[column], cells[insertBefore] || null); 8738 } 8739 } 8740 } 8741 } 8742 else { 8743 // Remove column 8744 $(_pluck(settings.aoData, 'anCells', column)).detach(); 8745 } 8746 8747 // Common actions 8748 col.bVisible = vis; 8749 8750 _colGroup(settings); 8751 8752 return true; 8753 }; 8754 8755 8756 _api_register('columns()', function (selector, opts) { 8757 // argument shifting 8758 if (selector === undefined) { 8759 selector = ''; 8760 } 8761 else if ($.isPlainObject(selector)) { 8762 opts = selector; 8763 selector = ''; 8764 } 8765 8766 opts = _selector_opts(opts); 8767 8768 var inst = this.iterator('table', function (settings) { 8769 return __column_selector(settings, selector, opts); 8770 }, 1); 8771 8772 // Want argument shifting here and in _row_selector? 8773 inst.selector.cols = selector; 8774 inst.selector.opts = opts; 8775 8776 return inst; 8777 }); 8778 8779 _api_registerPlural('columns().header()', 'column().header()', function (row) { 8780 return this.iterator('column', function (settings, column) { 8781 return __column_header(settings, column, row); 8782 }, 1); 8783 }); 8784 8785 _api_registerPlural('columns().footer()', 'column().footer()', function (row) { 8786 return this.iterator('column', function (settings, column) { 8787 var footer = settings.aoFooter; 8788 8789 if (!footer.length) { 8790 return null; 8791 } 8792 8793 return settings.aoFooter[row !== undefined ? row : 0][column].cell; 8794 }, 1); 8795 }); 8796 8797 _api_registerPlural('columns().data()', 'column().data()', function () { 8798 return this.iterator('column-rows', __columnData, 1); 8799 }); 8800 8801 _api_registerPlural('columns().render()', 'column().render()', function (type) { 8802 return this.iterator('column-rows', function (settings, column, i, j, rows) { 8803 return __columnData(settings, column, i, j, rows, type); 8804 }, 1); 8805 }); 8806 8807 _api_registerPlural('columns().dataSrc()', 'column().dataSrc()', function () { 8808 return this.iterator('column', function (settings, column) { 8809 return settings.aoColumns[column].mData; 8810 }, 1); 8811 }); 8812 8813 _api_registerPlural('columns().cache()', 'column().cache()', function (type) { 8814 return this.iterator('column-rows', function (settings, column, i, j, rows) { 8815 return _pluck_order(settings.aoData, rows, 8816 type === 'search' ? '_aFilterData' : '_aSortData', column 8817 ); 8818 }, 1); 8819 }); 8820 8821 _api_registerPlural('columns().init()', 'column().init()', function () { 8822 return this.iterator('column', function (settings, column) { 8823 return settings.aoColumns[column]; 8824 }, 1); 8825 }); 8826 8827 _api_registerPlural('columns().names()', 'column().name()', function () { 8828 return this.iterator('column', function (settings, column) { 8829 return settings.aoColumns[column].sName; 8830 }, 1); 8831 }); 8832 8833 _api_registerPlural('columns().nodes()', 'column().nodes()', function () { 8834 return this.iterator('column-rows', function (settings, column, i, j, rows) { 8835 return _pluck_order(settings.aoData, rows, 'anCells', column); 8836 }, 1); 8837 }); 8838 8839 _api_registerPlural('columns().titles()', 'column().title()', function (title, row) { 8840 return this.iterator('column', function (settings, column) { 8841 // Argument shifting 8842 if (typeof title === 'number') { 8843 row = title; 8844 title = undefined; 8845 } 8846 8847 var span = $('span.dt-column-title', this.column(column).header(row)); 8848 8849 if (title !== undefined) { 8850 span.html(title); 8851 return this; 8852 } 8853 8854 return span.html(); 8855 }, 1); 8856 }); 8857 8858 _api_registerPlural('columns().types()', 'column().type()', function () { 8859 return this.iterator('column', function (settings, column) { 8860 var type = settings.aoColumns[column].sType; 8861
8862 // If the type was invalidated, then resolve it. This actually does 8863 // all columns at the moment. Would only happen once if getting all 8864 // column's data types. 8865 if (!type) { 8866 _fnColumnTypes(settings); 8867 } 8868 8869 return type; 8870 }, 1); 8871 }); 8872 8873 _api_registerPlural('columns().visible()', 'column().visible()', function (vis, calc) { 8874 var that = this; 8875 var changed = []; 8876 var ret = this.iterator('column', function (settings, column) { 8877 if (vis === undefined) { 8878 return settings.aoColumns[column].bVisible; 8879 } // else 8880 8881 if (__setColumnVis(settings, column, vis)) { 8882 changed.push(column); 8883 } 8884 }); 8885 8886 // Group the column visibility changes 8887 if (vis !== undefined) { 8888 this.iterator('table', function (settings) { 8889 // Redraw the header after changes 8890 _fnDrawHead(settings, settings.aoHeader); 8891 _fnDrawHead(settings, settings.aoFooter); 8892 8893 // Update colspan for no records display. Child rows and extensions will use their own 8894 // listeners to do this - only need to update the empty table item here 8895 if (!settings.aiDisplay.length) { 8896 $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisibleColumns(settings)); 8897 } 8898 8899 _fnSaveState(settings); 8900 8901 // Second loop once the first is done for events 8902 that.iterator('column', function (settings, column) { 8903 if (changed.includes(column)) { 8904 _fnCallbackFire(settings, null, 'column-visibility', [settings, column, vis, calc]); 8905 } 8906 }); 8907 8908 if (changed.length && (calc === undefined || calc)) { 8909 that.columns.adjust(); 8910 } 8911 }); 8912 } 8913 8914 return ret; 8915 }); 8916 8917 _api_registerPlural('columns().widths()', 'column().width()', function () { 8918 // Injects a fake row into the table for just a moment so the widths can 8919 // be read, regardless of colspan in the header and rows being present in 8920 // the body 8921 var columns = this.columns(':visible').count(); 8922 var row = $('<tr>').html('<td>' + Array(columns).join('</td><td>') + '</td>'); 8923 8924 $(this.table().body()).append(row); 8925 8926 var widths = row.children().map(function () { 8927 return $(this).outerWidth(); 8928 }); 8929 8930 row.remove(); 8931 8932 return this.iterator('column', function (settings, column) { 8933 var visIdx = _fnColumnIndexToVisible(settings, column); 8934 8935 return visIdx !== null ? widths[visIdx] : 0; 8936 }, 1); 8937 }); 8938 8939 _api_registerPlural('columns().indexes()', 'column().index()', function (type) { 8940 return this.iterator('column', function (settings, column) { 8941 return type === 'visible' ? 8942 _fnColumnIndexToVisible(settings, column) : 8943 column; 8944 }, 1); 8945 }); 8946 8947 _api_register('columns.adjust()', function () { 8948 return this.iterator('table', function (settings) { 8949 // Force a column sizing to happen with a manual call - otherwise it can skip 8950 // if the size hasn't changed 8951 settings.containerWidth = -1; 8952 8953 _fnAdjustColumnSizing(settings); 8954 }, 1); 8955 }); 8956 8957 _api_register('column.index()', function (type, idx) { 8958 if (this.context.length !== 0) { 8959 var ctx = this.context[0]; 8960 8961 if (type === 'fromVisible' || type === 'toData') { 8962 return _fnVisibleToColumnIndex(ctx, idx); 8963 } 8964 else if (type === 'fromData' || type === 'toVisible') { 8965 return _fnColumnIndexToVisible(ctx, idx); 8966 } 8967 } 8968 }); 8969 8970 _api_register('column()', function (selector, opts) { 8971 return _selector_first(this.columns(selector, opts)); 8972 }); 8973 8974 var __cell_selector = function (settings, selector, opts) { 8975 var data = settings.aoData; 8976 var rows = _selector_row_indexes(settings, opts); 8977 var cells = _removeEmpty(_pluck_order(data, rows, 'anCells')); 8978 var allCells = $(_flatten([], cells)); 8979 var row; 8980 var columns = settings.aoColumns.length; 8981 var a, i, iLen, j, o, host; 8982 8983 var run = function (s) { 8984 var fnSelector = typeof s === 'function'; 8985 8986 if (s === null || s === undefined || fnSelector) { 8987 // All cells and function selectors 8988 a = []; 8989 8990 for (i = 0, iLen = rows.length; i < iLen; i++) { 8991 row = rows[i]; 8992 8993 for (j = 0; j < columns; j++) { 8994 o = { 8995 row: row, 8996 column: j 8997 }; 8998 8999 if (fnSelector) { 9000 // Selector - function 9001 host = data[row]; 9002 9003 if (s(o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null)) { 9004 a.push(o); 9005 } 9006 } 9007 else { 9008 // Selector - all 9009 a.push(o); 9010 } 9011 } 9012 } 9013 9014 return a; 9015 } 9016 9017 // Selector - index 9018 if ($.isPlainObject(s)) { 9019 // Valid cell index and its in the array of selectable rows 9020 return s.column !== undefined && s.row !== undefined && rows.indexOf(s.row) !== -1 ? 9021 [s] : 9022 []; 9023 } 9024 9025 // Selector - jQuery filtered cells 9026 var jqResult = allCells 9027 .filter(s) 9028 .map(function (i, el) { 9029 return { // use a new object, in case someone changes the values 9030 row: el._DT_CellIndex.row, 9031 column: el._DT_CellIndex.column 9032 }; 9033 }) 9034 .toArray(); 9035 9036 if (jqResult.length || !s.nodeName) {
vendor: 4,033 bytes, lines 9037-9163
9037 return jqResult; 9038 } 9039 9040 // Otherwise the selector is a node, and there is one last option - the 9041 // element might be a child of an element which has dt-row and dt-column 9042 // data attributes 9043 host = $(s).closest('*[data-dt-row]'); 9044 return host.length ? 9045 [{ 9046 row: host.data('dt-row'), 9047 column: host.data('dt-column') 9048 }] : 9049 []; 9050 }; 9051 9052 return _selector_run('cell', selector, run, settings, opts); 9053 }; 9054 9055 9056 9057 9058 _api_register('cells()', function (rowSelector, columnSelector, opts) { 9059 // Argument shifting 9060 if ($.isPlainObject(rowSelector)) { 9061 // Indexes 9062 if (rowSelector.row === undefined) { 9063 // Selector options in first parameter 9064 opts = rowSelector; 9065 rowSelector = null; 9066 } 9067 else { 9068 // Cell index objects in first parameter 9069 opts = columnSelector; 9070 columnSelector = null; 9071 } 9072 } 9073 if ($.isPlainObject(columnSelector)) { 9074 opts = columnSelector; 9075 columnSelector = null; 9076 } 9077 9078 // Cell selector 9079 if (columnSelector === null || columnSelector === undefined) { 9080 return this.iterator('table', function (settings) { 9081 return __cell_selector(settings, rowSelector, _selector_opts(opts)); 9082 }); 9083 } 9084 9085 // The default built in options need to apply to row and columns 9086 var internalOpts = opts ? { 9087 page: opts.page, 9088 order: opts.order, 9089 search: opts.search 9090 } : {}; 9091 9092 // Row + column selector 9093 var columns = this.columns(columnSelector, internalOpts); 9094 var rows = this.rows(rowSelector, internalOpts); 9095 var i, iLen, j, jen; 9096 9097 var cellsNoOpts = this.iterator('table', function (settings, idx) { 9098 var a = []; 9099 9100 for (i = 0, iLen = rows[idx].length; i < iLen; i++) { 9101 for (j = 0, jen = columns[idx].length; j < jen; j++) { 9102 a.push({ 9103 row: rows[idx][i], 9104 column: columns[idx][j] 9105 }); 9106 } 9107 } 9108 9109 return a; 9110 }, 1); 9111 9112 // There is currently only one extension which uses a cell selector extension 9113 // It is a _major_ performance drag to run this if it isn't needed, so this is 9114 // an extension specific check at the moment 9115 var cells = opts && opts.selected ? 9116 this.cells(cellsNoOpts, opts) : 9117 cellsNoOpts; 9118 9119 $.extend(cells.selector, { 9120 cols: columnSelector, 9121 rows: rowSelector, 9122 opts: opts 9123 }); 9124 9125 return cells; 9126 }); 9127 9128 9129 _api_registerPlural('cells().nodes()', 'cell().node()', function () { 9130 return this.iterator('cell', function (settings, row, column) { 9131 var data = settings.aoData[row]; 9132 9133 return data && data.anCells ? 9134 data.anCells[column] : 9135 undefined; 9136 }, 1); 9137 }); 9138 9139 9140 _api_register('cells().data()', function () { 9141 return this.iterator('cell', function (settings, row, column) { 9142 return _fnGetCellData(settings, row, column); 9143 }, 1); 9144 }); 9145 9146 9147 _api_registerPlural('cells().cache()', 'cell().cache()', function (type) { 9148 type = type === 'search' ? '_aFilterData' : '_aSortData'; 9149 9150 return this.iterator('cell', function (settings, row, column) { 9151 return settings.aoData[row][type][column]; 9152 }, 1); 9153 }); 9154 9155 9156 _api_registerPlural('cells().render()', 'cell().render()', function (type) { 9157 return this.iterator('cell', function (settings, row, column) { 9158 return _fnGetCellData(settings, row, column, type); 9159 }, 1); 9160 }); 9161 9162 9163 _api_registerPlural('cells().indexes()', 'cell().index()', function () {
9164 return this.iterator('cell', function (settings, row, column) { 9165 return { 9166 row: row, 9167 column: column, 9168 columnVisible: _fnColumnIndexToVisible(settings, column) 9169 }; 9170 }, 1); 9171 }); 9172 9173 9174 _api_registerPlural('cells().invalidate()', 'cell().invalidate()', function (src) { 9175 return this.iterator('cell', function (settings, row, column) { 9176 _fnInvalidate(settings, row, src, column); 9177 }); 9178 }); 9179 9180 9181 9182 _api_register('cell()', function (rowSelector, columnSelector, opts) { 9183 return _selector_first(this.cells(rowSelector, columnSelector, opts)); 9184 }); 9185 9186 9187 _api_register('cell().data()', function (data) { 9188 var ctx = this.context; 9189 var cell = this[0]; 9190 9191 if (data === undefined) { 9192 // Get 9193 return ctx.length && cell.length ? 9194 _fnGetCellData(ctx[0], cell[0].row, cell[0].column) : 9195 undefined; 9196 } 9197 9198 // Set 9199 _fnSetCellData(ctx[0], cell[0].row, cell[0].column, data); 9200 _fnInvalidate(ctx[0], cell[0].row, 'data', cell[0].column); 9201 9202 return this; 9203 }); 9204 9205 9206 9207 /** 9208 * Get current ordering (sorting) that has been applied to the table. 9209 * 9210 * @returns {array} 2D array containing the sorting information for the first 9211 * table in the current context. Each element in the parent array represents 9212 * a column being sorted upon (i.e. multi-sorting with two columns would have 9213 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is 9214 * the column index that the sorting condition applies to, the second is the 9215 * direction of the sort (`desc` or `asc`) and, optionally, the third is the 9216 * index of the sorting order from the `column.sorting` initialisation array. 9217 *//** 9218 * Set the ordering for the table. 9219 * 9220 * @param {integer} order Column index to sort upon. 9221 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`) 9222 * @returns {DataTables.Api} this 9223 *//** 9224 * Set the ordering for the table. 9225 * 9226 * @param {array} order 1D array of sorting information to be applied. 9227 * @param {array} [...] Optional additional sorting conditions 9228 * @returns {DataTables.Api} this 9229 *//** 9230 * Set the ordering for the table. 9231 * 9232 * @param {array} order 2D array of sorting information to be applied. 9233 * @returns {DataTables.Api} this 9234 */ 9235 _api_register('order()', function (order, dir) { 9236 var ctx = this.context; 9237 var args = Array.prototype.slice.call(arguments); 9238 9239 if (order === undefined) { 9240 // get 9241 return ctx.length !== 0 ? 9242 ctx[0].aaSorting : 9243 undefined; 9244 } 9245 9246 // set 9247 if (typeof order === 'number') { 9248 // Simple column / direction passed in 9249 order = [[order, dir]]; 9250 } 9251 else if (args.length > 1) { 9252 // Arguments passed in (list of 1D arrays) 9253 order = args; 9254 } 9255 // otherwise a 2D array was passed in 9256 9257 return this.iterator('table', function (settings) { 9258 var resolved = []; 9259 _fnSortResolve(settings, resolved, order); 9260 9261 settings.aaSorting = resolved; 9262 }); 9263 }); 9264 9265 9266 /** 9267 * Attach a sort listener to an element for a given column 9268 * 9269 * @param {node|jQuery|string} node Identifier for the element(s) to attach the 9270 * listener to. This can take the form of a single DOM node, a jQuery 9271 * collection of nodes or a jQuery selector which will identify the node(s). 9272 * @param {integer} column the column that a click on this node will sort on 9273 * @param {function} [callback] callback function when sort is run 9274 * @returns {DataTables.Api} this 9275 */ 9276 _api_register('order.listener()', function (node, column, callback) { 9277 return this.iterator('table', function (settings) { 9278 _fnSortAttachListener(settings, node, {}, column, callback); 9279 }); 9280 }); 9281 9282 9283 _api_register('order.fixed()', function (set) { 9284 if (!set) { 9285 var ctx = this.context; 9286 var fixed = ctx.length ? 9287 ctx[0].aaSortingFixed : 9288 undefined; 9289 9290 return Array.isArray(fixed) ? 9291 { pre: fixed } : 9292 fixed; 9293 } 9294 9295 return this.iterator('table', function (settings) { 9296 settings.aaSortingFixed = $.extend(true, {}, set); 9297 }); 9298 }); 9299 9300 9301 // Order by the selected column(s) 9302 _api_register([ 9303 'columns().order()', 9304 'column().order()' 9305 ], function (dir) { 9306 var that = this; 9307 9308 if (!dir) { 9309 return this.iterator('column', function (settings, idx) { 9310 var sort = _fnSortFlatten(settings); 9311 9312 for (var i = 0, iLen = sort.length; i < iLen; i++) { 9313 if (sort[i].col === idx) { 9314 return sort[i].dir; 9315 } 9316 } 9317 9318 return null; 9319 }, 1); 9320 } 9321 else { 9322 return this.iterator('table', function (settings, i) { 9323 settings.aaSorting = that[i].map(function (col) { 9324 return [col, dir]; 9325 }); 9326 }); 9327 } 9328 }); 9329 9330 _api_registerPlural('columns().orderable()', 'column().orderable()', function (directions) { 9331 return this.iterator('column', function (settings, idx) { 9332 var col = settings.aoColumns[idx]; 9333 9334 return directions ? 9335 col.asSorting : 9336 col.bSortable; 9337 }, 1); 9338 }); 9339 9340 9341 _api_register('processing()', function (show) { 9342 return this.iterator('table', function (ctx) { 9343 _fnProcessingDisplay(ctx, show); 9344 }); 9345 }); 9346 9347 9348 _api_register('search()', function (input, regex, smart, caseInsen) { 9349 var ctx = this.context; 9350 9351 if (input === undefined) { 9352 // get 9353 return ctx.length !== 0 ? 9354 ctx[0].oPreviousSearch.search : 9355 undefined; 9356 } 9357 9358 // set 9359 return this.iterator('table', function (settings) { 9360 if (!settings.oFeatures.bFilter) { 9361 return; 9362 } 9363 9364 if (typeof regex === 'object') { 9365 // New style options to pass to the search builder 9366 _fnFilterComplete(settings, $.extend(settings.oPreviousSearch, regex, { 9367 search: input 9368 })); 9369 } 9370 else { 9371 // Compat for the old options 9372 _fnFilterComplete(settings, $.extend(settings.oPreviousSearch, { 9373 search: input, 9374 regex: regex === null ? false : regex,
9375 smart: smart === null ? true : smart, 9376 caseInsensitive: caseInsen === null ? true : caseInsen 9377 })); 9378 } 9379 }); 9380 }); 9381 9382 _api_register('search.fixed()', function (name, search) { 9383 var ret = this.iterator(true, 'table', function (settings) { 9384 var fixed = settings.searchFixed; 9385 9386 if (!name) { 9387 return Object.keys(fixed); 9388 } 9389 else if (search === undefined) { 9390 return fixed[name]; 9391 } 9392 else if (search === null) { 9393 delete fixed[name]; 9394 } 9395 else { 9396 fixed[name] = search; 9397 } 9398 9399 return this; 9400 }); 9401 9402 return name !== undefined && search === undefined 9403 ? ret[0] 9404 : ret; 9405 }); 9406 9407 _api_registerPlural( 9408 'columns().search()', 9409 'column().search()', 9410 function (input, regex, smart, caseInsen) { 9411 return this.iterator('column', function (settings, column) { 9412 var preSearch = settings.aoPreSearchCols; 9413 9414 if (input === undefined) { 9415 // get 9416 return preSearch[column].search; 9417 } 9418 9419 // set 9420 if (!settings.oFeatures.bFilter) { 9421 return; 9422 } 9423 9424 if (typeof regex === 'object') { 9425 // New style options to pass to the search builder 9426 $.extend(preSearch[column], regex, { 9427 search: input 9428 }); 9429 } 9430 else { 9431 // Old style (with not all options available) 9432 $.extend(preSearch[column], { 9433 search: input, 9434 regex: regex === null ? false : regex, 9435 smart: smart === null ? true : smart, 9436 caseInsensitive: caseInsen === null ? true : caseInsen 9437 }); 9438 } 9439 9440 _fnFilterComplete(settings, settings.oPreviousSearch); 9441 }); 9442 } 9443 ); 9444 9445 _api_register([ 9446 'columns().search.fixed()', 9447 'column().search.fixed()' 9448 ], 9449 function (name, search) { 9450 var ret = this.iterator(true, 'column', function (settings, colIdx) { 9451 var fixed = settings.aoColumns[colIdx].searchFixed; 9452 9453 if (!name) { 9454 return Object.keys(fixed); 9455 } 9456 else if (search === undefined) { 9457 return fixed[name] || null; 9458 } 9459 else if (search === null) { 9460 delete fixed[name]; 9461 } 9462 else { 9463 fixed[name] = search; 9464 } 9465 9466 return this; 9467 }); 9468 9469 return name !== undefined && search === undefined 9470 ? ret[0] 9471 : ret; 9472 } 9473 ); 9474 /* 9475 * State API methods 9476 */ 9477 9478 _api_register('state()', function (set, ignoreTime) { 9479 // getter 9480 if (!set) { 9481 return this.context.length ? 9482 this.context[0].oSavedState : 9483 null; 9484 } 9485 9486 var setMutate = $.extend(true, {}, set); 9487 9488 // setter 9489 return this.iterator('table', function (settings) { 9490 if (ignoreTime !== false) { 9491 setMutate.time = +new Date() + 100; 9492 } 9493 9494 _fnImplementState(settings, setMutate, function () { }); 9495 }); 9496 }); 9497 9498 9499 _api_register('state.clear()', function () { 9500 return this.iterator('table', function (settings) { 9501 // Save an empty object 9502 settings.fnStateSaveCallback.call(settings.oInstance, settings, {}); 9503 }); 9504 }); 9505 9506 9507 _api_register('state.loaded()', function () { 9508 return this.context.length ? 9509 this.context[0].oLoadedState : 9510 null; 9511 }); 9512 9513 9514 _api_register('state.save()', function () { 9515 return this.iterator('table', function (settings) {
9516 _fnSaveState(settings); 9517 }); 9518 }); 9519 9520 // Can be assigned in DateTable.use() - note luxon and moment vars are in helpers.js 9521 var __bootstrap; 9522 var __foundation; 9523 9524 /** 9525 * Set the libraries that DataTables uses, or the global objects. 9526 * Note that the arguments can be either way around (legacy support) 9527 * and the second is optional. See docs. 9528 */ 9529 DataTable.use = function (arg1, arg2) { 9530 // Reverse arguments for legacy support 9531 var module = typeof arg1 === 'string' 9532 ? arg2 9533 : arg1; 9534 var type = typeof arg2 === 'string' 9535 ? arg2 9536 : arg1; 9537 9538 // Getter 9539 if (module === undefined && typeof type === 'string') { 9540 switch (type) { 9541 case 'lib': 9542 case 'jq': 9543 return $; 9544 9545 case 'win': 9546 return window; 9547 9548 case 'datetime': 9549 return DataTable.DateTime; 9550 9551 case 'luxon': 9552 return __luxon; 9553 9554 case 'moment': 9555 return __moment; 9556 9557 case 'bootstrap': 9558 // Use local if set, otherwise try window, which could be undefined 9559 return __bootstrap || window.bootstrap; 9560 9561 case 'foundation': 9562 // Ditto 9563 return __foundation || window.Foundation; 9564 9565 default: 9566 return null; 9567 } 9568 } 9569 9570 // Setter 9571 if (type === 'lib' || type === 'jq' || (module && module.fn && module.fn.jquery)) { 9572 $ = module; 9573 } 9574 else if (type === 'win' || (module && module.document)) { 9575 window = module; 9576 document = module.document; 9577 } 9578 else if (type === 'datetime' || (module && module.type === 'DateTime')) { 9579 DataTable.DateTime = module; 9580 } 9581 else if (type === 'luxon' || (module && module.FixedOffsetZone)) { 9582 __luxon = module; 9583 } 9584 else if (type === 'moment' || (module && module.isMoment)) { 9585 __moment = module; 9586 } 9587 else if (type === 'bootstrap' || (module && module.Modal && module.Modal.NAME === 'modal')) { 9588 // This is currently for BS5 only. BS3/4 attach to jQuery, so no need to use `.use()` 9589 __bootstrap = module; 9590 } 9591 else if (type === 'foundation' || (module && module.Reveal)) { 9592 __foundation = module; 9593 } 9594 } 9595 9596 /** 9597 * CommonJS factory function pass through. This will check if the arguments 9598 * given are a window object or a jQuery object. If so they are set 9599 * accordingly. 9600 * @param {*} root Window 9601 * @param {*} jq jQUery 9602 * @returns {boolean} Indicator 9603 */ 9604 DataTable.factory = function (root, jq) { 9605 var is = false; 9606 9607 // Test if the first parameter is a window object 9608 if (root && root.document) { 9609 window = root; 9610 document = root.document; 9611 } 9612 9613 // Test if the second parameter is a jQuery object 9614 if (jq && jq.fn && jq.fn.jquery) { 9615 $ = jq; 9616 is = true; 9617 } 9618 9619 return is; 9620 } 9621 9622 /** 9623 * Provide a common method for plug-ins to check the version of DataTables being 9624 * used, in order to ensure compatibility. 9625 * 9626 * @param {string} version Version string to check for, in the format "X.Y.Z". 9627 * Note that the formats "X" and "X.Y" are also acceptable. 9628 * @param {string} [version2=current DataTables version] As above, but optional. 9629 * If not given the current DataTables version will be used. 9630 * @returns {boolean} true if this version of DataTables is greater or equal to 9631 * the required version, or false if this version of DataTales is not 9632 * suitable 9633 * @static 9634 * @dtopt API-Static 9635 * 9636 * @example 9637 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) ); 9638 */ 9639 DataTable.versionCheck = function (version, version2) { 9640 var aThis = version2 ? 9641 version2.split('.') :
vendor: 4,354 bytes, lines 9642-9783
9642 DataTable.version.split('.'); 9643 var aThat = version.split('.'); 9644 var iThis, iThat; 9645 9646 for (var i = 0, iLen = aThat.length; i < iLen; i++) { 9647 iThis = parseInt(aThis[i], 10) || 0; 9648 iThat = parseInt(aThat[i], 10) || 0; 9649 9650 // Parts are the same, keep comparing 9651 if (iThis === iThat) { 9652 continue; 9653 } 9654 9655 // Parts are different, return immediately 9656 return iThis > iThat; 9657 } 9658 9659 return true; 9660 }; 9661 9662 9663 /** 9664 * Check if a `<table>` node is a DataTable table already or not. 9665 * 9666 * @param {node|jquery|string} table Table node, jQuery object or jQuery 9667 * selector for the table to test. Note that if more than more than one 9668 * table is passed on, only the first will be checked 9669 * @returns {boolean} true the table given is a DataTable, or false otherwise 9670 * @static 9671 * @dtopt API-Static 9672 * 9673 * @example 9674 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { 9675 * $('#example').dataTable(); 9676 * } 9677 */ 9678 DataTable.isDataTable = function (table) { 9679 var t = $(table).get(0); 9680 var is = false; 9681 9682 if (table instanceof DataTable.Api) { 9683 return true; 9684 } 9685 9686 $.each(DataTable.settings, function (i, o) { 9687 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null; 9688 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null; 9689 9690 if (o.nTable === t || head === t || foot === t) { 9691 is = true; 9692 } 9693 }); 9694 9695 return is; 9696 }; 9697 9698 9699 /** 9700 * Get all DataTable tables that have been initialised - optionally you can 9701 * select to get only currently visible tables. 9702 * 9703 * @param {boolean} [visible=false] Flag to indicate if you want all (default) 9704 * or visible tables only. 9705 * @returns {array} Array of `table` nodes (not DataTable instances) which are 9706 * DataTables 9707 * @static 9708 * @dtopt API-Static 9709 * 9710 * @example 9711 * $.each( $.fn.dataTable.tables(true), function () { 9712 * $(table).DataTable().columns.adjust(); 9713 * } ); 9714 */ 9715 DataTable.tables = function (visible) { 9716 var api = false; 9717 9718 if ($.isPlainObject(visible)) { 9719 api = visible.api; 9720 visible = visible.visible; 9721 } 9722 9723 var a = DataTable.settings 9724 .filter(function (o) { 9725 return !visible || (visible && $(o.nTable).is(':visible')) 9726 ? true 9727 : false; 9728 }) 9729 .map(function (o) { 9730 return o.nTable; 9731 }); 9732 9733 return api ? 9734 new _Api(a) : 9735 a; 9736 }; 9737 9738 9739 /** 9740 * Convert from camel case parameters to Hungarian notation. This is made public 9741 * for the extensions to provide the same ability as DataTables core to accept 9742 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase 9743 * parameters. 9744 * 9745 * @param {object} src The model object which holds all parameters that can be 9746 * mapped. 9747 * @param {object} user The object to convert from camel case to Hungarian. 9748 * @param {boolean} force When set to `true`, properties which already have a 9749 * Hungarian value in the `user` object will be overwritten. Otherwise they 9750 * won't be. 9751 */ 9752 DataTable.camelToHungarian = _fnCamelToHungarian; 9753 9754 9755 9756 /** 9757 * 9758 */ 9759 _api_register('$()', function (selector, opts) { 9760 var 9761 rows = this.rows(opts).nodes(), // Get all rows 9762 jqRows = $(rows); 9763 9764 return $([].concat( 9765 jqRows.filter(selector).toArray(), 9766 jqRows.find(selector).toArray() 9767 )); 9768 }); 9769 9770 9771 // jQuery functions to operate on the tables 9772 $.each(['on', 'one', 'off'], function (i, key) { 9773 _api_register(key + '()', function ( /* event, handler */) { 9774 var args = Array.prototype.slice.call(arguments); 9775 9776 // Add the `dt` namespace automatically if it isn't already present 9777 args[0] = args[0].split(/\s/).map(function (e) { 9778 return !e.match(/\.dt\b/) ? 9779 e + '.dt' : 9780 e; 9781 }).join(' '); 9782 9783 var inst = $(this.tables().nodes());
9784 inst[key].apply(inst, args); 9785 return this; 9786 }); 9787 }); 9788 9789 9790 _api_register('clear()', function () { 9791 return this.iterator('table', function (settings) { 9792 _fnClearTable(settings); 9793 }); 9794 }); 9795 9796 9797 _api_register('error()', function (msg) { 9798 return this.iterator('table', function (settings) { 9799 _fnLog(settings, 0, msg); 9800 }); 9801 }); 9802 9803 9804 _api_register('settings()', function () { 9805 return new _Api(this.context, this.context); 9806 }); 9807 9808 9809 _api_register('init()', function () { 9810 var ctx = this.context; 9811 return ctx.length ? ctx[0].oInit : null; 9812 }); 9813 9814 9815 _api_register('data()', function () { 9816 return this.iterator('table', function (settings) { 9817 return _pluck(settings.aoData, '_aData'); 9818 }).flatten(); 9819 }); 9820 9821 9822 _api_register('trigger()', function (name, args, bubbles) { 9823 return this.iterator('table', function (settings) { 9824 return _fnCallbackFire(settings, null, name, args, bubbles); 9825 }).flatten(); 9826 }); 9827 9828 9829 _api_register('ready()', function (fn) { 9830 var ctx = this.context; 9831 9832 // Get status of first table 9833 if (!fn) { 9834 return ctx.length 9835 ? (ctx[0]._bInitComplete || false) 9836 : null; 9837 } 9838 9839 // Function to run either once the table becomes ready or 9840 // immediately if it is already ready. 9841 return this.tables().every(function () { 9842 var api = this; 9843 9844 if (this.context[0]._bInitComplete) { 9845 fn.call(api); 9846 } 9847 else { 9848 this.on('init.dt.DT', function () { 9849 fn.call(api); 9850 }); 9851 } 9852 }); 9853 }); 9854 9855 9856 _api_register('destroy()', function (remove) { 9857 remove = remove || false; 9858 9859 return this.iterator('table', function (settings) { 9860 var classes = settings.oClasses; 9861 var table = settings.nTable; 9862 var tbody = settings.nTBody; 9863 var thead = settings.nTHead; 9864 var tfoot = settings.nTFoot; 9865 var jqTable = $(table); 9866 var jqTbody = $(tbody); 9867 var jqWrapper = $(settings.nTableWrapper); 9868 var rows = settings.aoData.map(function (r) { return r ? r.nTr : null; }); 9869 var orderClasses = classes.order; 9870 9871 // Flag to note that the table is currently being destroyed - no action 9872 // should be taken 9873 settings.bDestroying = true; 9874 9875 // Fire off the destroy callbacks for plug-ins etc 9876 _fnCallbackFire(settings, "aoDestroyCallback", "destroy", [settings], true); 9877 9878 // If not being removed from the document, make all columns visible 9879 if (!remove) { 9880 new _Api(settings).columns().visible(true); 9881 } 9882 9883 // Container width change listener 9884 if (settings.resizeObserver) { 9885 settings.resizeObserver.disconnect(); 9886 } 9887 9888 // Blitz all `DT` namespaced events (these are internal events, the 9889 // lowercase, `dt` events are user subscribed and they are responsible 9890 // for removing them 9891 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT'); 9892 $(window).off('.DT-' + settings.sInstance); 9893 9894 // When scrolling we had to break the table up - restore it 9895 if (table != thead.parentNode) { 9896 jqTable.children('thead').detach(); 9897 jqTable.append(thead); 9898 } 9899 9900 if (tfoot && table != tfoot.parentNode) { 9901 jqTable.children('tfoot').detach(); 9902 jqTable.append(tfoot); 9903 } 9904 9905 // Clean up the header / footer 9906 cleanHeader(thead, 'header'); 9907 cleanHeader(tfoot, 'footer'); 9908 settings.colgroup.remove(); 9909 9910 settings.aaSorting = []; 9911 settings.aaSortingFixed = []; 9912 _fnSortingClasses(settings); 9913 9914 $(jqTable).find('th, td').removeClass( 9915 $.map(DataTable.ext.type.className, function (v) { 9916 return v; 9917 }).join(' ') 9918 ); 9919 9920 $('th, td', thead) 9921 .removeClass( 9922 orderClasses.none + ' ' + 9923 orderClasses.canAsc + ' ' + 9924 orderClasses.canDesc + ' ' + 9925 orderClasses.isAsc + ' ' + 9926 orderClasses.isDesc 9927 ) 9928 .css('width', '') 9929 .removeAttr('aria-sort'); 9930 9931 // Add the TR elements back into the table in their original order 9932 jqTbody.children().detach(); 9933 jqTbody.append(rows); 9934 9935 var orig = settings.nTableWrapper.parentNode; 9936 var insertBefore = settings.nTableWrapper.nextSibling; 9937 9938 // Remove the DataTables generated nodes, events and classes 9939 var removedMethod = remove ? 'remove' : 'detach'; 9940 jqTable[removedMethod](); 9941 jqWrapper[removedMethod](); 9942 9943 // If we need to reattach the table to the document 9944 if (!remove && orig) { 9945 // insertBefore acts like appendChild if !arg[1] 9946 orig.insertBefore(table, insertBefore); 9947 9948 // Restore the width of the original table - was read from the style property, 9949 // so we can restore directly to that 9950 jqTable 9951 .css('width', settings.sDestroyWidth)
9952 .removeClass(classes.table); 9953 } 9954 9955 /* Remove the settings object from the settings array */ 9956 var idx = DataTable.settings.indexOf(settings); 9957 if (idx !== -1) { 9958 DataTable.settings.splice(idx, 1); 9959 } 9960 }); 9961 }); 9962 9963 9964 // Add the `every()` method for rows, columns and cells in a compact form 9965 $.each(['column', 'row', 'cell'], function (i, type) { 9966 _api_register(type + 's().every()', function (fn) { 9967 var opts = this.selector.opts; 9968 var api = this; 9969 var inst; 9970 var counter = 0; 9971 9972 return this.iterator('every', function (settings, selectedIdx, tableIdx) { 9973 inst = api[type](selectedIdx, opts); 9974 9975 if (type === 'cell') { 9976 fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter); 9977 } 9978 else { 9979 fn.call(inst, selectedIdx, tableIdx, counter); 9980 } 9981 9982 counter++; 9983 }); 9984 }); 9985 }); 9986 9987 9988 // i18n method for extensions to be able to use the language object from the 9989 // DataTable 9990 _api_register('i18n()', function (token, def, plural) { 9991 var ctx = this.context[0]; 9992 var resolved = _fnGetObjectDataFn(token)(ctx.oLanguage); 9993 9994 if (resolved === undefined) { 9995 resolved = def; 9996 } 9997 9998 if ($.isPlainObject(resolved)) { 9999 resolved = plural !== undefined && resolved[plural] !== undefined 10000 ? resolved[plural] 10001 : plural === false 10002 ? resolved 10003 : resolved._; 10004 } 10005 10006 return typeof resolved === 'string' 10007 ? resolved.replace('%d', plural) // nb: plural might be undefined, 10008 : resolved; 10009 }); 10010 10011 // Needed for header and footer, so pulled into its own function 10012 function cleanHeader(node, className) { 10013 $(node).find('span.dt-column-order').remove(); 10014 $(node).find('span.dt-column-title').each(function () { 10015 var title = $(this).html(); 10016 $(this).parent().parent().append(title); 10017 $(this).remove(); 10018 }); 10019 $(node).find('div.dt-column-' + className).remove(); 10020 10021 $('th, td', node).removeAttr('data-dt-column'); 10022 } 10023 10024 /** 10025 * Version string for plug-ins to check compatibility. Allowed format is 10026 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used 10027 * only for non-release builds. See https://semver.org/ for more information. 10028 * @member 10029 * @type string 10030 * @default Version number 10031 */ 10032 DataTable.version = "2.3.4"; 10033 10034 /** 10035 * Private data store, containing all of the settings objects that are 10036 * created for the tables on a given page. 10037 * 10038 * Note that the `DataTable.settings` object is aliased to 10039 * `jQuery.fn.dataTableExt` through which it may be accessed and 10040 * manipulated, or `jQuery.fn.dataTable.settings`. 10041 * @member 10042 * @type array 10043 * @default [] 10044 * @private 10045 */ 10046 DataTable.settings = []; 10047 10048 /** 10049 * Object models container, for the various models that DataTables has 10050 * available to it. These models define the objects that are used to hold 10051 * the active state and configuration of the table. 10052 * @namespace 10053 */ 10054 DataTable.models = {}; 10055 10056 10057 10058 /** 10059 * Template object for the way in which DataTables holds information about 10060 * search information for the global filter and individual column filters. 10061 * @namespace 10062 */ 10063 DataTable.models.oSearch = { 10064 /** 10065 * Flag to whether or not the filtering should be case-insensitive 10066 */ 10067 "caseInsensitive": true, 10068 10069 /** 10070 * Applied search term 10071 */ 10072 "search": "", 10073 10074 /** 10075 * Flag to indicate if the search term should be interpreted as a 10076 * regular expression (true) or not (false) and therefore and special 10077 * regex characters escaped. 10078 */ 10079 "regex": false, 10080 10081 /** 10082 * Flag to indicate if DataTables is to use its smart filtering or not. 10083 */ 10084 "smart": true, 10085 10086 /** 10087 * Flag to indicate if DataTables should only trigger a search when 10088 * the return key is pressed. 10089 */ 10090 "return": false 10091 }; 10092 10093 10094 10095 10096 /** 10097 * Template object for the way in which DataTables holds information about 10098 * each individual row. This is the object format used for the settings 10099 * aoData array.
10100 * @namespace 10101 */ 10102 DataTable.models.oRow = { 10103 /** 10104 * TR element for the row 10105 */ 10106 "nTr": null, 10107 10108 /** 10109 * Array of TD elements for each row. This is null until the row has been 10110 * created. 10111 */ 10112 "anCells": null, 10113 10114 /** 10115 * Data object from the original data source for the row. This is either 10116 * an array if using the traditional form of DataTables, or an object if 10117 * using mData options. The exact type will depend on the passed in 10118 * data from the data source, or will be an array if using DOM a data 10119 * source. 10120 */ 10121 "_aData": [], 10122 10123 /** 10124 * Sorting data cache - this array is ostensibly the same length as the 10125 * number of columns (although each index is generated only as it is 10126 * needed), and holds the data that is used for sorting each column in the 10127 * row. We do this cache generation at the start of the sort in order that 10128 * the formatting of the sort data need be done only once for each cell 10129 * per sort. This array should not be read from or written to by anything 10130 * other than the master sorting methods. 10131 */ 10132 "_aSortData": null, 10133 10134 /** 10135 * Per cell filtering data cache. As per the sort data cache, used to 10136 * increase the performance of the filtering in DataTables 10137 */ 10138 "_aFilterData": null, 10139 10140 /** 10141 * Filtering data cache. This is the same as the cell filtering cache, but 10142 * in this case a string rather than an array. This is easily computed with 10143 * a join on `_aFilterData`, but is provided as a cache so the join isn't 10144 * needed on every search (memory traded for performance) 10145 */ 10146 "_sFilterRow": null, 10147 10148 /** 10149 * Denote if the original data source was from the DOM, or the data source 10150 * object. This is used for invalidating data, so DataTables can 10151 * automatically read data from the original source, unless uninstructed 10152 * otherwise. 10153 */ 10154 "src": null, 10155 10156 /** 10157 * Index in the aoData array. This saves an indexOf lookup when we have the 10158 * object, but want to know the index 10159 */ 10160 "idx": -1, 10161 10162 /** 10163 * Cached display value 10164 */ 10165 displayData: null 10166 }; 10167 10168 10169 /** 10170 * Template object for the column information object in DataTables. This object 10171 * is held in the settings aoColumns array and contains all the information that 10172 * DataTables needs about each individual column. 10173 * 10174 * Note that this object is related to {@link DataTable.defaults.column} 10175 * but this one is the internal data store for DataTables's cache of columns. 10176 * It should NOT be manipulated outside of DataTables. Any configuration should 10177 * be done through the initialisation options. 10178 * @namespace 10179 */ 10180 DataTable.models.oColumn = { 10181 /** 10182 * Column index. 10183 */ 10184 "idx": null, 10185 10186 /** 10187 * A list of the columns that sorting should occur on when this column 10188 * is sorted. That this property is an array allows multi-column sorting 10189 * to be defined for a column (for example first name / last name columns 10190 * would benefit from this). The values are integers pointing to the 10191 * columns to be sorted on (typically it will be a single integer pointing 10192 * at itself, but that doesn't need to be the case). 10193 */ 10194 "aDataSort": null, 10195 10196 /** 10197 * Define the sorting directions that are applied to the column, in sequence 10198 * as the column is repeatedly sorted upon - i.e. the first value is used 10199 * as the sorting direction when the column if first sorted (clicked on). 10200 * Sort it again (click again) and it will move on to the next index. 10201 * Repeat until loop. 10202 */ 10203 "asSorting": null, 10204 10205 /** 10206 * Flag to indicate if the column is searchable, and thus should be included 10207 * in the filtering or not. 10208 */ 10209 "bSearchable": null, 10210 10211 /** 10212 * Flag to indicate if the column is sortable or not. 10213 */ 10214 "bSortable": null, 10215 10216 /** 10217 * Flag to indicate if the column is currently visible in the table or not 10218 */ 10219 "bVisible": null, 10220 10221 /** 10222 * Store for manual type assignment using the `column.type` option. This 10223 * is held in store so we can manipulate the column's `sType` property. 10224 */ 10225 "_sManualType": null, 10226 10227 /** 10228 * Flag to indicate if HTML5 data attributes should be used as the data 10229 * source for filtering or sorting. True is either are. 10230 */ 10231 "_bAttrSrc": false, 10232 10233 /** 10234 * Developer definable function that is called whenever a cell is created (Ajax source, 10235 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 10236 * allowing you to modify the DOM element (add background colour for example) when the 10237 * element is available. 10238 */ 10239 "fnCreatedCell": null, 10240 10241 /** 10242 * Function to get data from a cell in a column. You should <b>never</b> 10243 * access data directly through _aData internally in DataTables - always use 10244 * the method attached to this property. It allows mData to function as 10245 * required. This function is automatically assigned by the column 10246 * initialisation method 10247 */ 10248 "fnGetData": null, 10249 10250 /** 10251 * Function to set data for a cell in the column. You should <b>never</b> 10252 * set the data directly to _aData internally in DataTables - always use 10253 * this method. It allows mData to function as required. This function 10254 * is automatically assigned by the column initialisation method 10255 */ 10256 "fnSetData": null, 10257 10258 /** 10259 * Property to read the value for the cells in the column from the data 10260 * source array / object. If null, then the default content is used, if a 10261 * function is given then the return from the function is used. 10262 */ 10263 "mData": null, 10264 10265 /** 10266 * Partner property to mData which is used (only when defined) to get 10267 * the data - i.e. it is basically the same as mData, but without the 10268 * 'set' option, and also the data fed to it is the result from mData. 10269 * This is the rendering method to match the data method of mData. 10270 */ 10271 "mRender": null, 10272 10273 /** 10274 * The class to apply to all TD elements in the table's TBODY for the column 10275 */ 10276 "sClass": null, 10277 10278 /** 10279 * When DataTables calculates the column widths to assign to each column, 10280 * it finds the longest string in each column and then constructs a 10281 * temporary table and reads the widths from that. The problem with this 10282 * is that "mmm" is much wider then "iiii", but the latter is a longer 10283 * string - thus the calculation can go wrong (doing it properly and putting 10284 * it into an DOM object and measuring that is horribly(!) slow). Thus as 10285 * a "work around" we provide this option. It will append its value to the 10286 * text that is found to be the longest string for the column - i.e. padding. 10287 */ 10288 "sContentPadding": null, 10289 10290 /** 10291 * Allows a default value to be given for a column's data, and will be used 10292 * whenever a null data source is encountered (this can be because mData 10293 * is set to null, or because the data source itself is null). 10294 */ 10295 "sDefaultContent": null, 10296 10297 /** 10298 * Name for the column, allowing reference to the column by name as well as 10299 * by index (needs a lookup to work by name). 10300 */ 10301 "sName": null, 10302 10303 /** 10304 * Custom sorting data type - defines which of the available plug-ins in 10305 * afnSortData the custom sorting will use - if any is defined. 10306 */ 10307 "sSortDataType": 'std', 10308 10309 /** 10310 * Class to be applied to the header element when sorting on this column 10311 */ 10312 "sSortingClass": null, 10313 10314 /** 10315 * Title of the column - what is seen in the TH element (nTh). 10316 */ 10317 "sTitle": null, 10318 10319 /** 10320 * Column sorting and filtering type 10321 */ 10322 "sType": null, 10323 10324 /** 10325 * Width of the column 10326 */ 10327 "sWidth": null, 10328 10329 /** 10330 * Width of the column when it was first "encountered" 10331 */ 10332 "sWidthOrig": null, 10333 10334 /** Cached string which is the longest in the column */ 10335 maxLenString: null, 10336 10337 /** 10338 * Store for named searches 10339 */ 10340 searchFixed: null 10341 }; 10342 10343 10344 /* 10345 * Developer note: The properties of the object below are given in Hungarian 10346 * notation, that was used as the interface for DataTables prior to v1.10, however 10347 * from v1.10 onwards the primary interface is camel case. In order to avoid 10348 * breaking backwards compatibility utterly with this change, the Hungarian 10349 * version is still, internally the primary interface, but is is not documented 10350 * - hence the @name tags in each doc comment. This allows a JavaScript function 10351 * to create a map from Hungarian notation to camel case (going the other direction 10352 * would require each property to be listed, which would add around 3K to the size 10353 * of DataTables, while this method is about a 0.5K hit). 10354 * 10355 * Ultimately this does pave the way for Hungarian notation to be dropped 10356 * completely, but that is a massive amount of work and will break current 10357 * installs (therefore is on-hold until v2). 10358 */ 10359 10360 /** 10361 * Initialisation options that can be given to DataTables at initialisation 10362 * time. 10363 * @namespace 10364 */ 10365 DataTable.defaults = { 10366 /** 10367 * An array of data to use for the table, passed in at initialisation which 10368 * will be used in preference to any data which is already in the DOM. This is 10369 * particularly useful for constructing tables purely in JavaScript, for 10370 * example with a custom Ajax call. 10371 */ 10372 "aaData": null, 10373 10374 10375 /** 10376 * If ordering is enabled, then DataTables will perform a first pass sort on 10377 * initialisation. You can define which column(s) the sort is performed 10378 * upon, and the sorting direction, with this variable. The `sorting` array 10379 * should contain an array for each column to be sorted initially containing 10380 * the column's index and a direction string ('asc' or 'desc'). 10381 */ 10382 "aaSorting": [[0, 'asc']], 10383 10384 10385 /** 10386 * This parameter is basically identical to the `sorting` parameter, but 10387 * cannot be overridden by user interaction with the table. What this means 10388 * is that you could have a column (visible or hidden) which the sorting 10389 * will always be forced on first - any sorting after that (from the user) 10390 * will then be performed as required. This can be useful for grouping rows 10391 * together. 10392 */ 10393 "aaSortingFixed": [], 10394 10395 10396 /** 10397 * DataTables can be instructed to load data to display in the table from a 10398 * Ajax source. This option defines how that Ajax call is made and where to. 10399 * 10400 * The `ajax` property has three different modes of operation, depending on 10401 * how it is defined. These are: 10402 * 10403 * * `string` - Set the URL from where the data should be loaded from. 10404 * * `object` - Define properties for `jQuery.ajax`. 10405 * * `function` - Custom data get function 10406 * 10407 * `string` 10408 * -------- 10409 * 10410 * As a string, the `ajax` property simply defines the URL from which 10411 * DataTables will load data. 10412 * 10413 * `object` 10414 * -------- 10415 * 10416 * As an object, the parameters in the object are passed to 10417 * [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) allowing fine control
10418 * of the Ajax request. DataTables has a number of default parameters which 10419 * you can override using this option. Please refer to the jQuery 10420 * documentation for a full description of the options available, although 10421 * the following parameters provide additional options in DataTables or 10422 * require special consideration: 10423 * 10424 * * `data` - As with jQuery, `data` can be provided as an object, but it 10425 * can also be used as a function to manipulate the data DataTables sends 10426 * to the server. The function takes a single parameter, an object of 10427 * parameters with the values that DataTables has readied for sending. An 10428 * object may be returned which will be merged into the DataTables 10429 * defaults, or you can add the items to the object that was passed in and 10430 * not return anything from the function. This supersedes `fnServerParams` 10431 * from DataTables 1.9-. 10432 * 10433 * * `dataSrc` - By default DataTables will look for the property `data` (or 10434 * `aaData` for compatibility with DataTables 1.9-) when obtaining data 10435 * from an Ajax source or for server-side processing - this parameter 10436 * allows that property to be changed. You can use JavaScript dotted 10437 * object notation to get a data source for multiple levels of nesting, or 10438 * it my be used as a function. As a function it takes a single parameter, 10439 * the JSON returned from the server, which can be manipulated as 10440 * required, with the returned value being that used by DataTables as the 10441 * data source for the table. 10442 * 10443 * * `success` - Should not be overridden it is used internally in 10444 * DataTables. To manipulate / transform the data returned by the server 10445 * use `ajax.dataSrc`, or use `ajax` as a function (see below). 10446 * 10447 * `function` 10448 * ---------- 10449 * 10450 * As a function, making the Ajax call is left up to yourself allowing 10451 * complete control of the Ajax request. Indeed, if desired, a method other 10452 * than Ajax could be used to obtain the required data, such as Web storage 10453 * or an AIR database. 10454 * 10455 * The function is given four parameters and no return is required. The 10456 * parameters are: 10457 * 10458 * 1. _object_ - Data to send to the server 10459 * 2. _function_ - Callback function that must be executed when the required 10460 * data has been obtained. That data should be passed into the callback 10461 * as the only parameter 10462 * 3. _object_ - DataTables settings object for the table 10463 */ 10464 "ajax": null, 10465 10466 10467 /** 10468 * This parameter allows you to readily specify the entries in the length drop 10469 * down menu that DataTables shows when pagination is enabled. It can be 10470 * either a 1D array of options which will be used for both the displayed 10471 * option and the value, or a 2D array which will use the array in the first 10472 * position as the value, and the array in the second position as the 10473 * displayed options (useful for language strings such as 'All'). 10474 * 10475 * Note that the `pageLength` property will be automatically set to the 10476 * first value given in this array, unless `pageLength` is also provided. 10477 */ 10478 "aLengthMenu": [10, 25, 50, 100], 10479 10480 10481 /** 10482 * The `columns` option in the initialisation parameter allows you to define 10483 * details about the way individual columns behave. For a full list of 10484 * column options that can be set, please see 10485 * {@link DataTable.defaults.column}. Note that if you use `columns` to 10486 * define your columns, you must have an entry in the array for every single 10487 * column that you have in your table (these can be null if you don't which 10488 * to specify any options). 10489 */ 10490 "aoColumns": null, 10491 10492 /**
10493 * Very similar to `columns`, `columnDefs` allows you to target a specific 10494 * column, multiple columns, or all columns, using the `targets` property of 10495 * each object in the array. This allows great flexibility when creating 10496 * tables, as the `columnDefs` arrays can be of any length, targeting the 10497 * columns you specifically want. `columnDefs` may use any of the column 10498 * options available: {@link DataTable.defaults.column}, but it _must_ 10499 * have `targets` defined in each object in the array. Values in the `targets` 10500 * array may be: 10501 * <ul> 10502 * <li>a string - class name will be matched on the TH for the column</li> 10503 * <li>0 or a positive integer - column index counting from the left</li> 10504 * <li>a negative integer - column index counting from the right</li> 10505 * <li>the string "_all" - all columns (i.e. assign a default)</li> 10506 * </ul> 10507 */ 10508 "aoColumnDefs": null, 10509 10510 10511 /** 10512 * Basically the same as `search`, this parameter defines the individual column 10513 * filtering state at initialisation time. The array must be of the same size 10514 * as the number of columns, and each element be an object with the parameters 10515 * `search` and `escapeRegex` (the latter is optional). 'null' is also 10516 * accepted and the default will be used. 10517 */ 10518 "aoSearchCols": [], 10519 10520 10521 /** 10522 * Enable or disable automatic column width calculation. This can be disabled 10523 * as an optimisation (it takes some time to calculate the widths) if the 10524 * tables widths are passed in using `columns`. 10525 */ 10526 "bAutoWidth": true, 10527 10528 10529 /** 10530 * Deferred rendering can provide DataTables with a huge speed boost when you 10531 * are using an Ajax or JS data source for the table. This option, when set to 10532 * true, will cause DataTables to defer the creation of the table elements for 10533 * each row until they are needed for a draw - saving a significant amount of 10534 * time. 10535 */ 10536 "bDeferRender": true, 10537 10538 10539 /** 10540 * Replace a DataTable which matches the given selector and replace it with 10541 * one which has the properties of the new initialisation object passed. If no 10542 * table matches the selector, then the new DataTable will be constructed as 10543 * per normal. 10544 */ 10545 "bDestroy": false, 10546 10547 10548 /** 10549 * Enable or disable filtering of data. Filtering in DataTables is "smart" in 10550 * that it allows the end user to input multiple words (space separated) and 10551 * will match a row containing those words, even if not in the order that was 10552 * specified (this allow matching across multiple columns). Note that if you 10553 * wish to use filtering in DataTables this must remain 'true' - to remove the 10554 * default filtering input box and retain filtering abilities, please use 10555 * {@link DataTable.defaults.dom}. 10556 */ 10557 "bFilter": true, 10558 10559 /** 10560 * Used only for compatibility with DT1 10561 * @deprecated 10562 */ 10563 "bInfo": true, 10564 10565 /** 10566 * Used only for compatibility with DT1 10567 * @deprecated 10568 */ 10569 "bLengthChange": true, 10570 10571 /** 10572 * Enable or disable pagination. 10573 */ 10574 "bPaginate": true, 10575 10576 10577 /** 10578 * Enable or disable the display of a 'processing' indicator when the table is 10579 * being processed (e.g. a sort). This is particularly useful for tables with 10580 * large amounts of data where it can take a noticeable amount of time to sort 10581 * the entries. 10582 */ 10583 "bProcessing": false, 10584 10585 10586 /** 10587 * Retrieve the DataTables object for the given selector. Note that if the 10588 * table has already been initialised, this parameter will cause DataTables 10589 * to simply return the object that has already been set up - it will not take 10590 * account of any changes you might have made to the initialisation object 10591 * passed to DataTables (setting this parameter to true is an acknowledgement 10592 * that you understand this). `destroy` can be used to reinitialise a table if 10593 * you need. 10594 */ 10595 "bRetrieve": false, 10596 10597 10598 /** 10599 * When vertical (y) scrolling is enabled, DataTables will force the height of
10600 * the table's viewport to the given height at all times (useful for layout). 10601 * However, this can look odd when filtering data down to a small data set, 10602 * and the footer is left "floating" further down. This parameter (when 10603 * enabled) will cause DataTables to collapse the table's viewport down when 10604 * the result set will fit within the given Y height. 10605 */ 10606 "bScrollCollapse": false, 10607 10608 10609 /** 10610 * Configure DataTables to use server-side processing. Note that the 10611 * `ajax` parameter must also be given in order to give DataTables a 10612 * source to obtain the required data for each draw. 10613 */ 10614 "bServerSide": false, 10615 10616 10617 /** 10618 * Enable or disable sorting of columns. Sorting of individual columns can be 10619 * disabled by the `sortable` option for each column. 10620 */ 10621 "bSort": true, 10622 10623 10624 /** 10625 * Enable or display DataTables' ability to sort multiple columns at the 10626 * same time (activated by shift-click by the user). 10627 */ 10628 "bSortMulti": true, 10629 10630 10631 /** 10632 * Allows control over whether DataTables should use the top (true) unique 10633 * cell that is found for a single column, or the bottom (false - default). 10634 * This is useful when using complex headers. 10635 */ 10636 "bSortCellsTop": null, 10637 10638 10639 /** Specify which row is the title row in the header. Replacement for bSortCellsTop */ 10640 titleRow: null, 10641 10642 10643 /** 10644 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and 10645 * `sorting\_3` to the columns which are currently being sorted on. This is 10646 * presented as a feature switch as it can increase processing time (while 10647 * classes are removed and added) so for large data sets you might want to 10648 * turn this off. 10649 */ 10650 "bSortClasses": true, 10651 10652 10653 /** 10654 * Enable or disable state saving. When enabled HTML5 `localStorage` will be 10655 * used to save table display information such as pagination information, 10656 * display length, filtering and sorting. As such when the end user reloads 10657 * the page the display will match what thy had previously set up. 10658 */ 10659 "bStateSave": false, 10660 10661 10662 /** 10663 * This function is called when a TR element is created (and all TD child 10664 * elements have been inserted), or registered if using a DOM source, allowing 10665 * manipulation of the TR element (adding classes etc). 10666 */ 10667 "fnCreatedRow": null, 10668 10669 10670 /** 10671 * This function is called on every 'draw' event, and allows you to 10672 * dynamically modify any aspect you want about the created DOM. 10673 */ 10674 "fnDrawCallback": null, 10675 10676 10677 /** 10678 * Identical to fnHeaderCallback() but for the table footer this function 10679 * allows you to modify the table footer on every 'draw' event. 10680 */ 10681 "fnFooterCallback": null, 10682 10683 10684 /** 10685 * When rendering large numbers in the information element for the table 10686 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers 10687 * to have a comma separator for the 'thousands' units (e.g. 1 million is 10688 * rendered as "1,000,000") to help readability for the end user. This 10689 * function will override the default method DataTables uses. 10690 */ 10691 "fnFormatNumber": function (toFormat) { 10692 return toFormat.toString().replace( 10693 /\B(?=(\d{3})+(?!\d))/g, 10694 this.oLanguage.sThousands 10695 ); 10696 }, 10697 10698 10699 /** 10700 * This function is called on every 'draw' event, and allows you to 10701 * dynamically modify the header row. This can be used to calculate and 10702 * display useful information about the table. 10703 */ 10704 "fnHeaderCallback": null, 10705 10706 10707 /** 10708 * The information element can be used to convey information about the current 10709 * state of the table. Although the internationalisation options presented by 10710 * DataTables are quite capable of dealing with most customisations, there may 10711 * be times where you wish to customise the string further. This callback 10712 * allows you to do exactly that. 10713 */ 10714 "fnInfoCallback": null, 10715 10716 10717 /** 10718 * Called when the table has been initialised. Normally DataTables will 10719 * initialise sequentially and there will be no need for this function, 10720 * however, this does not hold true when using external language information 10721 * since that is obtained using an async XHR call. 10722 */ 10723 "fnInitComplete": null, 10724 10725 10726 /** 10727 * Called at the very start of each table draw and can be used to cancel the 10728 * draw by returning false, any other return (including undefined) results in 10729 * the full draw occurring). 10730 */ 10731 "fnPreDrawCallback": null, 10732 10733 10734 /** 10735 * This function allows you to 'post process' each row after it have been 10736 * generated for each table draw, but before it is rendered on screen. This 10737 * function might be used for setting the row class name etc. 10738 */ 10739 "fnRowCallback": null, 10740 10741 10742 /** 10743 * Load the table state. With this function you can define from where, and how, the 10744 * state of a table is loaded. By default DataTables will load from `localStorage` 10745 * but you might wish to use a server-side database or cookies. 10746 */ 10747 "fnStateLoadCallback": function (settings) { 10748 try { 10749 return JSON.parse(
vendor: 6,206 bytes, lines 10750-10914
10750 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem( 10751 'DataTables_' + settings.sInstance + '_' + location.pathname 10752 ) 10753 ); 10754 } catch (e) { 10755 return {}; 10756 } 10757 }, 10758 10759 10760 /** 10761 * Callback which allows modification of the saved state prior to loading that state. 10762 * This callback is called when the table is loading state from the stored data, but 10763 * prior to the settings object being modified by the saved state. Note that for 10764 * plug-in authors, you should use the `stateLoadParams` event to load parameters for 10765 * a plug-in. 10766 */ 10767 "fnStateLoadParams": null, 10768 10769 10770 /** 10771 * Callback that is called when the state has been loaded from the state saving method 10772 * and the DataTables settings object has been modified as a result of the loaded state. 10773 */ 10774 "fnStateLoaded": null, 10775 10776 10777 /** 10778 * Save the table state. This function allows you to define where and how the state 10779 * information for the table is stored By default DataTables will use `localStorage` 10780 * but you might wish to use a server-side database or cookies. 10781 */ 10782 "fnStateSaveCallback": function (settings, data) { 10783 try { 10784 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem( 10785 'DataTables_' + settings.sInstance + '_' + location.pathname, 10786 JSON.stringify(data) 10787 ); 10788 } catch (e) { 10789 // noop 10790 } 10791 }, 10792 10793 10794 /** 10795 * Callback which allows modification of the state to be saved. Called when the table 10796 * has changed state a new state save is required. This method allows modification of 10797 * the state saving object prior to actually doing the save, including addition or 10798 * other state properties or modification. Note that for plug-in authors, you should 10799 * use the `stateSaveParams` event to save parameters for a plug-in. 10800 */ 10801 "fnStateSaveParams": null, 10802 10803 10804 /** 10805 * Duration for which the saved state information is considered valid. After this period 10806 * has elapsed the state will be returned to the default. 10807 * Value is given in seconds. 10808 */ 10809 "iStateDuration": 7200, 10810 10811 10812 /** 10813 * Number of rows to display on a single page when using pagination. If 10814 * feature enabled (`lengthChange`) then the end user will be able to override 10815 * this to a custom setting using a pop-up menu. 10816 */ 10817 "iDisplayLength": 10, 10818 10819 10820 /** 10821 * Define the starting point for data display when using DataTables with 10822 * pagination. Note that this parameter is the number of records, rather than 10823 * the page number, so if you have 10 records per page and want to start on 10824 * the third page, it should be "20". 10825 */ 10826 "iDisplayStart": 0, 10827 10828 10829 /** 10830 * By default DataTables allows keyboard navigation of the table (sorting, paging, 10831 * and filtering) by adding a `tabindex` attribute to the required elements. This 10832 * allows you to tab through the controls and press the enter key to activate them. 10833 * The tabindex is default 0, meaning that the tab follows the flow of the document. 10834 * You can overrule this using this parameter if you wish. Use a value of -1 to 10835 * disable built-in keyboard navigation. 10836 */ 10837 "iTabIndex": 0, 10838 10839 10840 /** 10841 * Classes that DataTables assigns to the various components and features 10842 * that it adds to the HTML table. This allows classes to be configured 10843 * during initialisation in addition to through the static 10844 * {@link DataTable.ext.oStdClasses} object). 10845 */ 10846 "oClasses": {}, 10847 10848 10849 /** 10850 * All strings that DataTables uses in the user interface that it creates 10851 * are defined in this object, allowing you to modified them individually or 10852 * completely replace them all as required. 10853 */ 10854 "oLanguage": { 10855 /** 10856 * Strings that are used for WAI-ARIA labels and controls only (these are not 10857 * actually visible on the page, but will be read by screenreaders, and thus 10858 * must be internationalised as well). 10859 */ 10860 "oAria": { 10861 /** 10862 * ARIA label that is added to the table headers when the column may be sorted 10863 */ 10864 "orderable": ": Activate to sort", 10865 10866 /** 10867 * ARIA label that is added to the table headers when the column is currently being sorted 10868 */ 10869 "orderableReverse": ": Activate to invert sorting", 10870 10871 /** 10872 * ARIA label that is added to the table headers when the column is currently being 10873 * sorted and next step is to remove sorting 10874 */ 10875 "orderableRemove": ": Activate to remove sorting", 10876 10877 paginate: { 10878 first: 'First', 10879 last: 'Last', 10880 next: 'Next', 10881 previous: 'Previous', 10882 number: '' 10883 } 10884 }, 10885 10886 /** 10887 * Pagination string used by DataTables for the built-in pagination 10888 * control types. 10889 */ 10890 "oPaginate": { 10891 /** 10892 * Label and character for first page button («) 10893 */ 10894 "sFirst": "\u00AB", 10895 10896 /** 10897 * Last page button (») 10898 */ 10899 "sLast": "\u00BB", 10900 10901 /** 10902 * Next page button (âº) 10903 */ 10904 "sNext": "\u203A", 10905 10906 /** 10907 * Previous page button (â¹) 10908 */ 10909 "sPrevious": "\u2039", 10910 }, 10911 10912 /** 10913 * Plural object for the data type the table is showing 10914 */
10915 entries: { 10916 _: "entries", 10917 1: "entry" 10918 }, 10919 10920 /** 10921 * Page length options 10922 */ 10923 lengthLabels: { 10924 '-1': 'All' 10925 }, 10926 10927 /** 10928 * This string is shown in preference to `zeroRecords` when the table is 10929 * empty of data (regardless of filtering). Note that this is an optional 10930 * parameter - if it is not given, the value of `zeroRecords` will be used 10931 * instead (either the default or given value). 10932 */ 10933 "sEmptyTable": "No data available in table", 10934 10935 10936 /** 10937 * This string gives information to the end user about the information 10938 * that is current on display on the page. The following tokens can be 10939 * used in the string and will be dynamically replaced as the table 10940 * display updates. This tokens can be placed anywhere in the string, or 10941 * removed as needed by the language requires: 10942 * 10943 * * `\_START\_` - Display index of the first record on the current page 10944 * * `\_END\_` - Display index of the last record on the current page 10945 * * `\_TOTAL\_` - Number of records in the table after filtering 10946 * * `\_MAX\_` - Number of records in the table without filtering 10947 * * `\_PAGE\_` - Current page number 10948 * * `\_PAGES\_` - Total number of pages of data in the table 10949 */ 10950 "sInfo": "Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_", 10951 10952 10953 /** 10954 * Display information string for when the table is empty. Typically the 10955 * format of this string should match `info`. 10956 */ 10957 "sInfoEmpty": "Showing 0 to 0 of 0 _ENTRIES-TOTAL_", 10958 10959 10960 /** 10961 * When a user filters the information in a table, this string is appended 10962 * to the information (`info`) to give an idea of how strong the filtering 10963 * is. The variable _MAX_ is dynamically updated. 10964 */ 10965 "sInfoFiltered": "(filtered from _MAX_ total _ENTRIES-MAX_)", 10966 10967 10968 /** 10969 * If can be useful to append extra information to the info string at times, 10970 * and this variable does exactly that. This information will be appended to 10971 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are 10972 * being used) at all times. 10973 */ 10974 "sInfoPostFix": "", 10975 10976 10977 /** 10978 * This decimal place operator is a little different from the other 10979 * language options since DataTables doesn't output floating point 10980 * numbers, so it won't ever use this for display of a number. Rather, 10981 * what this parameter does is modify the sort methods of the table so 10982 * that numbers which are in a format which has a character other than 10983 * a period (`.`) as a decimal place will be sorted numerically. 10984 * 10985 * Note that numbers with different decimal places cannot be shown in 10986 * the same table and still be sortable, the table must be consistent. 10987 * However, multiple different tables on the page can use different 10988 * decimal place characters. 10989 */ 10990 "sDecimal": "", 10991 10992 10993 /** 10994 * DataTables has a build in number formatter (`formatNumber`) which is 10995 * used to format large numbers that are used in the table information. 10996 * By default a comma is used, but this can be trivially changed to any 10997 * character you wish with this parameter. 10998 */ 10999 "sThousands": ",", 11000 11001 11002 /** 11003 * Detail the action that will be taken when the drop down menu for the 11004 * pagination length option is changed. The '_MENU_' variable is replaced 11005 * with a default select list of 10, 25, 50 and 100, and can be replaced 11006 * with a custom select box if required. 11007 */ 11008 "sLengthMenu": "_MENU_ _ENTRIES_ per page", 11009 11010 11011 /** 11012 * When using Ajax sourced data and during the first draw when DataTables is 11013 * gathering the data, this message is shown in an empty row in the table to 11014 * indicate to the end user the data is being loaded. Note that this 11015 * parameter is not used when loading data by server-side processing, just 11016 * Ajax sourced data with client-side processing. 11017 */ 11018 "sLoadingRecords": "Loading...", 11019 11020 11021 /** 11022 * Text which is displayed when the table is processing a user action 11023 * (usually a sort command or similar). 11024 */ 11025 "sProcessing": "", 11026 11027 11028 /** 11029 * Details the actions that will be taken when the user types into the 11030 * filtering input text box. The variable "_INPUT_", if used in the string, 11031 * is replaced with the HTML text box for the filtering input allowing 11032 * control over where it appears in the string. If "_INPUT_" is not given 11033 * then the input box is appended to the string automatically. 11034 */ 11035 "sSearch": "Search:", 11036 11037 11038 /** 11039 * Assign a `placeholder` attribute to the search `input` element 11040 * @type string 11041 * @default 11042 * 11043 * @dtopt Language 11044 * @name DataTable.defaults.language.searchPlaceholder 11045 */ 11046 "sSearchPlaceholder": "", 11047 11048 11049 /** 11050 * All of the language information can be stored in a file on the 11051 * server-side, which DataTables will look up if this parameter is passed. 11052 * It must store the URL of the language file, which is in a JSON format, 11053 * and the object has the same properties as the oLanguage object in the 11054 * initialiser object (i.e. the above parameters). Please refer to one of 11055 * the example language files to see how this works in action. 11056 */ 11057 "sUrl": "", 11058 11059 11060 /** 11061 * Text shown inside the table records when the is no information to be 11062 * displayed after filtering. `emptyTable` is shown when there is simply no 11063 * information in the table at all (regardless of filtering). 11064 */
11065 "sZeroRecords": "No matching records found" 11066 }, 11067 11068 11069 /** The initial data order is reversed when `desc` ordering */ 11070 orderDescReverse: true, 11071 11072 11073 /** 11074 * This parameter allows you to have define the global filtering state at 11075 * initialisation time. As an object the `search` parameter must be 11076 * defined, but all other parameters are optional. When `regex` is true, 11077 * the search string will be treated as a regular expression, when false 11078 * (default) it will be treated as a straight string. When `smart` 11079 * DataTables will use it's smart filtering methods (to word match at 11080 * any point in the data), when false this will not be done. 11081 */ 11082 "oSearch": $.extend({}, DataTable.models.oSearch), 11083 11084 11085 /** 11086 * Table and control layout. This replaces the legacy `dom` option. 11087 */ 11088 layout: { 11089 topStart: 'pageLength', 11090 topEnd: 'search', 11091 bottomStart: 'info', 11092 bottomEnd: 'paging' 11093 }, 11094 11095 11096 /** 11097 * Legacy DOM layout option 11098 */ 11099 "sDom": null, 11100 11101 11102 /** 11103 * Search delay option. This will throttle full table searches that use the 11104 * DataTables provided search input element (it does not effect calls to 11105 * `dt-api search()`, providing a delay before the search is made. 11106 */ 11107 "searchDelay": null, 11108 11109 11110 /** 11111 * DataTables features six different built-in options for the buttons to 11112 * display for pagination control: 11113 * 11114 * * `numbers` - Page number buttons only 11115 * * `simple` - 'Previous' and 'Next' buttons only 11116 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers 11117 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons 11118 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers 11119 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers 11120 */ 11121 "sPaginationType": "", 11122 11123 11124 /** 11125 * Enable horizontal scrolling. When a table is too wide to fit into a 11126 * certain layout, or you have a large number of columns in the table, you 11127 * can enable x-scrolling to show the table in a viewport, which can be 11128 * scrolled. This property can be `true` which will allow the table to 11129 * scroll horizontally when needed, or any CSS unit, or a number (in which 11130 * case it will be treated as a pixel measurement). Setting as simply `true` 11131 * is recommended. 11132 */ 11133 "sScrollX": "", 11134 11135 11136 /** 11137 * This property can be used to force a DataTable to use more width than it 11138 * might otherwise do when x-scrolling is enabled. For example if you have a 11139 * table which requires to be well spaced, this parameter is useful for 11140 * "over-sizing" the table, and thus forcing scrolling. This property can by 11141 * any CSS unit, or a number (in which case it will be treated as a pixel 11142 * measurement). 11143 */ 11144 "sScrollXInner": "", 11145 11146 11147 /** 11148 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable 11149 * to the given height, and enable scrolling for any data which overflows the 11150 * current viewport. This can be used as an alternative to paging to display 11151 * a lot of data in a small area (although paging and scrolling can both be 11152 * enabled at the same time). This property can be any CSS unit, or a number 11153 * (in which case it will be treated as a pixel measurement). 11154 */ 11155 "sScrollY": "", 11156 11157 11158 /** 11159 * __Deprecated__ The functionality provided by this parameter has now been 11160 * superseded by that provided through `ajax`, which should be used instead. 11161 * 11162 * Set the HTTP method that is used to make the Ajax call for server-side 11163 * processing or Ajax sourced data. 11164 */ 11165 "sServerMethod": "GET", 11166 11167 11168 /** 11169 * DataTables makes use of renderers when displaying HTML elements for 11170 * a table. These renderers can be added or modified by plug-ins to 11171 * generate suitable mark-up for a site. For example the Bootstrap 11172 * integration plug-in for DataTables uses a paging button renderer to 11173 * display pagination buttons in the mark-up required by Bootstrap. 11174 * 11175 * For further information about the renderers available see 11176 * DataTable.ext.renderer 11177 */ 11178 "renderer": null, 11179 11180 11181 /** 11182 * Set the data property name that DataTables should use to get a row's id 11183 * to set as the `id` property in the node. 11184 */ 11185 "rowId": "DT_RowId", 11186 11187 11188 /** 11189 * Caption value 11190 */ 11191 "caption": null, 11192 11193 11194 /** 11195 * For server-side processing - use the data from the DOM for the first draw 11196 */ 11197 iDeferLoading: null, 11198 11199 /** Event listeners */ 11200 on: null 11201 }; 11202 11203 _fnHungarianMap(DataTable.defaults); 11204 11205 11206 11207 /* 11208 * Developer note - See note in model.defaults.js about the use of Hungarian 11209 * notation and camel case. 11210 */ 11211 11212 /** 11213 * Column options that can be given to DataTables at initialisation time. 11214 * @namespace 11215 */ 11216 DataTable.defaults.column = { 11217 /** 11218 * Define which column(s) an order will occur on for this column. This 11219 * allows a column's ordering to take multiple columns into account when
11220 * doing a sort or use the data from a different column. For example first 11221 * name / last name columns make sense to do a multi-column sort over the 11222 * two columns. 11223 */ 11224 "aDataSort": null, 11225 "iDataSort": -1, 11226 11227 ariaTitle: '', 11228 11229 11230 /** 11231 * You can control the default ordering direction, and even alter the 11232 * behaviour of the sort handler (i.e. only allow ascending ordering etc) 11233 * using this parameter. 11234 */ 11235 "asSorting": ['asc', 'desc', ''], 11236 11237 11238 /** 11239 * Enable or disable filtering on the data in this column. 11240 */ 11241 "bSearchable": true, 11242 11243 11244 /** 11245 * Enable or disable ordering on this column. 11246 */ 11247 "bSortable": true, 11248 11249 11250 /** 11251 * Enable or disable the display of this column. 11252 */ 11253 "bVisible": true, 11254 11255 11256 /** 11257 * Developer definable function that is called whenever a cell is created (Ajax source, 11258 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 11259 * allowing you to modify the DOM element (add background colour for example) when the 11260 * element is available. 11261 */ 11262 "fnCreatedCell": null, 11263 11264 11265 /** 11266 * This property can be used to read data from any data source property, 11267 * including deeply nested objects / properties. `data` can be given in a 11268 * number of different ways which effect its behaviour: 11269 * 11270 * * `integer` - treated as an array index for the data source. This is the 11271 * default that DataTables uses (incrementally increased for each column). 11272 * * `string` - read an object property from the data source. There are 11273 * three 'special' options that can be used in the string to alter how 11274 * DataTables reads the data from the source object: 11275 * * `.` - Dotted JavaScript notation. Just as you use a `.` in 11276 * JavaScript to read from nested objects, so to can the options 11277 * specified in `data`. For example: `browser.version` or 11278 * `browser.name`. If your object parameter name contains a period, use 11279 * `\\` to escape it - i.e. `first\\.name`. 11280 * * `[]` - Array notation. DataTables can automatically combine data 11281 * from and array source, joining the data with the characters provided 11282 * between the two brackets. For example: `name[, ]` would provide a 11283 * comma-space separated list from the source array. If no characters 11284 * are provided between the brackets, the original array source is 11285 * returned. 11286 * * `()` - Function notation. Adding `()` to the end of a parameter will 11287 * execute a function of the name given. For example: `browser()` for a 11288 * simple function on the data source, `browser.version()` for a 11289 * function in a nested property or even `browser().version` to get an 11290 * object property if the function called returns an object. Note that 11291 * function notation is recommended for use in `render` rather than 11292 * `data` as it is much simpler to use as a renderer. 11293 * * `null` - use the original data source for the row rather than plucking 11294 * data directly from it. This action has effects on two other 11295 * initialisation options: 11296 * * `defaultContent` - When null is given as the `data` option and 11297 * `defaultContent` is specified for the column, the value defined by 11298 * `defaultContent` will be used for the cell. 11299 * * `render` - When null is used for the `data` option and the `render` 11300 * option is specified for the column, the whole data source for the 11301 * row is used for the renderer. 11302 * * `function` - the function given will be executed whenever DataTables 11303 * needs to set or get the data for a cell in the column. The function 11304 * takes three parameters: 11305 * * Parameters: 11306 * * `{array|object}` The data source for the row 11307 * * `{string}` The type call data requested - this will be 'set' when 11308 * setting data or 'filter', 'display', 'type', 'sort' or undefined 11309 * when gathering data. Note that when `undefined` is given for the 11310 * type DataTables expects to get the raw data for the object back< 11311 * * `{*}` Data to set when the second parameter is 'set'. 11312 * * Return: 11313 * * The return value from the function is not required when '
11313set' is 11314 * the type of call, but otherwise the return is what will be used 11315 * for the data requested. 11316 * 11317 * Note that `data` is a getter and setter option. If you just require 11318 * formatting of data for output, you will likely want to use `render` which 11319 * is simply a getter and thus simpler to use. 11320 * 11321 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The 11322 * name change reflects the flexibility of this property and is consistent 11323 * with the naming of mRender. If 'mDataProp' is given, then it will still 11324 * be used by DataTables, as it automatically maps the old name to the new 11325 * if required. 11326 */ 11327 "mData": null, 11328 11329 11330 /** 11331 * This property is the rendering partner to `data` and it is suggested that 11332 * when you want to manipulate data for display (including filtering, 11333 * sorting etc) without altering the underlying data for the table, use this 11334 * property. `render` can be considered to be the read only companion to 11335 * `data` which is read / write (then as such more complex). Like `data` 11336 * this option can be given in a number of different ways to effect its 11337 * behaviour: 11338 * 11339 * * `integer` - treated as an array index for the data source. This is the 11340 * default that DataTables uses (incrementally increased for each column). 11341 * * `string` - read an object property from the data source. There are 11342 * three 'special' options that can be used in the string to alter how 11343 * DataTables reads the data from the source object: 11344 * * `.` - Dotted JavaScript notation. Just as you use a `.` in 11345 * JavaScript to read from nested objects, so to can the options 11346 * specified in `data`. For example: `browser.version` or 11347 * `browser.name`. If your object parameter name contains a period, use 11348 * `\\` to escape it - i.e. `first\\.name`. 11349 * * `[]` - Array notation. DataTables can automatically combine data 11350 * from and array source, joining the data with the characters provided 11351 * between the two brackets. For example: `name[, ]` would provide a 11352 * comma-space separated list from the source array. If no characters 11353 * are provided between the brackets, the original array source is 11354 * returned. 11355 * * `()` - Function notation. Adding `()` to the end of a parameter will 11356 * execute a function of the name given. For example: `browser()` for a 11357 * simple function on the data source, `browser.version()` for a 11358 * function in a nested property or even `browser().version` to get an 11359 * object property if the function called returns an object. 11360 * * `object` - use different data for the different data types requested by 11361 * DataTables ('filter', 'display', 'type' or 'sort'). The property names 11362 * of the object is the data type the property refers to and the value can 11363 * defined using an integer, string or function using the same rules as 11364 * `render` normally does. Note that an `_` option _must_ be specified. 11365 * This is the default value to use if you haven't specified a value for 11366 * the data type requested by DataTables. 11367 * * `function` - the function given will be executed whenever DataTables 11368 * needs to set or get the data for a cell in the column. The function 11369 * takes three parameters: 11370 * * Parameters: 11371 * * {array|object} The data source for the row (based on `data`) 11372 * * {string} The type call data requested - this will be 'filter', 11373 * 'display', 'type' or 'sort'. 11374 * * {array|object} The full data source for the row (not based on 11375 * `data`) 11376 * * Return: 11377 * * The return value from the function is what will be used for the 11378 * data requested. 11379 */ 11380 "mRender": null, 11381 11382 11383 /** 11384 * Change the cell type created for the column - either TD cells or TH cells. This 11385 * can be useful as TH cells have semantic meaning in the table body, allowing them 11386 * to act as a header for a row (you may wish to add scope='row' to the TH elements). 11387 */ 11388 "sCellType": "td", 11389 11390 11391 /** 11392 * Class to give to each cell in this column. 11393 */ 11394 "sClass": "", 11395 11396 /** 11397 * When DataTables calculates the column widths to assign to each column, 11398 * it finds the longest string in each column and then constructs a 11399 * temporary table and reads the widths from that. The problem with this 11400 * is that "mmm" is much wider then "iiii", but the latter is a longer 11401 * string - thus the calculation can go wrong (doing it properly and putting 11402 * it into an DOM object and measuring that is horribly(!) slow). Thus as 11403 * a "work around" we provide this option. It will append its value to the 11404 * text that is found to be the longest string for the column - i.e. padding. 11405 * Generally you shouldn't need this! 11406 */ 11407 "sContentPadding": "", 11408 11409 11410 /** 11411 * Allows a default value to be given for a column's data, and will be used 11412 * whenever a null data source is encountered (this can be because `data` 11413 * is set to null, or because the data source itself is null). 11414 */ 11415 "sDefaultContent": null, 11416 11417 11418 /** 11419 * This parameter is only used in DataTables' server-side processing. It can 11420 * be exceptionally useful to know what columns are being displayed on the 11421 * client side, and to map these to database fields. When defined, the names 11422 * also allow DataTables to reorder information from the server if it comes 11423 * back in an unexpected order (i.e. if you switch your columns around on the 11424 * client-side, your server-side code does not also need updating). 11425 */ 11426 "sName": "", 11427 11428 11429 /** 11430 * Defines a data source type for the ordering which can be used to read 11431 * real-time information from the table (updating the internally cached 11432 * version) prior to ordering. This allows ordering to occur on user 11433 * editable elements such as form inputs. 11434 */ 11435 "sSortDataType": "std", 11436 11437 11438 /** 11439 * The title of this column. 11440 */ 11441 "sTitle": null, 11442 11443 11444 /** 11445 * The type allows you to specify how the data for this column will be
11446 * ordered. Four types (string, numeric, date and html (which will strip 11447 * HTML tags before ordering)) are currently available. Note that only date 11448 * formats understood by JavaScript's Date() object will be accepted as type 11449 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 11450 * 'numeric', 'date' or 'html' (by default). Further types can be adding 11451 * through plug-ins. 11452 */ 11453 "sType": null, 11454 11455 11456 /** 11457 * Defining the width of the column, this parameter may take any CSS value 11458 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not 11459 * been given a specific width through this interface ensuring that the table 11460 * remains readable. 11461 */ 11462 "sWidth": null 11463 }; 11464 11465 _fnHungarianMap(DataTable.defaults.column); 11466 11467 11468 11469 /** 11470 * DataTables settings object - this holds all the information needed for a 11471 * given table, including configuration, data and current application of the 11472 * table options. DataTables does not have a single instance for each DataTable 11473 * with the settings attached to that instance, but rather instances of the 11474 * DataTable "class" are created on-the-fly as needed (typically by a 11475 * $().dataTable() call) and the settings object is then applied to that 11476 * instance. 11477 * 11478 * Note that this object is related to {@link DataTable.defaults} but this 11479 * one is the internal data store for DataTables's cache of columns. It should 11480 * NOT be manipulated outside of DataTables. Any configuration should be done 11481 * through the initialisation options. 11482 */ 11483 DataTable.models.oSettings = { 11484 /** 11485 * Primary features of DataTables and their enablement state. 11486 */ 11487 "oFeatures": { 11488 11489 /** 11490 * Flag to say if DataTables should automatically try to calculate the 11491 * optimum table and columns widths (true) or not (false). 11492 * Note that this parameter will be set by the initialisation routine. To 11493 * set a default use {@link DataTable.defaults}. 11494 */ 11495 "bAutoWidth": null, 11496 11497 /** 11498 * Delay the creation of TR and TD elements until they are actually 11499 * needed by a driven page draw. This can give a significant speed 11500 * increase for Ajax source and JavaScript source data, but makes no 11501 * difference at all for DOM and server-side processing tables. 11502 * Note that this parameter will be set by the initialisation routine. To 11503 * set a default use {@link DataTable.defaults}. 11504 */ 11505 "bDeferRender": null, 11506 11507 /** 11508 * Enable filtering on the table or not. Note that if this is disabled 11509 * then there is no filtering at all on the table, including fnFilter. 11510 * To just remove the filtering input use sDom and remove the 'f' option. 11511 * Note that this parameter will be set by the initialisation routine. To 11512 * set a default use {@link DataTable.defaults}. 11513 */ 11514 "bFilter": null, 11515 11516 /** 11517 * Used only for compatibility with DT1 11518 * @deprecated 11519 */ 11520 "bInfo": true, 11521 11522 /** 11523 * Used only for compatibility with DT1 11524 * @deprecated 11525 */ 11526 "bLengthChange": true, 11527 11528 /** 11529 * Pagination enabled or not. Note that if this is disabled then length 11530 * changing must also be disabled. 11531 * Note that this parameter will be set by the initialisation routine. To 11532 * set a default use {@link DataTable.defaults}. 11533 */ 11534 "bPaginate": null, 11535 11536 /** 11537 * Processing indicator enable flag whenever DataTables is enacting a 11538 * user request - typically an Ajax request for server-side processing. 11539 * Note that this parameter will be set by the initialisation routine. To 11540 * set a default use {@link DataTable.defaults}. 11541 */ 11542 "bProcessing": null, 11543 11544 /** 11545 * Server-side processing enabled flag - when enabled DataTables will 11546 * get all data from the server for every draw - there is no filtering, 11547 * sorting or paging done on the client-side. 11548 * Note that this parameter will be set by the initialisation routine. To 11549 * set a default use {@link DataTable.defaults}. 11550 */ 11551 "bServerSide": null, 11552 11553 /** 11554 * Sorting enablement flag. 11555 * Note that this parameter will be set by the initialisation routine. To 11556 * set a default use {@link DataTable.defaults}. 11557 */ 11558 "bSort": null, 11559 11560 /**
11561 * Multi-column sorting 11562 * Note that this parameter will be set by the initialisation routine. To 11563 * set a default use {@link DataTable.defaults}. 11564 */ 11565 "bSortMulti": null, 11566 11567 /** 11568 * Apply a class to the columns which are being sorted to provide a 11569 * visual highlight or not. This can slow things down when enabled since 11570 * there is a lot of DOM interaction. 11571 * Note that this parameter will be set by the initialisation routine. To 11572 * set a default use {@link DataTable.defaults}. 11573 */ 11574 "bSortClasses": null, 11575 11576 /** 11577 * State saving enablement flag. 11578 * Note that this parameter will be set by the initialisation routine. To 11579 * set a default use {@link DataTable.defaults}. 11580 */ 11581 "bStateSave": null 11582 }, 11583 11584 11585 /** 11586 * Scrolling settings for a table. 11587 */ 11588 "oScroll": { 11589 /** 11590 * When the table is shorter in height than sScrollY, collapse the 11591 * table container down to the height of the table (when true). 11592 * Note that this parameter will be set by the initialisation routine. To 11593 * set a default use {@link DataTable.defaults}. 11594 */ 11595 "bCollapse": null, 11596 11597 /** 11598 * Width of the scrollbar for the web-browser's platform. Calculated 11599 * during table initialisation. 11600 */ 11601 "iBarWidth": 0, 11602 11603 /** 11604 * Viewport width for horizontal scrolling. Horizontal scrolling is 11605 * disabled if an empty string. 11606 * Note that this parameter will be set by the initialisation routine. To 11607 * set a default use {@link DataTable.defaults}. 11608 */ 11609 "sX": null, 11610 11611 /** 11612 * Width to expand the table to when using x-scrolling. Typically you 11613 * should not need to use this. 11614 * Note that this parameter will be set by the initialisation routine. To 11615 * set a default use {@link DataTable.defaults}. 11616 * @deprecated 11617 */ 11618 "sXInner": null, 11619 11620 /** 11621 * Viewport height for vertical scrolling. Vertical scrolling is disabled 11622 * if an empty string. 11623 * Note that this parameter will be set by the initialisation routine. To 11624 * set a default use {@link DataTable.defaults}. 11625 */ 11626 "sY": null 11627 }, 11628 11629 /** 11630 * Language information for the table. 11631 */ 11632 "oLanguage": { 11633 /** 11634 * Information callback function. See 11635 * {@link DataTable.defaults.fnInfoCallback} 11636 */ 11637 "fnInfoCallback": null 11638 }, 11639 11640 /** 11641 * Browser support parameters 11642 */ 11643 "oBrowser": { 11644 /** 11645 * Determine if the vertical scrollbar is on the right or left of the 11646 * scrolling container - needed for rtl language layout, although not 11647 * all browsers move the scrollbar (Safari). 11648 */ 11649 "bScrollbarLeft": false, 11650 11651 /** 11652 * Browser scrollbar width 11653 */ 11654 "barWidth": 0 11655 }, 11656 11657 11658 "ajax": null, 11659 11660 11661 /** 11662 * Array referencing the nodes which are used for the features. The 11663 * parameters of this object match what is allowed by sDom - i.e. 11664 * <ul> 11665 * <li>'l' - Length changing</li> 11666 * <li>'f' - Filtering input</li> 11667 * <li>'t' - The table!</li> 11668 * <li>'i' - Information</li> 11669 * <li>'p' - Pagination</li> 11670 * <li>'r' - pRocessing</li> 11671 * </ul> 11672 */ 11673 "aanFeatures": [], 11674 11675 /** 11676 * Store data information - see {@link DataTable.models.oRow} for detailed 11677 * information. 11678 */ 11679 "aoData": [], 11680 11681 /** 11682 * Array of indexes which are in the current display (after filtering etc) 11683 */ 11684 "aiDisplay": [], 11685 11686 /** 11687 * Array of indexes for display - no filtering 11688 */ 11689 "aiDisplayMaster": [], 11690 11691 /** 11692 * Map of row ids to data indexes 11693 */ 11694 "aIds": {}, 11695 11696 /** 11697 * Store information about each column that is in use 11698 */ 11699 "aoColumns": [], 11700 11701 /** 11702 * Store information about the table's header 11703 */ 11704 "aoHeader": [], 11705 11706 /** 11707 * Store information about the table's footer 11708 */ 11709 "aoFooter": [], 11710 11711 /** 11712 * Store the applied global search information in case we want to force a 11713 * research or compare the old search to a new one. 11714 * Note that this parameter will be set by the initialisation routine. To 11715 * set a default use {@link DataTable.defaults}. 11716 */ 11717 "oPreviousSearch": {}, 11718 11719 /** 11720 * Store for named searches 11721 */ 11722 searchFixed: {}, 11723 11724 /** 11725 * Store the applied search for each column - see 11726 * {@link DataTable.models.oSearch} for the format that is used for the 11727 * filtering information for each column. 11728 */ 11729 "aoPreSearchCols": [], 11730 11731 /** 11732 * Sorting that is applied to the table. Note that the inner arrays are 11733 * used in the following manner: 11734 * <ul> 11735 * <li>Index 0 - column number</li> 11736 * <li>Index 1 - current sorting direction</li> 11737 * </ul> 11738 * Note that this parameter will be set by the initialisation routine. To 11739 * set a default use {@link DataTable.defaults}. 11740 */ 11741 "aaSorting": null, 11742 11743 /** 11744 * Sorting that is always applied to the table (i.e. prefixed in front of 11745 * aaSorting). 11746 * Note that this parameter will be set by the initialisation routine. To 11747 * set a default use {@link DataTable.defaults}. 11748 */ 11749 "aaSortingFixed": [], 11750 11751 /** 11752 * If restoring a table - we should restore its width 11753 */ 11754 "sDestroyWidth": 0, 11755 11756 /** 11757 * Callback functions array for every time a row is inserted (i.e. on a draw). 11758 */ 11759 "aoRowCallback": [], 11760 11761 /** 11762 * Callback functions for the header on each draw. 11763 */ 11764 "aoHeaderCallback": [], 11765 11766 /** 11767 * Callback function for the footer on each draw. 11768 */ 11769 "aoFooterCallback": [], 11770 11771 /** 11772 * Array of callback functions for draw callback functions 11773 */ 11774 "aoDrawCallback": [], 11775 11776 /** 11777 * Array of callback functions for row created function 11778 */ 11779 "aoRowCreatedCallback": [], 11780 11781 /** 11782 * Callback functions for just before the table is redrawn. A return of 11783 * false will be used to cancel the draw. 11784 */ 11785 "aoPreDrawCallback": [], 11786 11787 /** 11788 * Callback functions for when the table has been initialised. 11789 */ 11790 "aoInitComplete": [], 11791 11792 11793 /** 11794 * Callbacks for modifying the settings to be stored for state saving, prior to 11795 * saving state. 11796 */ 11797 "aoStateSaveParams": [], 11798 11799 /** 11800 * Callbacks for modifying the settings that have been stored for state saving 11801 * prior to using the stored values to restore the state. 11802 */ 11803 "aoStateLoadParams": [], 11804 11805 /** 11806 * Callbacks for operating on the settings object once the saved state has been 11807 * loaded 11808 */ 11809 "aoStateLoaded": [], 11810 11811 /** 11812 * Cache the table ID for quick access 11813 */ 11814 "sTableId": "", 11815 11816 /** 11817 * The TABLE node for the main table 11818 */ 11819 "nTable": null, 11820 11821 /** 11822 * Permanent ref to the thead element 11823 */ 11824 "nTHead": null, 11825 11826 /** 11827 * Permanent ref to the tfoot element - if it exists 11828 */ 11829 "nTFoot": null, 11830 11831 /** 11832 * Permanent ref to the tbody element 11833 */ 11834 "nTBody": null, 11835 11836 /** 11837 * Cache the wrapper node (contains all DataTables controlled elements) 11838 */ 11839 "nTableWrapper": null, 11840 11841 /** 11842 * Indicate if all required information has been read in 11843 */ 11844 "bInitialised": false, 11845 11846 /** 11847 * Information about open rows. Each object in the array has the parameters 11848 * 'nTr' and 'nParent' 11849 */ 11850 "aoOpenRows": [], 11851 11852 /**
11853 * Dictate the positioning of DataTables' control elements - see 11854 * {@link DataTable.model.oInit.sDom}. 11855 * Note that this parameter will be set by the initialisation routine. To 11856 * set a default use {@link DataTable.defaults}. 11857 */ 11858 "sDom": null, 11859 11860 /** 11861 * Search delay (in mS) 11862 */ 11863 "searchDelay": null, 11864 11865 /** 11866 * Which type of pagination should be used. 11867 * Note that this parameter will be set by the initialisation routine. To 11868 * set a default use {@link DataTable.defaults}. 11869 */ 11870 "sPaginationType": "two_button", 11871 11872 /** 11873 * Number of paging controls on the page. Only used for backwards compatibility 11874 */ 11875 pagingControls: 0, 11876 11877 /** 11878 * The state duration (for `stateSave`) in seconds. 11879 * Note that this parameter will be set by the initialisation routine. To 11880 * set a default use {@link DataTable.defaults}. 11881 */ 11882 "iStateDuration": 0, 11883 11884 /** 11885 * Array of callback functions for state saving. Each array element is an 11886 * object with the following parameters: 11887 * <ul> 11888 * <li>function:fn - function to call. Takes two parameters, oSettings 11889 * and the JSON string to save that has been thus far created. Returns 11890 * a JSON string to be inserted into a json object 11891 * (i.e. '"param": [ 0, 1, 2]')</li> 11892 * <li>string:sName - name of callback</li> 11893 * </ul> 11894 */ 11895 "aoStateSave": [], 11896 11897 /** 11898 * Array of callback functions for state loading. Each array element is an 11899 * object with the following parameters: 11900 * <ul> 11901 * <li>function:fn - function to call. Takes two parameters, oSettings 11902 * and the object stored. May return false to cancel state loading</li> 11903 * <li>string:sName - name of callback</li> 11904 * </ul> 11905 */ 11906 "aoStateLoad": [], 11907 11908 /** 11909 * State that was saved. Useful for back reference 11910 */ 11911 "oSavedState": null, 11912 11913 /** 11914 * State that was loaded. Useful for back reference 11915 */ 11916 "oLoadedState": null, 11917 11918 /** 11919 * Note if draw should be blocked while getting data 11920 */ 11921 "bAjaxDataGet": true, 11922 11923 /** 11924 * The last jQuery XHR object that was used for server-side data gathering. 11925 * This can be used for working with the XHR information in one of the 11926 * callbacks 11927 */ 11928 "jqXHR": null, 11929 11930 /** 11931 * JSON returned from the server in the last Ajax request 11932 */ 11933 "json": undefined, 11934 11935 /** 11936 * Data submitted as part of the last Ajax request 11937 */ 11938 "oAjaxData": undefined, 11939 11940 /** 11941 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if 11942 * required). 11943 * Note that this parameter will be set by the initialisation routine. To 11944 * set a default use {@link DataTable.defaults}. 11945 */ 11946 "sServerMethod": null, 11947 11948 /** 11949 * Format numbers for display. 11950 * Note that this parameter will be set by the initialisation routine. To 11951 * set a default use {@link DataTable.defaults}. 11952 */ 11953 "fnFormatNumber": null, 11954 11955 /** 11956 * List of options that can be used for the user selectable length menu. 11957 * Note that this parameter will be set by the initialisation routine. To 11958 * set a default use {@link DataTable.defaults}. 11959 */ 11960 "aLengthMenu": null, 11961 11962 /** 11963 * Counter for the draws that the table does. Also used as a tracker for 11964 * server-side processing 11965 */ 11966 "iDraw": 0, 11967 11968 /** 11969 * Indicate if a redraw is being done - useful for Ajax 11970 */ 11971 "bDrawing": false, 11972 11973 /** 11974 * Draw index (iDraw) of the last error when parsing the returned data 11975 */ 11976 "iDrawError": -1, 11977 11978 /** 11979 * Paging display length 11980 */ 11981 "_iDisplayLength": 10, 11982 11983 /** 11984 * Paging start point - aiDisplay index 11985 */ 11986 "_iDisplayStart": 0, 11987 11988 /** 11989 * Server-side processing - number of records in the result set 11990 * (i.e. before filtering), Use fnRecordsTotal rather than 11991 * this property to get the value of the number of records, regardless of 11992 * the server-side processing setting. 11993 */ 11994 "_iRecordsTotal": 0, 11995 11996 /** 11997 * Server-side processing - number of records in the current display set 11998 * (i.e. after filtering). Use fnRecordsDisplay rather than 11999 * this property to get the value of the number of records, regardless of 12000 * the server-side processing setting. 12001 */ 12002 "_iRecordsDisplay": 0, 12003 12004 /** 12005 * The classes to use for the table 12006 */
12007 "oClasses": {}, 12008 12009 /** 12010 * Flag attached to the settings object so you can check in the draw 12011 * callback if filtering has been done in the draw. Deprecated in favour of 12012 * events. 12013 * @deprecated 12014 */ 12015 "bFiltered": false, 12016 12017 /** 12018 * Flag attached to the settings object so you can check in the draw 12019 * callback if sorting has been done in the draw. Deprecated in favour of 12020 * events. 12021 * @deprecated 12022 */ 12023 "bSorted": false, 12024 12025 /** 12026 * Indicate that if multiple rows are in the header and there is more than 12027 * one unique cell per column. Replaced by titleRow 12028 */ 12029 "bSortCellsTop": null, 12030 12031 /** 12032 * Initialisation object that is used for the table 12033 */ 12034 "oInit": null, 12035 12036 /** 12037 * Destroy callback functions - for plug-ins to attach themselves to the 12038 * destroy so they can clean up markup and events. 12039 */ 12040 "aoDestroyCallback": [], 12041 12042 12043 /** 12044 * Get the number of records in the current record set, before filtering 12045 */ 12046 "fnRecordsTotal": function () { 12047 return _fnDataSource(this) == 'ssp' ? 12048 this._iRecordsTotal * 1 : 12049 this.aiDisplayMaster.length; 12050 }, 12051 12052 /** 12053 * Get the number of records in the current record set, after filtering 12054 */ 12055 "fnRecordsDisplay": function () { 12056 return _fnDataSource(this) == 'ssp' ? 12057 this._iRecordsDisplay * 1 : 12058 this.aiDisplay.length; 12059 }, 12060 12061 /** 12062 * Get the display end point - aiDisplay index 12063 */ 12064 "fnDisplayEnd": function () { 12065 var 12066 len = this._iDisplayLength, 12067 start = this._iDisplayStart, 12068 calc = start + len, 12069 records = this.aiDisplay.length, 12070 features = this.oFeatures, 12071 paginate = features.bPaginate; 12072 12073 if (features.bServerSide) { 12074 return paginate === false || len === -1 ? 12075 start + records : 12076 Math.min(start + len, this._iRecordsDisplay); 12077 } 12078 else { 12079 return !paginate || calc > records || len === -1 ? 12080 records : 12081 calc; 12082 } 12083 }, 12084 12085 /** 12086 * The DataTables object for this table 12087 */ 12088 "oInstance": null, 12089 12090 /** 12091 * Unique identifier for each instance of the DataTables object. If there 12092 * is an ID on the table node, then it takes that value, otherwise an 12093 * incrementing internal counter is used. 12094 */ 12095 "sInstance": null, 12096 12097 /** 12098 * tabindex attribute value that is added to DataTables control elements, allowing 12099 * keyboard navigation of the table and its controls. 12100 */ 12101 "iTabIndex": 0, 12102 12103 /** 12104 * DIV container for the footer scrolling table if scrolling 12105 */ 12106 "nScrollHead": null, 12107 12108 /** 12109 * DIV container for the footer scrolling table if scrolling 12110 */ 12111 "nScrollFoot": null, 12112 12113 /** 12114 * Last applied sort 12115 */ 12116 "aLastSort": [], 12117 12118 /** 12119 * Stored plug-in instances 12120 */ 12121 "oPlugins": {}, 12122 12123 /** 12124 * Function used to get a row's id from the row's data 12125 */ 12126 "rowIdFn": null, 12127 12128 /** 12129 * Data location where to store a row's id 12130 */ 12131 "rowId": null, 12132 12133 caption: '', 12134 12135 captionNode: null, 12136 12137 colgroup: null, 12138 12139 /** Delay loading of data */ 12140 deferLoading: null, 12141 12142 /** Allow auto type detection */ 12143 typeDetect: true, 12144 12145 /** ResizeObserver for the container div */ 12146 resizeObserver: null, 12147 12148 /** Keep a record of the last size of the container, so we can skip duplicates */ 12149 containerWidth: -1, 12150 12151 /** Reverse the initial order of the data set on desc ordering */ 12152 orderDescReverse: null, 12153 12154 /** Show / hide ordering indicators in headers */ 12155 orderIndicators: true, 12156 12157 /** Default ordering listener */ 12158 orderHandler: true, 12159 12160 /** Title row indicator */ 12161 titleRow: null 12162 }; 12163 12164 /** 12165 * Extension object for DataTables that is used to provide all extension 12166 * options. 12167 * 12168 * Note that the `DataTable.ext` object is available through 12169 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is 12170 * also aliased to `jQuery.fn.dataTableExt` for historic reasons. 12171 * @namespace 12172 * @extends DataTable.models.ext 12173 */ 12174 12175 12176 var extPagination = DataTable.ext.pager; 12177 12178 // Paging buttons configuration 12179 $.extend(extPagination, { 12180 simple: function () { 12181 return ['previous', 'next']; 12182 }, 12183 12184 full: function () { 12185 return ['first', 'previous', 'next', 'last']; 12186 }, 12187 12188 numbers: function () { 12189 return ['numbers']; 12190 }, 12191 12192 simple_numbers: function () { 12193 return ['previous', 'numbers', 'next']; 12194 }, 12195 12196 full_numbers: function () { 12197 return ['first', 'previous', 'numbers', 'next', 'last']; 12198 }, 12199 12200 first_last: function () { 12201 return ['first', 'last']; 12202 }, 12203 12204 first_last_numbers: function () { 12205 return ['first', 'numbers', 'last']; 12206 }, 12207 12208 // For testing and plug-ins to use 12209 _numbers: _pagingNumbers, 12210 12211 // Number of number buttons - legacy, use `numbers` option for paging feature 12212 numbers_length: 7 12213 }); 12214 12215 12216 $.extend(true, DataTable.ext.renderer, { 12217 pagingButton: { 12218 _: function (settings, buttonType, content, active, disabled) { 12219 var classes = settings.oClasses.paging; 12220 var btnClasses = [classes.button]; 12221 var btn; 12222 12223 if (active) { 12224 btnClasses.push(classes.active); 12225 } 12226 12227 if (disabled) { 12228 btnClasses.push(classes.disabled) 12229 } 12230 12231 if (buttonType === 'ellipsis') { 12232 btn = $('<span class="ellipsis"></span>').html(content)[0]; 12233 } 12234 else { 12235 btn = $('<button>', { 12236 class: btnClasses.join(' '), 12237 role: 'link', 12238 type: 'button' 12239 }).html(content); 12240 } 12241 12242 return { 12243 display: btn, 12244 clicker: btn 12245 } 12246 } 12247 }, 12248 12249 pagingContainer: { 12250 _: function (settings, buttons) { 12251 // No wrapping element - just append directly to the host 12252 return buttons; 12253 } 12254 } 12255 }); 12256 12257 // Common function to remove new lines, strip HTML and diacritic control 12258 var _filterString = function (stripHtml, normalize) { 12259 return function (str) { 12260 if (_empty(str) || typeof str !== 'string') { 12261 return str; 12262 } 12263 12264 str = str.replace(_re_new_lines, " "); 12265 12266 if (stripHtml) { 12267 str = _stripHtml(str); 12268 } 12269 12270 if (normalize) { 12271 str = _normalize(str, false); 12272 } 12273 12274 return str; 12275 }; 12276 } 12277 12278 /* 12279 * Public helper functions. These aren't used internally by DataTables, or 12280 * called by any of the options passed into DataTables, but they can be used 12281 * externally by developers working with DataTables. They are helper functions 12282 * to make working with DataTables a little bit easier. 12283 */ 12284 12285 /** 12286 * Common logic for moment, luxon or a date action. 12287 * 12288 * Happens after __mldObj, so don't need to call `resolveWindowsLibs` again 12289 */ 12290 function __mld(dtLib, momentFn, luxonFn, dateFn, arg1) { 12291 if (__moment) { 12292 return dtLib[momentFn](arg1); 12293 } 12294 else if (__luxon) { 12295 return dtLib[luxonFn](arg1); 12296 } 12297 12298 return dateFn ? dtLib[dateFn](arg1) : dtLib; 12299 } 12300 12301 12302 var __mlWarning = false;
vendor: 6,471 bytes, lines 12303-12495
12303 var __luxon; // Can be assigned in DateTable.use() 12304 var __moment; // Can be assigned in DateTable.use() 12305 12306 /** 12307 * 12308 */ 12309 function resolveWindowLibs() { 12310 if (window.luxon && !__luxon) { 12311 __luxon = window.luxon; 12312 } 12313 12314 if (window.moment && !__moment) { 12315 __moment = window.moment; 12316 } 12317 } 12318 12319 function __mldObj(d, format, locale) { 12320 var dt; 12321 12322 resolveWindowLibs(); 12323 12324 if (__moment) { 12325 dt = __moment.utc(d, format, locale, true); 12326 12327 if (!dt.isValid()) { 12328 return null; 12329 } 12330 } 12331 else if (__luxon) { 12332 dt = format && typeof d === 'string' 12333 ? __luxon.DateTime.fromFormat(d, format) 12334 : __luxon.DateTime.fromISO(d); 12335 12336 if (!dt.isValid) { 12337 return null; 12338 } 12339 12340 dt = dt.setLocale(locale); 12341 } 12342 else if (!format) { 12343 // No format given, must be ISO 12344 dt = new Date(d); 12345 } 12346 else { 12347 if (!__mlWarning) { 12348 alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17'); 12349 } 12350 12351 __mlWarning = true; 12352 } 12353 12354 return dt; 12355 } 12356 12357 // Wrapper for date, datetime and time which all operate the same way with the exception of 12358 // the output string for auto locale support 12359 function __mlHelper(localeString) { 12360 return function (from, to, locale, def) { 12361 // Luxon and Moment support 12362 // Argument shifting 12363 if (arguments.length === 0) { 12364 locale = 'en'; 12365 to = null; // means toLocaleString 12366 from = null; // means iso8601 12367 } 12368 else if (arguments.length === 1) { 12369 locale = 'en'; 12370 to = from; 12371 from = null; 12372 } 12373 else if (arguments.length === 2) { 12374 locale = to; 12375 to = from; 12376 from = null; 12377 } 12378 12379 var typeName = 'datetime' + (to ? '-' + to : ''); 12380 12381 // Add type detection and sorting specific to this date format - we need to be able to identify 12382 // date type columns as such, rather than as numbers in extensions. Hence the need for this. 12383 if (!DataTable.ext.type.order[typeName + '-pre']) { 12384 DataTable.type(typeName, { 12385 detect: function (d) { 12386 // The renderer will give the value to type detect as the type! 12387 return d === typeName ? typeName : false; 12388 }, 12389 order: { 12390 pre: function (d) { 12391 // The renderer gives us Moment, Luxon or Date objects for the sorting, all of which have a 12392 // `valueOf` which gives milliseconds epoch 12393 return d.valueOf(); 12394 } 12395 }, 12396 className: 'dt-right' 12397 }); 12398 } 12399 12400 return function (d, type) { 12401 // Allow for a default value 12402 if (d === null || d === undefined) { 12403 if (def === '--now') { 12404 // We treat everything as UTC further down, so no changes are 12405 // made, as such need to get the local date / time as if it were 12406 // UTC 12407 var local = new Date(); 12408 d = new Date(Date.UTC( 12409 local.getFullYear(), local.getMonth(), local.getDate(), 12410 local.getHours(), local.getMinutes(), local.getSeconds() 12411 )); 12412 } 12413 else { 12414 d = ''; 12415 } 12416 } 12417 12418 if (type === 'type') { 12419 // Typing uses the type name for fast matching 12420 return typeName; 12421 } 12422 12423 if (d === '') { 12424 return type !== 'sort' 12425 ? '' 12426 : __mldObj('0000-01-01 00:00:00', null, locale); 12427 } 12428 12429 // Shortcut. If `from` and `to` are the same, we are using the renderer to 12430 // format for ordering, not display - its already in the display format. 12431 if (to !== null && from === to && type !== 'sort' && type !== 'type' && !(d instanceof Date)) { 12432 return d; 12433 } 12434 12435 var dt = __mldObj(d, from, locale); 12436 12437 if (dt === null) { 12438 return d; 12439 } 12440 12441 if (type === 'sort') { 12442 return dt; 12443 } 12444 12445 var formatted = to === null 12446 ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]( 12447 navigator.language, 12448 { timeZone: "UTC" } 12449 ) 12450 : __mld(dt, 'format', 'toFormat', 'toISOString', to); 12451 12452 // XSS protection 12453 return type === 'display' ? 12454 _escapeHtml(formatted) : 12455 formatted; 12456 }; 12457 } 12458 } 12459 12460 // Based on locale, determine standard number formatting 12461 // Fallback for legacy browsers is US English 12462 var __thousands = ','; 12463 var __decimal = '.'; 12464 12465 if (window.Intl !== undefined) { 12466 try { 12467 var num = new Intl.NumberFormat().formatToParts(100000.1); 12468 12469 for (var i = 0; i < num.length; i++) { 12470 if (num[i].type === 'group') { 12471 __thousands = num[i].value; 12472 } 12473 else if (num[i].type === 'decimal') { 12474 __decimal = num[i].value; 12475 } 12476 } 12477 } 12478 catch (e) { 12479 // noop 12480 } 12481 } 12482 12483 // Formatted date time detection - use by declaring the formats you are going to use 12484 DataTable.datetime = function (format, locale) { 12485 var typeName = 'datetime-' + format; 12486 12487 if (!locale) { 12488 locale = 'en'; 12489 } 12490 12491 if (!DataTable.ext.type.order[typeName]) { 12492 DataTable.type(typeName, { 12493 detect: function (d) { 12494 var dt = __mldObj(d, format, locale); 12495 return d === '' || dt ? typeName : false;
12496 }, 12497 order: { 12498 pre: function (d) { 12499 return __mldObj(d, format, locale) || 0; 12500 } 12501 }, 12502 className: 'dt-right' 12503 }); 12504 } 12505 } 12506 12507 /** 12508 * Helpers for `columns.render`. 12509 * 12510 * The options defined here can be used with the `columns.render` initialisation 12511 * option to provide a display renderer. The following functions are defined: 12512 * 12513 * * `moment` - Uses the MomentJS library to convert from a given format into another. 12514 * This renderer has three overloads: 12515 * * 1 parameter: 12516 * * `string` - Format to convert to (assumes input is ISO8601 and locale is `en`) 12517 * * 2 parameters: 12518 * * `string` - Format to convert from 12519 * * `string` - Format to convert to. Assumes `en` locale 12520 * * 3 parameters: 12521 * * `string` - Format to convert from 12522 * * `string` - Format to convert to 12523 * * `string` - Locale 12524 * * `number` - Will format numeric data (defined by `columns.data`) for 12525 * display, retaining the original unformatted data for sorting and filtering. 12526 * It takes 5 parameters: 12527 * * `string` - Thousands grouping separator 12528 * * `string` - Decimal point indicator 12529 * * `integer` - Number of decimal points to show 12530 * * `string` (optional) - Prefix. 12531 * * `string` (optional) - Postfix (/suffix). 12532 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional 12533 * parameters. 12534 * 12535 * @example 12536 * // Column definition using the number renderer 12537 * { 12538 * data: "salary", 12539 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' ) 12540 * } 12541 * 12542 * @namespace 12543 */ 12544 DataTable.render = { 12545 date: __mlHelper('toLocaleDateString'), 12546 datetime: __mlHelper('toLocaleString'), 12547 time: __mlHelper('toLocaleTimeString'), 12548 number: function (thousands, decimal, precision, prefix, postfix) { 12549 // Auto locale detection 12550 if (thousands === null || thousands === undefined) { 12551 thousands = __thousands; 12552 } 12553 12554 if (decimal === null || decimal === undefined) { 12555 decimal = __decimal; 12556 } 12557 12558 return { 12559 display: function (d) { 12560 if (typeof d !== 'number' && typeof d !== 'string') { 12561 return d; 12562 } 12563 12564 if (d === '' || d === null) { 12565 return d; 12566 } 12567 12568 var negative = d < 0 ? '-' : ''; 12569 var flo = parseFloat(d); 12570 var abs = Math.abs(flo); 12571 12572 // Scientific notation for large and small numbers 12573 if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0)) { 12574 var exp = flo.toExponential(precision).split(/e\+?/); 12575 return exp[0] + ' x 10<sup>' + exp[1] + '</sup>'; 12576 } 12577 12578 // If NaN then there isn't much formatting that we can do - just 12579 // return immediately, escaping any HTML (this was supposed to 12580 // be a number after all) 12581 if (isNaN(flo)) { 12582 return _escapeHtml(d); 12583 } 12584 12585 flo = flo.toFixed(precision); 12586 d = Math.abs(flo); 12587 12588 var intPart = parseInt(d, 10); 12589 var floatPart = precision ? 12590 decimal + (d - intPart).toFixed(precision).substring(2) : 12591 ''; 12592 12593 // If zero, then can't have a negative prefix 12594 if (intPart === 0 && parseFloat(floatPart) === 0) { 12595 negative = ''; 12596 } 12597 12598 return negative + (prefix || '') + 12599 intPart.toString().replace( 12600 /\B(?=(\d{3})+(?!\d))/g, thousands 12601 ) + 12602 floatPart + 12603 (postfix || ''); 12604 } 12605 }; 12606 }, 12607 12608 text: function () { 12609 return { 12610 display: _escapeHtml, 12611 filter: _escapeHtml 12612 }; 12613 } 12614 }; 12615 12616 12617 var _extTypes = DataTable.ext.type; 12618 12619 // Get / set type 12620 DataTable.type = function (name, prop, val) { 12621 if (!prop) { 12622 return { 12623 className: _extTypes.className[name], 12624 detect: _extTypes.detect.find(function (fn) { 12625 return fn._name === name; 12626 }), 12627 order: { 12628 pre: _extTypes.order[name + '-pre'], 12629 asc: _extTypes.order[name + '-asc'], 12630 desc: _extTypes.order[name + '-desc'] 12631 }, 12632 render: _extTypes.render[name], 12633 search: _extTypes.search[name] 12634 }; 12635 } 12636 12637 var setProp = function (prop, propVal) { 12638 _extTypes[prop][name] = propVal; 12639 }; 12640 var setDetect = function (detect) { 12641 // `detect` can be a function or an object - we set a name 12642 // property for either - that is used for the detection 12643 Object.defineProperty(detect, "_name", { value: name }); 12644 12645 var idx = _extTypes.detect.findIndex(function (item) { 12646 return item._name === name; 12647 }); 12648 12649 if (idx === -1) { 12650 _extTypes.detect.unshift(detect); 12651 } 12652 else { 12653 _extTypes.detect.splice(idx, 1, detect); 12654 } 12655 }; 12656 var setOrder = function (obj) { 12657 _extTypes.order[name + '-pre'] = obj.pre; // can be undefined 12658 _extTypes.order[name + '-asc'] = obj.asc; // can be undefined 12659 _extTypes.order[name + '-desc'] = obj.desc; // can be undefined 12660 }; 12661 12662 // prop is optional 12663 if (val === undefined) { 12664 val = prop; 12665 prop = null; 12666 } 12667 12668 if (prop === 'className') { 12669 setProp('className', val); 12670 } 12671 else if (prop === 'detect') { 12672 setDetect(val); 12673 } 12674 else if (prop === 'order') { 12675 setOrder(val); 12676 } 12677 else if (prop === 'render') { 12678 setProp('render', val); 12679 } 12680 else if (prop === 'search') { 12681 setProp('search', val); 12682 } 12683 else if (!prop) { 12684 if (val.className) { 12685 setProp('className', val.className); 12686 } 12687 12688 if (val.detect !== undefined) { 12689 setDetect(val.detect); 12690 } 12691 12692 if (val.order) { 12693 setOrder(val.order); 12694 } 12695 12696 if (val.render !== undefined) { 12697 setProp('render', val.render); 12698 } 12699 12700 if (val.search !== undefined) { 12701 setProp('search', val.search); 12702 } 12703 } 12704 } 12705 12706 // Get a list of types 12707 DataTable.types = function () { 12708 return _extTypes.detect.map(function (fn) { 12709 return fn._name; 12710 }); 12711 }; 12712 12713 var __diacriticSort = function (a, b) { 12714 a = a !== null && a !== undefined ? a.toString().toLowerCase() : ''; 12715 b = b !== null && b !== undefined ? b.toString().toLowerCase() : ''; 12716 12717 // Checked for `navigator.languages` support in `oneOf` so this code can't execute in old 12718 // Safari and thus can disable this check 12719 // eslint-disable-next-line compat/compat 12720 return a.localeCompare(b, navigator.languages[0] || navigator.language, { 12721 numeric: true, 12722 ignorePunctuation: true, 12723 }); 12724 } 12725 12726 var __diacriticHtmlSort = function (a, b) { 12727 a = _stripHtml(a); 12728 b = _stripHtml(b); 12729 12730 return __diacriticSort(a, b); 12731 } 12732 12733 // 12734 // Built in data types 12735 // 12736 12737 DataTable.type('string', { 12738 detect: function () { 12739 return 'string'; 12740 }, 12741 order: { 12742 pre: function (a) { 12743 // This is a little complex, but faster than always calling toString, 12744 // http://jsperf.com/tostring-v-check 12745 return _empty(a) && typeof a !== 'boolean' ? 12746 '' : 12747 typeof a === 'string' ? 12748 a.toLowerCase() : 12749 !a.toString ? 12750 '' : 12751 a.toString(); 12752 } 12753 }, 12754 search: _filterString(false, true) 12755 }); 12756 12757 DataTable.type('string-utf8', { 12758 detect: { 12759 allOf: function (d) { 12760 return true; 12761 }, 12762 oneOf: function (d) { 12763 // At least one data point must contain a non-ASCII character 12764 // This line will also check if navigator.languages is supported or not. If not (Safari 10.0-) 12765 // this data type won't be supported. 12766 // eslint-disable-next-line compat/compat 12767 return !_empty(d) && navigator.languages && typeof d === 'string' && d.match(/[^\x00-\x7F]/); 12768 } 12769 }, 12770 order: { 12771 asc: __diacriticSort, 12772 desc: function (a, b) { 12773 return __diacriticSort(a, b) * -1; 12774 } 12775 }, 12776 search: _filterString(false, true) 12777 }); 12778 12779 12780 DataTable.type('html', { 12781 detect: { 12782 allOf: function (d) { 12783 return _empty(d) || (typeof d === 'string' && d.indexOf('<') !== -1); 12784 }, 12785 oneOf: function (d) { 12786 // At least one data point must contain a `<` 12787 return !_empty(d) && typeof d === 'string' && d.indexOf('<') !== -1; 12788 } 12789 }, 12790 order: { 12791 pre: function (a) { 12792 return _empty(a) ? 12793 '' : 12794 a.replace ? 12795 _stripHtml(a).trim().toLowerCase() : 12796 a + ''; 12797 } 12798 }, 12799 search: _filterString(true, true) 12800 }); 12801 12802 12803 DataTable.type('html-utf8', { 12804 detect: { 12805 allOf: function (d) { 12806 return _empty(d) || (typeof d === 'string' && d.indexOf('<') !== -1); 12807 }, 12808 oneOf: function (d) { 12809 // At least one data point must contain a `<` and a non-ASCII character 12810 // eslint-disable-next-line compat/compat 12811 return navigator.languages && 12812 !_empty(d) && 12813 typeof d === 'string' && 12814 d.indexOf('<') !== -1 && 12815 typeof d === 'string' && d.match(/[^\x00-\x7F]/); 12816 } 12817 }, 12818 order: { 12819 asc: __diacriticHtmlSort, 12820 desc: function (a, b) { 12821 return __diacriticHtmlSort(a, b) * -1; 12822 } 12823 }, 12824 search: _filterString(true, true) 12825 }); 12826 12827 12828 DataTable.type('date', { 12829 className: 'dt-type-date', 12830 detect: { 12831 allOf: function (d) { 12832 // V8 tries _very_ hard to make a string passed into `Date.parse()` 12833 // valid, so we need to use a regex to restrict date formats. Use a 12834 // plug-in for anything other than ISO8601 style strings 12835 if (d && !(d instanceof Date) && !_re_date.test(d)) { 12836 return null; 12837 } 12838 var parsed = Date.parse(d); 12839 return (parsed !== null && !isNaN(parsed)) || _empty(d); 12840 }, 12841 oneOf: function (d) { 12842 // At least one entry must be a date or a string with a date 12843 return (d instanceof Date) || (typeof d === 'string' && _re_date.test(d)); 12844 } 12845 }, 12846 order: { 12847 pre: function (d) { 12848 var ts = Date.parse(d); 12849 return isNaN(ts) ? -Infinity : ts; 12850 } 12851 } 12852 }); 12853 12854 12855 DataTable.type('html-num-fmt', { 12856 className: 'dt-type-numeric', 12857 detect: { 12858 allOf: function (d, settings) { 12859 var decimal = settings.oLanguage.sDecimal; 12860 return _htmlNumeric(d, decimal, true, false); 12861 }, 12862 oneOf: function (d, settings) { 12863 // At least one data point must contain a numeric value 12864 var decimal = settings.oLanguage.sDecimal; 12865 return _htmlNumeric(d, decimal, true, false); 12866 } 12867 }, 12868 order: { 12869 pre: function (d, s) { 12870 var dp = s.oLanguage.sDecimal; 12871 return __numericReplace(d, dp, _re_html, _re_formatted_numeric); 12872 } 12873 }, 12874 search: _filterString(true, true) 12875 }); 12876 12877 12878 DataTable.type('html-num', { 12879 className: 'dt-type-numeric', 12880 detect: { 12881 allOf: function (d, settings) { 12882 var decimal = settings.oLanguage.sDecimal; 12883 return _htmlNumeric(d, decimal, false, true); 12884 }, 12885 oneOf: function (d, settings) { 12886 // At least one data point must contain a numeric value 12887 var decimal = settings.oLanguage.sDecimal; 12888 return _htmlNumeric(d, decimal, false, false); 12889 } 12890 }, 12891 order: { 12892 pre: function (d, s) { 12893 var dp = s.oLanguage.sDecimal; 12894 return __numericReplace(d, dp, _re_html); 12895 } 12896 }, 12897 search: _filterString(true, true) 12898 }); 12899 12900 12901 DataTable.type('num-fmt', { 12902 className: 'dt-type-numeric', 12903 detect: { 12904 allOf: function (d, settings) { 12905 var decimal = settings.oLanguage.sDecimal; 12906 return _isNumber(d, decimal, true, true); 12907 }, 12908 oneOf: function (d, settings) { 12909 // At least one data point must contain a numeric value 12910 var decimal = settings.oLanguage.sDecimal; 12911 return _isNumber(d, decimal, true, false); 12912 } 12913 }, 12914 order: { 12915 pre: function (d, s) { 12916 var dp = s.oLanguage.sDecimal; 12917 return __numericReplace(d, dp, _re_formatted_numeric); 12918 } 12919 } 12920 }); 12921 12922 12923 DataTable.type('num', { 12924 className: 'dt-type-numeric', 12925 detect: { 12926 allOf: function (d, settings) { 12927 var decimal = settings.oLanguage.sDecimal; 12928 return _isNumber(d, decimal, false, true); 12929 }, 12930 oneOf: function (d, settings) { 12931 // At least one data point must contain a numeric value 12932 var decimal = settings.oLanguage.sDecimal; 12933 return _isNumber(d, decimal, false, false); 12934 } 12935 }, 12936 order: { 12937 pre: function (d, s) { 12938 var dp = s.oLanguage.sDecimal; 12939 return __numericReplace(d, dp); 12940 } 12941 } 12942 }); 12943 12944 12945 12946 12947 var __numericReplace = function (d, decimalPlace, re1, re2) { 12948 if (d !== 0 && (!d || d === '-')) { 12949 return -Infinity; 12950 } 12951 12952 var type = typeof d; 12953 12954 if (type === 'number' || type === 'bigint') { 12955 return d; 12956 } 12957 12958 // If a decimal place other than `.` is used, it needs to be given to the 12959 // function so we can detect it and replace with a `.` which is the only 12960 // decimal place JavaScript recognises - it is not locale aware. 12961 if (decimalPlace) { 12962 d = _numToDecimal(d, decimalPlace); 12963 } 12964 12965 if (d.replace) { 12966 if (re1) { 12967 d = d.replace(re1, ''); 12968 } 12969 12970 if (re2) { 12971 d = d.replace(re2, ''); 12972 } 12973 } 12974 12975 return d * 1; 12976 }; 12977 12978 12979 $.extend(true, DataTable.ext.renderer, { 12980 footer: { 12981 _: function (settings, cell, classes) {
12982 cell.addClass(classes.tfoot.cell); 12983 } 12984 }, 12985 12986 header: { 12987 _: function (settings, cell, classes) { 12988 cell.addClass(classes.thead.cell); 12989 12990 if (!settings.oFeatures.bSort) { 12991 cell.addClass(classes.order.none); 12992 } 12993 12994 var titleRow = settings.titleRow; 12995 var headerRows = cell.closest('thead').find('tr'); 12996 var rowIdx = cell.parent().index(); 12997 12998 // Conditions to not apply the ordering icons 12999 if ( 13000 // Cells and rows which have the attribute to disable the icons 13001 cell.attr('data-dt-order') === 'disable' || 13002 cell.parent().attr('data-dt-order') === 'disable' || 13003 13004 // titleRow support, for defining a specific row in the header 13005 (titleRow === true && rowIdx !== 0) || 13006 (titleRow === false && rowIdx !== headerRows.length - 1) || 13007 (typeof titleRow === 'number' && rowIdx !== titleRow) 13008 ) { 13009 return; 13010 } 13011 13012 // No additional mark-up required 13013 // Attach a sort listener to update on sort - note that using the 13014 // `DT` namespace will allow the event to be removed automatically 13015 // on destroy, while the `dt` namespaced event is the one we are 13016 // listening for 13017 $(settings.nTable).on('order.dt.DT column-visibility.dt.DT', function (e, ctx, column) { 13018 if (settings !== ctx) { // need to check if this is the host 13019 return; // table, not a nested one 13020 } 13021 13022 var sorting = ctx.sortDetails; 13023 13024 if (!sorting) { 13025 return; 13026 } 13027 13028 var orderedColumns = _pluck(sorting, 'col'); 13029 13030 // This handler is only needed on column visibility if the column is part of the 13031 // ordering. If it isn't, then we can bail out to save performance. It could be a 13032 // separate event handler, but this is a balance between code reuse / size and performance 13033 // console.log(e, e.name, column, orderedColumns, orderedColumns.includes(column)) 13034 if (e.type === 'column-visibility' && !orderedColumns.includes(column)) { 13035 return; 13036 } 13037 13038 var i; 13039 var orderClasses = classes.order; 13040 var columns = ctx.api.columns(cell); 13041 var col = settings.aoColumns[columns.flatten()[0]]; 13042 var orderable = columns.orderable().includes(true); 13043 var ariaType = ''; 13044 var indexes = columns.indexes(); 13045 var sortDirs = columns.orderable(true).flatten(); 13046 var tabIndex = settings.iTabIndex; 13047 var canOrder = ctx.orderHandler && orderable; 13048 13049 cell 13050 .removeClass( 13051 orderClasses.isAsc + ' ' + 13052 orderClasses.isDesc 13053 ) 13054 .toggleClass(orderClasses.none, !orderable) 13055 .toggleClass(orderClasses.canAsc, canOrder && sortDirs.includes('asc')) 13056 .toggleClass(orderClasses.canDesc, canOrder && sortDirs.includes('desc')); 13057 13058 // Determine if all of the columns that this cell covers are included in the 13059 // current ordering 13060 var isOrdering = true; 13061 13062 for (i = 0; i < indexes.length; i++) { 13063 if (!orderedColumns.includes(indexes[i])) { 13064 isOrdering = false; 13065 } 13066 } 13067 13068 if (isOrdering) { 13069 // Get the ordering direction for the columns under this cell 13070 // Note that it is possible for a cell to be asc and desc sorting 13071 // (column spanning cells) 13072 var orderDirs = columns.order(); 13073 13074 cell.addClass( 13075 orderDirs.includes('asc') ? orderClasses.isAsc : '' + 13076 orderDirs.includes('desc') ? orderClasses.isDesc : '' 13077 ); 13078 } 13079 13080 // Find the first visible column that has ordering applied to it - it get's 13081 // the aria information, as the ARIA spec says that only one column should 13082 // be marked with aria-sort 13083 var firstVis = -1; // column index 13084 13085 for (i = 0; i < orderedColumns.length; i++) { 13086 if (settings.aoColumns[orderedColumns[i]].bVisible) { 13087 firstVis = orderedColumns[i]; 13088 break; 13089 } 13090 } 13091 13092 if (indexes[0] == firstVis) { 13093 var firstSort = sorting[0]; 13094 var sortOrder = col.asSorting; 13095 13096 cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending'); 13097 13098 // Determine if the next click will remove sorting or change the sort 13099 ariaType = !sortOrder[firstSort.index + 1] ? 'Remove' : 'Reverse'; 13100 } 13101 else { 13102 cell.removeAttr('aria-sort'); 13103 } 13104 13105 // Make the headers tab-able for keyboard navigation 13106 if (orderable) { 13107 var orderSpan = cell.find('.dt-column-order'); 13108 13109 orderSpan 13110 .attr('role', 'button') 13111 .attr('aria-label', orderable 13112 ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType) 13113 : col.ariaTitle 13114 ); 13115 13116 if (tabIndex !== -1) { 13117 orderSpan.attr('tabindex', tabIndex); 13118 } 13119 } 13120 }); 13121 } 13122 }, 13123 13124 layout: { 13125 _: function (settings, container, items) { 13126 var classes = settings.oClasses.layout; 13127 var row = $('<div/>') 13128 .attr('id', items.id || null) 13129 .addClass(items.className || classes.row) 13130 .appendTo(container); 13131 13132 DataTable.ext.renderer.layout._forLayoutRow(items, function (key, val) { 13133 if (key === 'id' || key === 'className') { 13134 return; 13135 } 13136 13137 var klass = ''; 13138 13139 if (val.table) {
13140 row.addClass(classes.tableRow); 13141 klass += classes.tableCell + ' '; 13142 } 13143 13144 if (key === 'start') { 13145 klass += classes.start; 13146 } 13147 else if (key === 'end') { 13148 klass += classes.end; 13149 } 13150 else { 13151 klass += classes.full; 13152 } 13153 13154 $('<div/>') 13155 .attr({ 13156 id: val.id || null, 13157 "class": val.className 13158 ? val.className 13159 : classes.cell + ' ' + klass 13160 }) 13161 .append(val.contents) 13162 .appendTo(row); 13163 }); 13164 }, 13165 13166 // Shared for use by the styling frameworks 13167 _forLayoutRow: function (items, fn) { 13168 // As we are inserting dom elements, we need start / end in a 13169 // specific order, this function is used for sorting the layout 13170 // keys. 13171 var layoutEnum = function (x) { 13172 switch (x) { 13173 case '': return 0; 13174 case 'start': return 1; 13175 case 'end': return 2; 13176 default: return 3; 13177 } 13178 }; 13179 13180 Object 13181 .keys(items) 13182 .sort(function (a, b) { 13183 return layoutEnum(a) - layoutEnum(b); 13184 }) 13185 .forEach(function (key) { 13186 fn(key, items[key]); 13187 }); 13188 } 13189 } 13190 }); 13191 13192 13193 DataTable.feature = {}; 13194 13195 // Third parameter is internal only! 13196 DataTable.feature.register = function (name, cb, legacy) { 13197 DataTable.ext.features[name] = cb; 13198 13199 if (legacy) { 13200 _ext.feature.push({ 13201 cFeature: legacy, 13202 fnInit: cb 13203 }); 13204 } 13205 }; 13206 13207 function _divProp(el, prop, val) { 13208 if (val) { 13209 el[prop] = val; 13210 } 13211 } 13212 13213 DataTable.feature.register('div', function (settings, opts) { 13214 var n = $('<div>')[0]; 13215 13216 if (opts) { 13217 _divProp(n, 'className', opts.className); 13218 _divProp(n, 'id', opts.id); 13219 _divProp(n, 'innerHTML', opts.html); 13220 _divProp(n, 'textContent', opts.text); 13221 } 13222 13223 return n; 13224 }); 13225 13226 DataTable.feature.register('info', function (settings, opts) { 13227 // For compatibility with the legacy `info` top level option 13228 if (!settings.oFeatures.bInfo) { 13229 return null; 13230 } 13231 13232 var 13233 lang = settings.oLanguage, 13234 tid = settings.sTableId, 13235 n = $('<div/>', { 13236 'class': settings.oClasses.info.container, 13237 }); 13238 13239 opts = $.extend({ 13240 callback: lang.fnInfoCallback, 13241 empty: lang.sInfoEmpty, 13242 postfix: lang.sInfoPostFix, 13243 search: lang.sInfoFiltered, 13244 text: lang.sInfo, 13245 }, opts); 13246 13247 13248 // Update display on each draw 13249 settings.aoDrawCallback.push(function (s) { 13250 _fnUpdateInfo(s, opts, n); 13251 }); 13252 13253 // For the first info display in the table, we add a callback and aria information. 13254 if (!settings._infoEl) { 13255 n.attr({ 13256 'aria-live': 'polite', 13257 id: tid + '_info', 13258 role: 'status' 13259 }); 13260 13261 // Table is described by our info div 13262 $(settings.nTable).attr('aria-describedby', tid + '_info'); 13263 13264 settings._infoEl = n; 13265 } 13266 13267 return n; 13268 }, 'i'); 13269 13270 /** 13271 * Update the information elements in the display 13272 * @param {object} settings dataTables settings object 13273 * @memberof DataTable#oApi 13274 */ 13275 function _fnUpdateInfo(settings, opts, node) { 13276 var 13277 start = settings._iDisplayStart + 1, 13278 end = settings.fnDisplayEnd(), 13279 max = settings.fnRecordsTotal(), 13280 total = settings.fnRecordsDisplay(), 13281 out = total 13282 ? opts.text 13283 : opts.empty; 13284 13285 if (total !== max) { 13286 // Record set after filtering 13287 out += ' ' + opts.search; 13288 } 13289 13290 // Convert the macros 13291 out += opts.postfix; 13292 out = _fnMacros(settings, out); 13293 13294 if (opts.callback) { 13295 out = opts.callback.call(settings.oInstance, 13296 settings, start, end, max, total, out 13297 ); 13298 } 13299 13300 node.html(out); 13301 13302 _fnCallbackFire(settings, null, 'info', [settings, node[0], out]); 13303 } 13304 13305 var __searchCounter = 0; 13306 13307 // opts 13308 // - text 13309 // - placeholder 13310 DataTable.feature.register('search', function (settings, opts) { 13311 // Don't show the input if filtering isn't available on the table 13312 if (!settings.oFeatures.bFilter) { 13313 return null; 13314 } 13315 13316 var classes = settings.oClasses.search; 13317 var tableId = settings.sTableId; 13318 var language = settings.oLanguage; 13319 var previousSearch = settings.oPreviousSearch; 13320 var input = '<input type="search" class="' + classes.input + '"/>'; 13321 13322 opts = $.extend({ 13323 placeholder: language.sSearchPlaceholder, 13324 processing: false, 13325 text: language.sSearch 13326 }, opts); 13327
13328 // The _INPUT_ is optional - is appended if not present 13329 if (opts.text.indexOf('_INPUT_') === -1) { 13330 opts.text += '_INPUT_'; 13331 } 13332 13333 opts.text = _fnMacros(settings, opts.text); 13334 13335 // We can put the <input> outside of the label if it is at the start or end 13336 // which helps improve accessability (not all screen readers like implicit 13337 // for elements). 13338 var end = opts.text.match(/_INPUT_$/); 13339 var start = opts.text.match(/^_INPUT_/); 13340 var removed = opts.text.replace(/_INPUT_/, ''); 13341 var str = '<label>' + opts.text + '</label>'; 13342 13343 if (start) { 13344 str = '_INPUT_<label>' + removed + '</label>'; 13345 } 13346 else if (end) { 13347 str = '<label>' + removed + '</label>_INPUT_'; 13348 } 13349 13350 var filter = $('<div>') 13351 .addClass(classes.container) 13352 .append(str.replace(/_INPUT_/, input)); 13353 13354 // add for and id to label and input 13355 filter.find('label').attr('for', 'dt-search-' + __searchCounter); 13356 filter.find('input').attr('id', 'dt-search-' + __searchCounter); 13357 __searchCounter++; 13358 13359 var searchFn = function (event) { 13360 var val = this.value; 13361 13362 if (previousSearch.return && event.key !== "Enter") { 13363 return; 13364 } 13365 13366 /* Now do the filter */ 13367 if (val != previousSearch.search) { 13368 _fnProcessingRun(settings, opts.processing, function () { 13369 previousSearch.search = val; 13370 13371 _fnFilterComplete(settings, previousSearch); 13372 13373 // Need to redraw, without resorting 13374 settings._iDisplayStart = 0; 13375 _fnDraw(settings); 13376 }); 13377 } 13378 }; 13379 13380 var searchDelay = settings.searchDelay !== null ? 13381 settings.searchDelay : 13382 0; 13383 13384 var jqFilter = $('input', filter) 13385 .val(previousSearch.search) 13386 .attr('placeholder', opts.placeholder) 13387 .on( 13388 'keyup.DT search.DT input.DT paste.DT cut.DT', 13389 searchDelay ? 13390 DataTable.util.debounce(searchFn, searchDelay) : 13391 searchFn 13392 ) 13393 .on('mouseup.DT', function (e) { 13394 // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking 13395 // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn` 13396 // checks the value to see if it has changed. In other browsers it won't have. 13397 setTimeout(function () { 13398 searchFn.call(jqFilter[0], e); 13399 }, 10); 13400 }) 13401 .on('keypress.DT', function (e) { 13402 /* Prevent form submission */ 13403 if (e.keyCode == 13) { 13404 return false; 13405 } 13406 }) 13407 .attr('aria-controls', tableId); 13408 13409 // Update the input elements whenever the table is filtered 13410 $(settings.nTable).on('search.dt.DT', function (ev, s) { 13411 if (settings === s && jqFilter[0] !== document.activeElement) { 13412 jqFilter.val(typeof previousSearch.search !== 'function' 13413 ? previousSearch.search 13414 : '' 13415 ); 13416 } 13417 }); 13418 13419 return filter; 13420 }, 'f'); 13421 13422 // opts 13423 // - type - button configuration 13424 // - buttons - number of buttons to show - must be odd 13425 DataTable.feature.register('paging', function (settings, opts) { 13426 // Don't show the paging input if the table doesn't have paging enabled 13427 if (!settings.oFeatures.bPaginate) { 13428 return null; 13429 } 13430 13431 opts = $.extend({ 13432 buttons: DataTable.ext.pager.numbers_length, 13433 type: settings.sPaginationType, 13434 boundaryNumbers: true, 13435 firstLast: true, 13436 previousNext: true, 13437 numbers: true 13438 }, opts); 13439 13440 var host = $('<div/>') 13441 .addClass(settings.oClasses.paging.container + (opts.type ? ' paging_' + opts.type : '')) 13442 .append( 13443 $('<nav>') 13444 .attr('aria-label', 'pagination') 13445 .addClass(settings.oClasses.paging.nav) 13446 ); 13447 var draw = function () { 13448 _pagingDraw(settings, host.children(), opts); 13449 }; 13450 13451 settings.aoDrawCallback.push(draw); 13452 13453 // Responsive redraw of paging control 13454 $(settings.nTable).on('column-sizing.dt.DT', draw); 13455 13456 return host; 13457 }, 'p'); 13458 13459 /** 13460 * Dynamically create the button type array based on the configuration options. 13461 * This will only happen if the paging type is not defined. 13462 */ 13463 function _pagingDynamic(opts) { 13464 var out = []; 13465 13466 if (opts.numbers) { 13467 out.push('numbers'); 13468 } 13469 13470 if (opts.previousNext) { 13471 out.unshift('previous'); 13472 out.push('next'); 13473 } 13474 13475 if (opts.firstLast) { 13476 out.unshift('first'); 13477 out.push('last'); 13478 } 13479 13480 return out; 13481 } 13482 13483 function _pagingDraw(settings, host, opts) { 13484 if (!settings._bInitComplete) { 13485 return; 13486 } 13487 13488 var 13489 plugin = opts.type 13490 ? DataTable.ext.pager[opts.type] 13491 : _pagingDynamic, 13492 aria = settings.oLanguage.oAria.paginate || {}, 13493 start = settings._iDisplayStart, 13494 len = settings._iDisplayLength, 13495 visRecords = settings.fnRecordsDisplay(), 13496 all = len === -1, 13497 page = all ? 0 : Math.ceil(start / len), 13498 pages = all ? 1 : Math.ceil(visRecords / len), 13499 buttons = [], 13500 buttonEls = [], 13501 buttonsNested = plugin(opts) 13502 .map(function (val) { 13503 return val === 'numbers' 13504 ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers) 13505 : val; 13506 }); 13507 13508 // .flat() would be better, but not supported in old Safari 13509 buttons = buttons.concat.apply(buttons, buttonsNested); 13510 13511 for (var i = 0; i < buttons.length; i++) { 13512 var button = buttons[i]; 13513 13514 var btnInfo = _pagingButtonInfo(settings, button, page, pages); 13515 var btn = _fnRenderer(settings, 'pagingButton')( 13516 settings, 13517 button, 13518 btnInfo.display, 13519 btnInfo.active, 13520 btnInfo.disabled 13521 ); 13522 13523 var ariaLabel = typeof button === 'string' 13524 ? aria[button] 13525 : aria.number 13526 ? aria.number + (button + 1) 13527 : null; 13528 13529 // Common attributes 13530 $(btn.clicker).attr({ 13531 'aria-controls': settings.sTableId, 13532 'aria-disabled': btnInfo.disabled ? 'true' : null, 13533 'aria-current': btnInfo.active ? 'page' : null, 13534 'aria-label': ariaLabel, 13535 'data-dt-idx': button, 13536 'tabIndex': btnInfo.disabled 13537 ? -1 13538 : settings.iTabIndex && btn.clicker[0].nodeName.toLowerCase() !== 'span' 13539 ? settings.iTabIndex 13540 : null, // `0` doesn't need a tabIndex since it is the default 13541 }); 13542 13543 if (typeof button !== 'number') { 13544 $(btn.clicker).addClass(button); 13545 } 13546 13547 _fnBindAction( 13548 btn.clicker, { action: button }, function (e) { 13549 e.preventDefault(); 13550
13551 _fnPageChange(settings, e.data.action, true); 13552 } 13553 ); 13554 13555 buttonEls.push(btn.display); 13556 } 13557 13558 var wrapped = _fnRenderer(settings, 'pagingContainer')( 13559 settings, buttonEls 13560 ); 13561 13562 var activeEl = host.find(document.activeElement).data('dt-idx'); 13563 13564 host.empty().append(wrapped); 13565 13566 if (activeEl !== undefined) { 13567 host.find('[data-dt-idx=' + activeEl + ']').trigger('focus'); 13568 } 13569 13570 // Responsive - check if the buttons are over two lines based on the 13571 // height of the buttons and the container. 13572 if (buttonEls.length) { 13573 var outerHeight = $(buttonEls[0]).outerHeight(); 13574 13575 if ( 13576 opts.buttons > 1 && // prevent infinite 13577 outerHeight > 0 && // will be 0 if hidden 13578 $(host).height() >= (outerHeight * 2) - 10 13579 ) { 13580 _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 })); 13581 } 13582 } 13583 } 13584 13585 /** 13586 * Get properties for a button based on the current paging state of the table 13587 * 13588 * @param {*} settings DT settings object 13589 * @param {*} button The button type in question 13590 * @param {*} page Table's current page 13591 * @param {*} pages Number of pages 13592 * @returns Info object 13593 */ 13594 function _pagingButtonInfo(settings, button, page, pages) { 13595 var lang = settings.oLanguage.oPaginate; 13596 var o = { 13597 display: '', 13598 active: false, 13599 disabled: false 13600 }; 13601 13602 switch (button) { 13603 case 'ellipsis': 13604 o.display = '…'; 13605 break; 13606 13607 case 'first': 13608 o.display = lang.sFirst; 13609 13610 if (page === 0) { 13611 o.disabled = true; 13612 } 13613 break; 13614 13615 case 'previous': 13616 o.display = lang.sPrevious; 13617 13618 if (page === 0) { 13619 o.disabled = true; 13620 } 13621 break; 13622 13623 case 'next': 13624 o.display = lang.sNext; 13625 13626 if (pages === 0 || page === pages - 1) { 13627 o.disabled = true; 13628 } 13629 break; 13630 13631 case 'last': 13632 o.display = lang.sLast; 13633 13634 if (pages === 0 || page === pages - 1) { 13635 o.disabled = true; 13636 } 13637 break; 13638 13639 default: 13640 if (typeof button === 'number') { 13641 o.display = settings.fnFormatNumber(button + 1); 13642 13643 if (page === button) { 13644 o.active = true; 13645 } 13646 } 13647 break; 13648 } 13649 13650 return o; 13651 } 13652 13653 /** 13654 * Compute what number buttons to show in the paging control 13655 * 13656 * @param {*} page Current page 13657 * @param {*} pages Total number of pages 13658 * @param {*} buttons Target number of number buttons 13659 * @param {boolean} addFirstLast Indicate if page 1 and end should be included 13660 * @returns Buttons to show 13661 */ 13662 function _pagingNumbers(page, pages, buttons, addFirstLast) { 13663 var 13664 numbers = [], 13665 half = Math.floor(buttons / 2), 13666 before = addFirstLast ? 2 : 1, 13667 after = addFirstLast ? 1 : 0; 13668 13669 if (pages <= buttons) { 13670 numbers = _range(0, pages); 13671 } 13672 else if (buttons === 1) { 13673 // Single button - current page only 13674 numbers = [page]; 13675 } 13676 else if (buttons === 3) { 13677 // Special logic for just three buttons 13678 if (page <= 1) { 13679 numbers = [0, 1, 'ellipsis']; 13680 } 13681 else if (page >= pages - 2) { 13682 numbers = _range(pages - 2, pages); 13683 numbers.unshift('ellipsis'); 13684 } 13685 else { 13686 numbers = ['ellipsis', page, 'ellipsis']; 13687 } 13688 } 13689 else if (page <= half) { 13690 numbers = _range(0, buttons - before); 13691 numbers.push('ellipsis'); 13692 13693 if (addFirstLast) { 13694 numbers.push(pages - 1); 13695 } 13696 } 13697 else if (page >= pages - 1 - half) { 13698 numbers = _range(pages - (buttons - before), pages); 13699 numbers.unshift('ellipsis'); 13700 13701 if (addFirstLast) { 13702 numbers.unshift(0); 13703 } 13704 } 13705 else { 13706 numbers = _range(page - half + before, page + half - after); 13707 numbers.push('ellipsis'); 13708 numbers.unshift('ellipsis'); 13709 13710 if (addFirstLast) { 13711 numbers.push(pages - 1); 13712 numbers.unshift(0); 13713 } 13714 } 13715 13716 return numbers; 13717 } 13718 13719 var __lengthCounter = 0; 13720 13721 // opts 13722 // - menu 13723 // - text 13724 DataTable.feature.register('pageLength', function (settings, opts) { 13725 var features = settings.oFeatures; 13726 13727 // For compatibility with the legacy `pageLength` top level option 13728 if (!features.bPaginate || !features.bLengthChange) { 13729 return null; 13730 } 13731 13732 opts = $.extend({ 13733 menu: settings.aLengthMenu, 13734 text: settings.oLanguage.sLengthMenu 13735 }, opts); 13736 13737 var 13738 classes = settings.oClasses.length, 13739 tableId = settings.sTableId, 13740 menu = opts.menu, 13741 lengths = [], 13742 language = [], 13743 i; 13744 13745 // Options can be given in a number of ways 13746 if (Array.isArray(menu[0])) { 13747 // Old 1.x style - 2D array 13748 lengths = menu[0]; 13749 language = menu[1]; 13750 } 13751 else { 13752 for (i = 0; i < menu.length; i++) { 13753 // An object with different label and value 13754 if ($.isPlainObject(menu[i])) { 13755 lengths.push(menu[i].value); 13756 language.push(menu[i].label); 13757 } 13758 else { 13759 // Or just a number to display and use 13760 lengths.push(menu[i]); 13761 language.push(menu[i]); 13762 } 13763 } 13764 } 13765 13766 // We can put the <select> outside of the label if it is at the start or 13767 // end which helps improve accessability (not all screen readers like 13768 // implicit for elements). 13769 var end = opts.text.match(/_MENU_$/); 13770 var start = opts.text.match(/^_MENU_/); 13771 var removed = opts.text.replace(/_MENU_/, ''); 13772 var str = '<label>' + opts.text + '</label>'; 13773 13774 if (start) { 13775 str = '_MENU_<label>' + removed + '</label>'; 13776 } 13777 else if (end) { 13778 str = '<label>' + removed + '</label>_MENU_'; 13779 } 13780 13781 // Wrapper element - use a span as a holder for where the select will go 13782 var tmpId = 'tmp-' + (+new Date())
13783 var div = $('<div/>') 13784 .addClass(classes.container) 13785 .append( 13786 str.replace('_MENU_', '<span id="' + tmpId + '"></span>') 13787 ); 13788 13789 // Save text node content for macro updating 13790 var textNodes = []; 13791 Array.prototype.slice.call(div.find('label')[0].childNodes).forEach(function (el) { 13792 if (el.nodeType === Node.TEXT_NODE) { 13793 textNodes.push({ 13794 el: el, 13795 text: el.textContent 13796 }); 13797 } 13798 }); 13799 13800 // Update the label text in case it has an entries value 13801 var updateEntries = function (len) { 13802 textNodes.forEach(function (node) { 13803 node.el.textContent = _fnMacros(settings, node.text, len); 13804 }); 13805 } 13806 13807 // Next, the select itself, along with the options 13808 var select = $('<select/>', { 13809 'aria-controls': tableId, 13810 'class': classes.select 13811 }); 13812 13813 for (i = 0; i < lengths.length; i++) { 13814 // Attempt to look up the length from the i18n options 13815 var label = settings.api.i18n('lengthLabels.' + lengths[i], null); 13816 13817 if (label === null) { 13818 // If not present, fallback to old style 13819 label = typeof language[i] === 'number' ? 13820 settings.fnFormatNumber(language[i]) : 13821 language[i]; 13822 } 13823 13824 select[0][i] = new Option(label, lengths[i]); 13825 } 13826 13827 // add for and id to label and input 13828 div.find('label').attr('for', 'dt-length-' + __lengthCounter); 13829 select.attr('id', 'dt-length-' + __lengthCounter); 13830 __lengthCounter++; 13831 13832 // Swap in the select list 13833 div.find('#' + tmpId).replaceWith(select); 13834 13835 // Can't use `select` variable as user might provide their own and the 13836 // reference is broken by the use of outerHTML 13837 $('select', div) 13838 .val(settings._iDisplayLength) 13839 .on('change.DT', function () { 13840 _fnLengthChange(settings, $(this).val()); 13841 _fnDraw(settings); 13842 }); 13843 13844 // Update node value whenever anything changes the table's length 13845 $(settings.nTable).on('length.dt.DT', function (e, s, len) { 13846 if (settings === s) { 13847 $('select', div).val(len); 13848 13849 // Resolve plurals in the text for the new length 13850 updateEntries(len); 13851 } 13852 }); 13853 13854 updateEntries(settings._iDisplayLength); 13855 13856 return div; 13857 }, 'l'); 13858 13859 // jQuery access 13860 $.fn.dataTable = DataTable; 13861 13862 // Provide access to the host jQuery object (circular reference) 13863 DataTable.$ = $; 13864 13865 // Legacy aliases 13866 $.fn.dataTableSettings = DataTable.settings; 13867 $.fn.dataTableExt = DataTable.ext; 13868 13869 // With a capital `D` we return a DataTables API instance rather than a 13870 // jQuery object 13871 $.fn.DataTable = function (opts) { 13872 return $(this).dataTable(opts).api(); 13873 }; 13874 13875 // All properties that are available to $.fn.dataTable should also be 13876 // available on $.fn.DataTable 13877 $.each(DataTable, function (prop, val) { 13878 $.fn.DataTable[prop] = val; 13879 }); 13880 13881 return DataTable; 13882}));
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.