1/* 2 * This combined file was created by the DataTables downloader builder: 3 * https://datatables.net/download 4 * 5 * To rebuild or modify this file with the latest versions of the included 6 * software please visit: 7 * https://datatables.net/download/#dt/dt-1.10.11,af-2.1.1,b-1.1.2,b-html5-1.1.2,fh-3.1.1 8 * 9 * Included libraries: 10 * DataTables 1.10.11, AutoFill 2.1.1, Buttons 1.1.2, HTML5 export 1.1.2, FixedHeader 3.1.1 11 */ 12 13/*! DataTables 1.10.11 14 * ©2008-2015 SpryMedia Ltd - datatables.net/license 15 */ 16 17/** 18 * @summary DataTables 19 * @description Paginate, search and order HTML tables 20 * @version 1.10.11 21 * @file jquery.dataTables.js 22 * @author SpryMedia Ltd (www.sprymedia.co.uk) 23 * @contact www.sprymedia.co.uk/contact 24 * @copyright Copyright 2008-2015 SpryMedia Ltd. 25 * 26 * This source file is free software, available under the following license: 27 * MIT license - http://datatables.net/license 28 * 29 * This source file is distributed in the hope that it will be useful, but 30 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 31 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. 32 * 33 * For details please refer to: http://www.datatables.net 34 */ 35 36/*jslint evil: true, undef: true, browser: true */ 37/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/ 38 39(function( factory ) { 40 "use strict"; 41 42 if ( typeof define === 'function' && define.amd ) { 43 // AMD 44 define( ['jquery'], function ( $ ) { 45 return factory( $, window, document ); 46 } ); 47 } 48 else if ( typeof exports === 'object' ) { 49 // CommonJS 50 module.exports = function (root, $) { 51 if ( ! root ) { 52 // CommonJS environments without a window global must pass a 53 // root. This will give an error otherwise 54 root = window; 55 } 56 57 if ( ! $ ) { 58 $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window 59 require('jquery') : 60 require('jquery')( root ); 61 } 62 63 return factory( $, root, root.document ); 64 }; 65 } 66 else { 67 // Browser 68 factory( jQuery, window, document ); 69 } 70} 71(function( $, window, document, undefined ) { 72 "use strict"; 73 74 /** 75 * DataTables is a plug-in for the jQuery Javascript library. It is a highly 76 * flexible tool, based upon the foundations of progressive enhancement, 77 * which will add advanced interaction controls to any HTML table. For a 78 * full list of features please refer to 79 * [DataTables.net](href="http://datatables.net). 80 * 81 * Note that the `DataTable` object is not a global variable but is aliased 82 * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may 83 * be accessed. 84 * 85 * @class 86 * @param {object} [init={}] Configuration object for DataTables. Options 87 * are defined by {@link DataTable.defaults} 88 * @requires jQuery 1.7+ 89 * 90 * @example 91 * // Basic initialisation 92 * $(document).ready( function { 93 * $('#example').dataTable(); 94 * } ); 95 * 96 * @example 97 * // Initialisation with configuration options - in this case, disable 98 * // pagination and sorting. 99 * $(document).ready( function { 100 * $('#example').dataTable( { 101 * "paginate": false, 102 * "sort": false 103 * } ); 104 * } ); 105 */ 106 var DataTable; 107 108 109 /*
110 * It is useful to have variables which are scoped locally so only the 111 * DataTables functions can access them and they don't leak into global space. 112 * At the same time these functions are often useful over multiple files in the 113 * core and API, so we list, or at least document, all variables which are used 114 * by DataTables as private variables here. This also ensures that there is no 115 * clashing of variable names and that they can easily referenced for reuse. 116 */ 117 118 119 // Defined else where 120 // _selector_run 121 // _selector_opts 122 // _selector_first 123 // _selector_row_indexes 124 125 var _ext; // DataTable.ext 126 var _Api; // DataTable.Api 127 var _api_register; // DataTable.Api.register 128 var _api_registerPlural; // DataTable.Api.registerPlural 129 130 var _re_dic = {}; 131 var _re_new_lines = /[\r\n]/g; 132 var _re_html = /<.*?>/g; 133 var _re_date_start = /^[\w\+\-]/; 134 var _re_date_end = /[\w\+\-]$/; 135 136 // Escape regular expression special characters 137 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); 138 139 // http://en.wikipedia.org/wiki/Foreign_exchange_market 140 // - \u20BD - Russian ruble. 141 // - \u20a9 - South Korean Won 142 // - \u20BA - Turkish Lira 143 // - \u20B9 - Indian Rupee 144 // - R - Brazil (R$) and South Africa 145 // - fr - Swiss Franc 146 // - kr - Swedish krona, Norwegian krone and Danish krone 147 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many 148 // standards as thousands separators. 149 var _re_formatted_numeric = /[',$£â¬Â¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi; 150 151 152 var _empty = function ( d ) { 153 return !d || d === true || d === '-' ? true : false; 154 }; 155 156 157 var _intVal = function ( s ) { 158 var integer = parseInt( s, 10 ); 159 return !isNaN(integer) && isFinite(s) ? integer : null; 160 }; 161 162 // Convert from a formatted number with characters other than `.` as the 163 // decimal place, to a Javascript number 164 var _numToDecimal = function ( num, decimalPoint ) { 165 // Cache created regular expressions for speed as this function is called often 166 if ( ! _re_dic[ decimalPoint ] ) { 167 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); 168 } 169 return typeof num === 'string' && decimalPoint !== '.' ? 170 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : 171 num; 172 }; 173 174 175 var _isNumber = function ( d, decimalPoint, formatted ) { 176 var strType = typeof d === 'string'; 177 178 // If empty return immediately so there must be a number if it is a 179 // formatted string (this stops the string "k", or "kr", etc being detected 180 // as a formatted number for currency 181 if ( _empty( d ) ) { 182 return true; 183 } 184 185 if ( decimalPoint && strType ) { 186 d = _numToDecimal( d, decimalPoint ); 187 } 188 189 if ( formatted && strType ) { 190 d = d.replace( _re_formatted_numeric, '' ); 191 } 192 193 return !isNaN( parseFloat(d) ) && isFinite( d ); 194 }; 195 196 197 // A string without HTML in it can be considered to be HTML still 198 var _isHtml = function ( d ) { 199 return _empty( d ) || typeof d === 'string'; 200 }; 201 202 203 var _htmlNumeric = function ( d, decimalPoint, formatted ) { 204 if ( _empty( d ) ) { 205 return true; 206 } 207 208 var html = _isHtml( d ); 209 return ! html ? 210 null : 211 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? 212 true : 213 null; 214 }; 215 216 217 var _pluck = function ( a, prop, prop2 ) { 218 var out = []; 219 var i=0, ien=a.length; 220 221 // Could have the test in the loop for slightly smaller code, but speed 222 // is essential here 223 if ( prop2 !== undefined ) { 224 for ( ; i<ien ; i++ ) { 225 if ( a[i] && a[i][ prop ] ) { 226 out.push( a[i][ prop ][ prop2 ] ); 227 } 228 } 229 } 230 else { 231 for ( ; i<ien ; i++ ) { 232 if ( a[i] ) { 233 out.push( a[i][ prop ] ); 234 } 235 } 236 } 237 238 return out; 239 }; 240 241 242 // Basically the same as _pluck, but rather than looping over `a` we use `order` 243 // as the indexes to pick from `a` 244 var _pluck_order = function ( a, order, prop, prop2 ) 245 { 246 var out = []; 247 var i=0, ien=order.length; 248 249 // Could have the test in the loop for slightly smaller code, but speed 250 // is essential here 251 if ( prop2 !== undefined ) { 252 for ( ; i<ien ; i++ ) { 253 if ( a[ order[i] ][ prop ] ) { 254 out.push( a[ order[i] ][ prop ][ prop2 ] ); 255 } 256 } 257 } 258 else { 259 for ( ; i<ien ; i++ ) { 260 out.push( a[ order[i] ][ prop ] ); 261 } 262 } 263 264 return out; 265 }; 266 267 268 var _range = function ( len, start ) 269 { 270 var out = []; 271 var end; 272 273 if ( start === undefined ) { 274 start = 0; 275 end = len; 276 } 277 else { 278 end = start; 279 start = len; 280 } 281 282 for ( var i=start ; i<end ; i++ ) { 283 out.push( i ); 284 } 285 286 return out; 287 }; 288 289 290 var _removeEmpty = function ( a ) 291 { 292 var out = []; 293 294 for ( var i=0, ien=a.length ; i<ien ; i++ ) { 295 if ( a[i] ) { // careful - will remove all falsy values! 296 out.push( a[i] ); 297 } 298 } 299 300 return out; 301 }; 302 303 304 var _stripHtml = function ( d ) { 305 return d.replace( _re_html, '' ); 306 }; 307 308 309 /** 310 * Find the unique elements in a source array. 311 * 312 * @param {array} src Source array 313 * @return {array} Array of unique items 314 * @ignore 315 */ 316 var _unique = function ( src ) 317 {
318 // A faster unique method is to use object keys to identify used values, 319 // but this doesn't work with arrays or objects, which we must also 320 // consider. See jsperf.com/compare-array-unique-versions/4 for more 321 // information. 322 var 323 out = [], 324 val, 325 i, ien=src.length, 326 j, k=0; 327 328 again: for ( i=0 ; i<ien ; i++ ) { 329 val = src[i]; 330 331 for ( j=0 ; j<k ; j++ ) { 332 if ( out[j] === val ) { 333 continue again; 334 } 335 } 336 337 out.push( val ); 338 k++; 339 } 340 341 return out; 342 }; 343 344 345 346 /** 347 * Create a mapping object that allows camel case parameters to be looked up 348 * for their Hungarian counterparts. The mapping is stored in a private 349 * parameter called `_hungarianMap` which can be accessed on the source object. 350 * @param {object} o 351 * @memberof DataTable#oApi 352 */ 353 function _fnHungarianMap ( o ) 354 { 355 var 356 hungarian = 'a aa ai ao as b fn i m o s ', 357 match, 358 newKey, 359 map = {}; 360 361 $.each( o, function (key, val) { 362 match = key.match(/^([^A-Z]+?)([A-Z])/); 363 364 if ( match && hungarian.indexOf(match[1]+' ') !== -1 ) 365 { 366 newKey = key.replace( match[0], match[2].toLowerCase() ); 367 map[ newKey ] = key; 368 369 if ( match[1] === 'o' ) 370 { 371 _fnHungarianMap( o[key] ); 372 } 373 } 374 } ); 375 376 o._hungarianMap = map; 377 } 378 379 380 /** 381 * Convert from camel case parameters to Hungarian, based on a Hungarian map 382 * created by _fnHungarianMap. 383 * @param {object} src The model object which holds all parameters that can be 384 * mapped. 385 * @param {object} user The object to convert from camel case to Hungarian. 386 * @param {boolean} force When set to `true`, properties which already have a 387 * Hungarian value in the `user` object will be overwritten. Otherwise they 388 * won't be. 389 * @memberof DataTable#oApi 390 */ 391 function _fnCamelToHungarian ( src, user, force ) 392 { 393 if ( ! src._hungarianMap ) { 394 _fnHungarianMap( src ); 395 } 396 397 var hungarianKey; 398 399 $.each( user, function (key, val) { 400 hungarianKey = src._hungarianMap[ key ]; 401 402 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) ) 403 { 404 // For objects, we need to buzz down into the object to copy parameters 405 if ( hungarianKey.charAt(0) === 'o' ) 406 { 407 // Copy the camelCase options over to the hungarian 408 if ( ! user[ hungarianKey ] ) { 409 user[ hungarianKey ] = {}; 410 } 411 $.extend( true, user[hungarianKey], user[key] ); 412 413 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force ); 414 } 415 else { 416 user[hungarianKey] = user[ key ]; 417 } 418 } 419 } ); 420 } 421 422 423 /** 424 * Language compatibility - when certain options are given, and others aren't, we 425 * need to duplicate the values over, in order to provide backwards compatibility 426 * with older language files. 427 * @param {object} oSettings dataTables settings object 428 * @memberof DataTable#oApi 429 */ 430 function _fnLanguageCompat( lang ) 431 { 432 var defaults = DataTable.defaults.oLanguage; 433 var zeroRecords = lang.sZeroRecords; 434 435 /* Backwards compatibility - if there is no sEmptyTable given, then use the same as 436 * sZeroRecords - assuming that is given. 437 */ 438 if ( ! lang.sEmptyTable && zeroRecords && 439 defaults.sEmptyTable === "No data available in table" ) 440 { 441 _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' ); 442 } 443 444 /* Likewise with loading records */ 445 if ( ! lang.sLoadingRecords && zeroRecords && 446 defaults.sLoadingRecords === "Loading..." ) 447 { 448 _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' ); 449 } 450 451 // Old parameter name of the thousands separator mapped onto the new 452 if ( lang.sInfoThousands ) { 453 lang.sThousands = lang.sInfoThousands; 454 } 455 456 var decimal = lang.sDecimal; 457 if ( decimal ) { 458 _addNumericSort( decimal ); 459 } 460 } 461 462 463 /** 464 * Map one parameter onto another 465 * @param {object} o Object to map 466 * @param {*} knew The new parameter name 467 * @param {*} old The old parameter name 468 */ 469 var _fnCompatMap = function ( o, knew, old ) { 470 if ( o[ knew ] !== undefined ) { 471 o[ old ] = o[ knew ]; 472 } 473 }; 474 475 476 /** 477 * Provide backwards compatibility for the main DT options. Note that the new 478 * options are mapped onto the old parameters, so this is an external interface 479 * change only. 480 * @param {object} init Object to map 481 */ 482 function _fnCompatOpts ( init ) 483 { 484 _fnCompatMap( init, 'ordering', 'bSort' ); 485 _fnCompatMap( init, 'orderMulti', 'bSortMulti' ); 486 _fnCompatMap( init, 'orderClasses', 'bSortClasses' ); 487 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' ); 488 _fnCompatMap( init, 'order', 'aaSorting' ); 489 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' ); 490 _fnCompatMap( init, 'paging', 'bPaginate' ); 491 _fnCompatMap( init, 'pagingType', 'sPaginationType' ); 492 _fnCompatMap( init, 'pageLength', 'iDisplayLength' ); 493 _fnCompatMap( init, 'searching', 'bFilter' ); 494 495 // Boolean initialisation of x-scrolling 496 if ( typeof init.sScrollX === 'boolean' ) { 497 init.sScrollX = init.sScrollX ? '100%' : ''; 498 } 499 if ( typeof init.scrollX === 'boolean' ) { 500 init.scrollX = init.scrollX ? '100%' : ''; 501 } 502 503 // Column search objects are in an array, so it needs to be converted 504 // element by element 505 var searchCols = init.aoSearchCols; 506 507 if ( searchCols ) { 508 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) { 509 if ( searchCols[i] ) { 510 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] ); 511 } 512 } 513 } 514 } 515 516 517 /** 518 * Provide backwards compatibility for column options. Note that the new options 519 * are mapped onto the old parameters, so this is an external interface change 520 * only. 521 * @param {object} init Object to map 522 */ 523 function _fnCompatCols ( init ) 524 { 525 _fnCompatMap( init, 'orderable', 'bSortable' ); 526 _fnCompatMap( init, 'orderData', 'aDataSort' ); 527 _fnCompatMap( init, 'orderSequence', 'asSorting' ); 528 _fnCompatMap( init, 'orderDataType', 'sortDataType' ); 529 530 // orderData can be given as an integer 531 var dataSort = init.aDataSort; 532 if ( dataSort && ! $.isArray( dataSort ) ) { 533 init.aDataSort = [ dataSort ]; 534 } 535 } 536 537 538 /** 539 * Browser feature detection for capabilities, quirks 540 * @param {object} settings dataTables settings object 541 * @memberof DataTable#oApi 542 */ 543 function _fnBrowserDetect( settings ) 544 { 545 // We don't need to do this every time DataTables is constructed, the values 546 // calculated are specific to the browser and OS configuration which we 547 // don't expect to change between initialisations 548 if ( ! DataTable.__browser ) { 549 var browser = {}; 550 DataTable.__browser = browser; 551 552 // Scrolling feature / quirks detection 553 var n = $('<div/>') 554 .css( { 555 position: 'fixed', 556 top: 0, 557 left: 0, 558 height: 1, 559 width: 1, 560 overflow: 'hidden' 561 } ) 562 .append( 563 $('<div/>') 564 .css( { 565 position: 'absolute', 566 top: 1, 567 left: 1, 568 width: 100, 569 overflow: 'scroll' 570 } ) 571 .append( 572 $('<div/>') 573 .css( { 574 width: '100%', 575 height: 10 576 } ) 577 ) 578 ) 579 .appendTo( 'body' ); 580 581 var outer = n.children(); 582 var inner = outer.children(); 583 584 // Numbers below, in order, are: 585 // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth 586 // 587 // IE6 XP: 100 100 100 83 588 // IE7 Vista: 100 100 100 83 589 // IE 8+ Windows: 83 83 100 83 590 // Evergreen Windows: 83 83 100 83 591 // Evergreen Mac with scrollbars: 85 85 100 85 592 // Evergreen Mac without scrollbars: 100 100 100 100 593 594 // Get scrollbar width 595 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; 596 597 // IE6/7 will oversize a width 100% element inside a scrolling element, to 598 // include the width of the scrollbar, while other browsers ensure the inner 599 // element is contained without forcing scrolling 600 browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100; 601 602 // In rtl text layout, some browsers (most, but not all) will place the 603 // scrollbar on the left, rather than the right. 604 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; 605 606 // IE8- don't provide height and width for getBoundingClientRect 607 browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
608 609 n.remove(); 610 } 611 612 $.extend( settings.oBrowser, DataTable.__browser ); 613 settings.oScroll.iBarWidth = DataTable.__browser.barWidth; 614 } 615 616 617 /** 618 * Array.prototype reduce[Right] method, used for browsers which don't support 619 * JS 1.6. Done this way to reduce code size, since we iterate either way 620 * @param {object} settings dataTables settings object 621 * @memberof DataTable#oApi 622 */ 623 function _fnReduce ( that, fn, init, start, end, inc ) 624 { 625 var 626 i = start, 627 value, 628 isSet = false; 629 630 if ( init !== undefined ) { 631 value = init; 632 isSet = true; 633 } 634 635 while ( i !== end ) { 636 if ( ! that.hasOwnProperty(i) ) { 637 continue; 638 } 639 640 value = isSet ? 641 fn( value, that[i], i, that ) : 642 that[i]; 643 644 isSet = true; 645 i += inc; 646 } 647 648 return value; 649 } 650 651 /** 652 * Add a column to the list used for the table with default values 653 * @param {object} oSettings dataTables settings object 654 * @param {node} nTh The th element for this column 655 * @memberof DataTable#oApi 656 */ 657 function _fnAddColumn( oSettings, nTh ) 658 { 659 // Add column to aoColumns array 660 var oDefaults = DataTable.defaults.column; 661 var iCol = oSettings.aoColumns.length; 662 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { 663 "nTh": nTh ? nTh : document.createElement('th'), 664 "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', 665 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], 666 "mData": oDefaults.mData ? oDefaults.mData : iCol, 667 idx: iCol 668 } ); 669 oSettings.aoColumns.push( oCol ); 670 671 // Add search object for column specific search. Note that the `searchCols[ iCol ]` 672 // passed into extend can be undefined. This allows the user to give a default 673 // with only some of the parameters defined, and also not give a default 674 var searchCols = oSettings.aoPreSearchCols; 675 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); 676 677 // Use the default column options function to initialise classes etc 678 _fnColumnOptions( oSettings, iCol, $(nTh).data() ); 679 } 680 681 682 /** 683 * Apply options for a column 684 * @param {object} oSettings dataTables settings object 685 * @param {int} iCol column index to consider 686 * @param {object} oOptions object with sType, bVisible and bSearchable etc 687 * @memberof DataTable#oApi 688 */ 689 function _fnColumnOptions( oSettings, iCol, oOptions ) 690 { 691 var oCol = oSettings.aoColumns[ iCol ]; 692 var oClasses = oSettings.oClasses; 693 var th = $(oCol.nTh); 694 695 // Try to get width information from the DOM. We can't get it from CSS 696 // as we'd need to parse the CSS stylesheet. `width` option can override 697 if ( ! oCol.sWidthOrig ) { 698 // Width attribute 699 oCol.sWidthOrig = th.attr('width') || null; 700 701 // Style attribute 702 var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/); 703 if ( t ) { 704 oCol.sWidthOrig = t[1]; 705 } 706 } 707 708 /* User specified column options */ 709 if ( oOptions !== undefined && oOptions !== null ) 710 { 711 // Backwards compatibility 712 _fnCompatCols( oOptions ); 713 714 // Map camel case parameters to their Hungarian counterparts 715 _fnCamelToHungarian( DataTable.defaults.column, oOptions ); 716 717 /* Backwards compatibility for mDataProp */ 718 if ( oOptions.mDataProp !== undefined && !oOptions.mData ) 719 { 720 oOptions.mData = oOptions.mDataProp; 721 } 722 723 if ( oOptions.sType ) 724 { 725 oCol._sManualType = oOptions.sType; 726 } 727 728 // `class` is a reserved word in Javascript, so we need to provide 729 // the ability to use a valid name for the camel case input 730 if ( oOptions.className && ! oOptions.sClass ) 731 { 732 oOptions.sClass = oOptions.className; 733 } 734 735 $.extend( oCol, oOptions ); 736 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); 737 738 /* iDataSort to be applied (backwards compatibility), but aDataSort will take 739 * priority if defined 740 */ 741 if ( oOptions.iDataSort !== undefined ) 742 { 743 oCol.aDataSort = [ oOptions.iDataSort ]; 744 } 745 _fnMap( oCol, oOptions, "aDataSort" ); 746 } 747 748 /* Cache the data get and set functions for speed */ 749 var mDataSrc = oCol.mData; 750 var mData = _fnGetObjectDataFn( mDataSrc ); 751 var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; 752 753 var attrTest = function( src ) { 754 return typeof src === 'string' && src.indexOf('@') !== -1; 755 }; 756 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( 757 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) 758 ); 759 oCol._setter = null; 760 761 oCol.fnGetData = function (rowData, type, meta) { 762 var innerData = mData( rowData, type, undefined, meta ); 763
764 return mRender && type ? 765 mRender( innerData, type, rowData, meta ) : 766 innerData; 767 }; 768 oCol.fnSetData = function ( rowData, val, meta ) { 769 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); 770 }; 771 772 // Indicate if DataTables should read DOM data as an object or array 773 // Used in _fnGetRowElements 774 if ( typeof mDataSrc !== 'number' ) { 775 oSettings._rowReadObject = true; 776 } 777 778 /* Feature sorting overrides column specific when off */ 779 if ( !oSettings.oFeatures.bSort ) 780 { 781 oCol.bSortable = false; 782 th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called 783 } 784 785 /* Check that the class assignment is correct for sorting */ 786 var bAsc = $.inArray('asc', oCol.asSorting) !== -1; 787 var bDesc = $.inArray('desc', oCol.asSorting) !== -1; 788 if ( !oCol.bSortable || (!bAsc && !bDesc) ) 789 { 790 oCol.sSortingClass = oClasses.sSortableNone; 791 oCol.sSortingClassJUI = ""; 792 } 793 else if ( bAsc && !bDesc ) 794 { 795 oCol.sSortingClass = oClasses.sSortableAsc; 796 oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed; 797 } 798 else if ( !bAsc && bDesc ) 799 { 800 oCol.sSortingClass = oClasses.sSortableDesc; 801 oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed; 802 } 803 else 804 { 805 oCol.sSortingClass = oClasses.sSortable; 806 oCol.sSortingClassJUI = oClasses.sSortJUI; 807 } 808 } 809 810 811 /** 812 * Adjust the table column widths for new data. Note: you would probably want to 813 * do a redraw after calling this function! 814 * @param {object} settings dataTables settings object 815 * @memberof DataTable#oApi 816 */ 817 function _fnAdjustColumnSizing ( settings ) 818 { 819 /* Not interested in doing column width calculation if auto-width is disabled */ 820 if ( settings.oFeatures.bAutoWidth !== false ) 821 { 822 var columns = settings.aoColumns; 823 824 _fnCalculateColumnWidths( settings ); 825 for ( var i=0 , iLen=columns.length ; i<iLen ; i++ ) 826 { 827 columns[i].nTh.style.width = columns[i].sWidth; 828 } 829 } 830 831 var scroll = settings.oScroll; 832 if ( scroll.sY !== '' || scroll.sX !== '') 833 { 834 _fnScrollDraw( settings ); 835 } 836 837 _fnCallbackFire( settings, null, 'column-sizing', [settings] ); 838 } 839 840 841 /** 842 * Covert the index of a visible column to the index in the data array (take account 843 * of hidden columns) 844 * @param {object} oSettings dataTables settings object 845 * @param {int} iMatch Visible column index to lookup 846 * @returns {int} i the data index 847 * @memberof DataTable#oApi 848 */ 849 function _fnVisibleToColumnIndex( oSettings, iMatch ) 850 { 851 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 852 853 return typeof aiVis[iMatch] === 'number' ? 854 aiVis[iMatch] : 855 null; 856 } 857 858 859 /** 860 * Covert the index of an index in the data array and convert it to the visible 861 * column index (take account of hidden columns) 862 * @param {int} iMatch Column index to lookup 863 * @param {object} oSettings dataTables settings object 864 * @returns {int} i the data index 865 * @memberof DataTable#oApi 866 */ 867 function _fnColumnIndexToVisible( oSettings, iMatch ) 868 { 869 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 870 var iPos = $.inArray( iMatch, aiVis ); 871 872 return iPos !== -1 ? iPos : null; 873 } 874 875 876 /** 877 * Get the number of visible columns 878 * @param {object} oSettings dataTables settings object 879 * @returns {int} i the number of visible columns 880 * @memberof DataTable#oApi 881 */ 882 function _fnVisbleColumns( oSettings ) 883 { 884 return $( _pluck( oSettings.aoColumns, 'nTh' ) ).filter(':visible').length; 885 } 886 887 888 /** 889 * Get an array of column indexes that match a given property 890 * @param {object} oSettings dataTables settings object 891 * @param {string} sParam Parameter in aoColumns to look for - typically 892 * bVisible or bSearchable 893 * @returns {array} Array of indexes with matched properties 894 * @memberof DataTable#oApi 895 */ 896 function _fnGetColumns( oSettings, sParam ) 897 { 898 var a = []; 899 900 $.map( oSettings.aoColumns, function(val, i) { 901 if ( val[sParam] ) { 902 a.push( i ); 903 } 904 } ); 905 906 return a; 907 } 908 909 910 /** 911 * Calculate the 'type' of a column 912 * @param {object} settings dataTables settings object 913 * @memberof DataTable#oApi 914 */ 915 function _fnColumnTypes ( settings ) 916 { 917 var columns = settings.aoColumns; 918 var data = settings.aoData; 919 var types = DataTable.ext.type.detect; 920 var i, ien, j, jen, k, ken; 921 var col, cell, detectedType, cache; 922 923 // For each column, spin over the 924 for ( i=0, ien=columns.length ; i<ien ; i++ ) { 925 col = columns[i]; 926 cache = []; 927 928 if ( ! col.sType && col._sManualType ) {
929 col.sType = col._sManualType; 930 } 931 else if ( ! col.sType ) { 932 for ( j=0, jen=types.length ; j<jen ; j++ ) { 933 for ( k=0, ken=data.length ; k<ken ; k++ ) { 934 // Use a cache array so we only need to get the type data 935 // from the formatter once (when using multiple detectors) 936 if ( cache[k] === undefined ) { 937 cache[k] = _fnGetCellData( settings, k, i, 'type' ); 938 } 939 940 detectedType = types[j]( cache[k], settings ); 941 942 // If null, then this type can't apply to this column, so 943 // rather than testing all cells, break out. There is an 944 // exception for the last type which is `html`. We need to 945 // scan all rows since it is possible to mix string and HTML 946 // types 947 if ( ! detectedType && j !== types.length-1 ) { 948 break; 949 } 950 951 // Only a single match is needed for html type since it is 952 // bottom of the pile and very similar to string 953 if ( detectedType === 'html' ) { 954 break; 955 } 956 } 957 958 // Type is valid for all data points in the column - use this 959 // type 960 if ( detectedType ) { 961 col.sType = detectedType; 962 break; 963 } 964 } 965 966 // Fall back - if no type was detected, always use string 967 if ( ! col.sType ) { 968 col.sType = 'string'; 969 } 970 } 971 } 972 } 973 974 975 /** 976 * Take the column definitions and static columns arrays and calculate how 977 * they relate to column indexes. The callback function will then apply the 978 * definition found for a column to a suitable configuration object. 979 * @param {object} oSettings dataTables settings object 980 * @param {array} aoColDefs The aoColumnDefs array that is to be applied 981 * @param {array} aoCols The aoColumns array that defines columns individually 982 * @param {function} fn Callback function - takes two parameters, the calculated 983 * column index and the definition for that column. 984 * @memberof DataTable#oApi 985 */ 986 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn ) 987 { 988 var i, iLen, j, jLen, k, kLen, def; 989 var columns = oSettings.aoColumns; 990 991 // Column definitions with aTargets 992 if ( aoColDefs ) 993 { 994 /* Loop over the definitions array - loop in reverse so first instance has priority */ 995 for ( i=aoColDefs.length-1 ; i>=0 ; i-- ) 996 { 997 def = aoColDefs[i]; 998 999 /* Each definition can target multiple columns, as it is an array */ 1000 var aTargets = def.targets !== undefined ? 1001 def.targets : 1002 def.aTargets; 1003 1004 if ( ! $.isArray( aTargets ) ) 1005 { 1006 aTargets = [ aTargets ]; 1007 } 1008 1009 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ ) 1010 { 1011 if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 ) 1012 { 1013 /* Add columns that we don't yet know about */ 1014 while( columns.length <= aTargets[j] ) 1015 { 1016 _fnAddColumn( oSettings ); 1017 } 1018 1019 /* Integer, basic index */ 1020 fn( aTargets[j], def ); 1021 } 1022 else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 ) 1023 { 1024 /* Negative integer, right to left column counting */ 1025 fn( columns.length+aTargets[j], def ); 1026 } 1027 else if ( typeof aTargets[j] === 'string' ) 1028 { 1029 /* Class name matching on TH element */ 1030 for ( k=0, kLen=columns.length ; k<kLen ; k++ ) 1031 { 1032 if ( aTargets[j] == "_all" || 1033 $(columns[k].nTh).hasClass( aTargets[j] ) ) 1034 { 1035 fn( k, def ); 1036 } 1037 } 1038 } 1039 } 1040 } 1041 } 1042 1043 // Statically defined columns array 1044 if ( aoCols ) 1045 { 1046 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) 1047 { 1048 fn( i, aoCols[i] ); 1049 } 1050 } 1051 } 1052 1053 /** 1054 * Add a data array to the table, creating DOM node etc. This is the parallel to 1055 * _fnGatherData, but for adding rows from a Javascript source, rather than a 1056 * DOM source. 1057 * @param {object} oSettings dataTables settings object 1058 * @param {array} aData data array to be added 1059 * @param {node} [nTr] TR element to add to the table - optional. If not given, 1060 * DataTables will create a row automatically 1061 * @param {array} [anTds] Array of TD|TH elements for the row - must be given 1062 * if nTr is. 1063 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed 1064 * @memberof DataTable#oApi 1065 */ 1066 function _fnAddData ( oSettings, aDataIn, nTr, anTds ) 1067 { 1068 /* Create the object for storing information about this new row */ 1069 var iRow = oSettings.aoData.length; 1070 var oData = $.extend( true, {}, DataTable.models.oRow, { 1071 src: nTr ? 'dom' : 'data', 1072 idx: iRow 1073 } ); 1074 1075 oData._aData = aDataIn; 1076 oSettings.aoData.push( oData ); 1077 1078 /* Create the cells */ 1079 var nTd, sThisType; 1080 var columns = oSettings.aoColumns; 1081 1082 // Invalidate the column types as the new data needs to be revalidated 1083 for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) 1084 {
1085 columns[i].sType = null; 1086 } 1087 1088 /* Add to the display array */ 1089 oSettings.aiDisplayMaster.push( iRow ); 1090 1091 var id = oSettings.rowIdFn( aDataIn ); 1092 if ( id !== undefined ) { 1093 oSettings.aIds[ id ] = oData; 1094 } 1095 1096 /* Create the DOM information, or register it if already present */ 1097 if ( nTr || ! oSettings.oFeatures.bDeferRender ) 1098 { 1099 _fnCreateTr( oSettings, iRow, nTr, anTds ); 1100 } 1101 1102 return iRow; 1103 } 1104 1105 1106 /** 1107 * Add one or more TR elements to the table. Generally we'd expect to 1108 * use this for reading data from a DOM sourced table, but it could be 1109 * used for an TR element. Note that if a TR is given, it is used (i.e. 1110 * it is not cloned). 1111 * @param {object} settings dataTables settings object 1112 * @param {array|node|jQuery} trs The TR element(s) to add to the table 1113 * @returns {array} Array of indexes for the added rows 1114 * @memberof DataTable#oApi 1115 */ 1116 function _fnAddTr( settings, trs ) 1117 { 1118 var row; 1119 1120 // Allow an individual node to be passed in 1121 if ( ! (trs instanceof $) ) { 1122 trs = $(trs); 1123 } 1124 1125 return trs.map( function (i, el) { 1126 row = _fnGetRowElements( settings, el ); 1127 return _fnAddData( settings, row.data, el, row.cells ); 1128 } ); 1129 } 1130 1131 1132 /** 1133 * Take a TR element and convert it to an index in aoData 1134 * @param {object} oSettings dataTables settings object 1135 * @param {node} n the TR element to find 1136 * @returns {int} index if the node is found, null if not 1137 * @memberof DataTable#oApi 1138 */ 1139 function _fnNodeToDataIndex( oSettings, n ) 1140 { 1141 return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null; 1142 } 1143 1144 1145 /** 1146 * Take a TD element and convert it into a column data index (not the visible index) 1147 * @param {object} oSettings dataTables settings object 1148 * @param {int} iRow The row number the TD/TH can be found in 1149 * @param {node} n The TD/TH element to find 1150 * @returns {int} index if the node is found, -1 if not 1151 * @memberof DataTable#oApi 1152 */ 1153 function _fnNodeToColumnIndex( oSettings, iRow, n ) 1154 { 1155 return $.inArray( n, oSettings.aoData[ iRow ].anCells ); 1156 } 1157 1158 1159 /** 1160 * Get the data for a given cell from the internal cache, taking into account data mapping 1161 * @param {object} settings dataTables settings object 1162 * @param {int} rowIdx aoData row id 1163 * @param {int} colIdx Column index 1164 * @param {string} type data get type ('display', 'type' 'filter' 'sort') 1165 * @returns {*} Cell data 1166 * @memberof DataTable#oApi 1167 */ 1168 function _fnGetCellData( settings, rowIdx, colIdx, type ) 1169 { 1170 var draw = settings.iDraw; 1171 var col = settings.aoColumns[colIdx]; 1172 var rowData = settings.aoData[rowIdx]._aData; 1173 var defaultContent = col.sDefaultContent; 1174 var cellData = col.fnGetData( rowData, type, { 1175 settings: settings, 1176 row: rowIdx, 1177 col: colIdx 1178 } ); 1179 1180 if ( cellData === undefined ) { 1181 if ( settings.iDrawError != draw && defaultContent === null ) { 1182 _fnLog( settings, 0, "Requested unknown parameter "+ 1183 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+ 1184 " for row "+rowIdx+", column "+colIdx, 4 ); 1185 settings.iDrawError = draw; 1186 } 1187 return defaultContent; 1188 } 1189 1190 // When the data source is null and a specific data type is requested (i.e. 1191 // not the original data), we can use default column data 1192 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) { 1193 cellData = defaultContent; 1194 } 1195 else if ( typeof cellData === 'function' ) { 1196 // If the data source is a function, then we run it and use the return, 1197 // executing in the scope of the data object (for instances) 1198 return cellData.call( rowData ); 1199 } 1200 1201 if ( cellData === null && type == 'display' ) { 1202 return ''; 1203 } 1204 return cellData; 1205 } 1206 1207 1208 /** 1209 * Set the value for a specific cell, into the internal data cache 1210 * @param {object} settings dataTables settings object 1211 * @param {int} rowIdx aoData row id 1212 * @param {int} colIdx Column index 1213 * @param {*} val Value to set 1214 * @memberof DataTable#oApi 1215 */ 1216 function _fnSetCellData( settings, rowIdx, colIdx, val ) 1217 { 1218 var col = settings.aoColumns[colIdx]; 1219 var rowData = settings.aoData[rowIdx]._aData; 1220 1221 col.fnSetData( rowData, val, { 1222 settings: settings, 1223 row: rowIdx, 1224 col: colIdx 1225 } ); 1226 } 1227 1228 1229 // Private variable that is used to match action syntax in the data property object 1230 var __reArray = /\[.*?\]$/; 1231 var __reFn = /\(\)$/; 1232 1233 /** 1234 * Split string on periods, taking into account escaped periods 1235 * @param {string} str String to split 1236 * @return {array} Split string 1237 */ 1238 function _fnSplitObjNotation( str ) 1239 { 1240 return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) { 1241 return s.replace(/\\./g, '.'); 1242 } ); 1243 } 1244 1245 1246 /** 1247 * Return a function that can be used to get data from a source object, taking 1248 * into account the ability to use nested objects as a source 1249 * @param {string|int|function} mSource The data source for the object 1250 * @returns {function} Data get function 1251 * @memberof DataTable#oApi 1252 */ 1253 function _fnGetObjectDataFn( mSource ) 1254 { 1255 if ( $.isPlainObject( mSource ) ) 1256 { 1257 /* Build an object of get functions, and wrap them in a single call */ 1258 var o = {}; 1259 $.each( mSource, function (key, val) { 1260 if ( val ) { 1261 o[key] = _fnGetObjectDataFn( val ); 1262 } 1263 } ); 1264 1265 return function (data, type, row, meta) { 1266 var t = o[type] || o._; 1267 return t !== undefined ? 1268 t(data, type, row, meta) : 1269 data; 1270 }; 1271 } 1272 else if ( mSource === null ) 1273 { 1274 /* Give an empty string for rendering / sorting etc */ 1275 return function (data) { // type, row and meta also passed, but not used 1276 return data; 1277 }; 1278 } 1279 else if ( typeof mSource === 'function' ) 1280 { 1281 return function (data, type, row, meta) { 1282 return mSource( data, type, row, meta ); 1283 }; 1284 } 1285 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || 1286 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) ) 1287 { 1288 /* If there is a . in the source string then the data source is in a 1289 * nested object so we loop over the data for each level to get the next 1290 * level down. On each loop we test for undefined, and if found immediately 1291 * return. This allows entire objects to be missing and sDefaultContent to 1292 * be used if defined, rather than throwing an error 1293 */
1294 var fetchData = function (data, type, src) { 1295 var arrayNotation, funcNotation, out, innerSrc; 1296 1297 if ( src !== "" ) 1298 { 1299 var a = _fnSplitObjNotation( src ); 1300 1301 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) 1302 { 1303 // Check if we are dealing with special notation 1304 arrayNotation = a[i].match(__reArray); 1305 funcNotation = a[i].match(__reFn); 1306 1307 if ( arrayNotation ) 1308 { 1309 // Array notation 1310 a[i] = a[i].replace(__reArray, ''); 1311 1312 // Condition allows simply [] to be passed in 1313 if ( a[i] !== "" ) { 1314 data = data[ a[i] ]; 1315 } 1316 out = []; 1317 1318 // Get the remainder of the nested object to get 1319 a.splice( 0, i+1 ); 1320 innerSrc = a.join('.'); 1321 1322 // Traverse each entry in the array getting the properties requested 1323 if ( $.isArray( data ) ) { 1324 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) { 1325 out.push( fetchData( data[j], type, innerSrc ) ); 1326 } 1327 } 1328 1329 // If a string is given in between the array notation indicators, that 1330 // is used to join the strings together, otherwise an array is returned 1331 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1); 1332 data = (join==="") ? out : out.join(join); 1333 1334 // The inner call to fetchData has already traversed through the remainder 1335 // of the source requested, so we exit from the loop 1336 break; 1337 } 1338 else if ( funcNotation ) 1339 { 1340 // Function call 1341 a[i] = a[i].replace(__reFn, ''); 1342 data = data[ a[i] ](); 1343 continue; 1344 } 1345 1346 if ( data === null || data[ a[i] ] === undefined ) 1347 { 1348 return undefined; 1349 } 1350 data = data[ a[i] ]; 1351 } 1352 } 1353 1354 return data; 1355 }; 1356 1357 return function (data, type) { // row and meta also passed, but not used 1358 return fetchData( data, type, mSource ); 1359 }; 1360 } 1361 else 1362 { 1363 /* Array or flat object mapping */ 1364 return function (data, type) { // row and meta also passed, but not used 1365 return data[mSource]; 1366 }; 1367 } 1368 } 1369 1370 1371 /** 1372 * Return a function that can be used to set data from a source object, taking 1373 * into account the ability to use nested objects as a source 1374 * @param {string|int|function} mSource The data source for the object 1375 * @returns {function} Data set function 1376 * @memberof DataTable#oApi 1377 */ 1378 function _fnSetObjectDataFn( mSource ) 1379 { 1380 if ( $.isPlainObject( mSource ) ) 1381 { 1382 /* Unlike get, only the underscore (global) option is used for for 1383 * setting data since we don't know the type here. This is why an object 1384 * option is not documented for `mData` (which is read/write), but it is 1385 * for `mRender` which is read only. 1386 */ 1387 return _fnSetObjectDataFn( mSource._ ); 1388 } 1389 else if ( mSource === null ) 1390 { 1391 /* Nothing to do when the data source is null */ 1392 return function () {}; 1393 } 1394 else if ( typeof mSource === 'function' ) 1395 { 1396 return function (data, val, meta) { 1397 mSource( data, 'set', val, meta ); 1398 }; 1399 } 1400 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || 1401 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) ) 1402 { 1403 /* Like the get, we need to get data from a nested object */ 1404 var setData = function (data, val, src) { 1405 var a = _fnSplitObjNotation( src ), b; 1406 var aLast = a[a.length-1]; 1407 var arrayNotation, funcNotation, o, innerSrc; 1408 1409 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) 1410 { 1411 // Check if we are dealing with an array notation request 1412 arrayNotation = a[i].match(__reArray); 1413 funcNotation = a[i].match(__reFn); 1414 1415 if ( arrayNotation ) 1416 { 1417 a[i] = a[i].replace(__reArray, ''); 1418 data[ a[i] ] = []; 1419 1420 // Get the remainder of the nested object to set so we can recurse 1421 b = a.slice(); 1422 b.splice( 0, i+1 ); 1423 innerSrc = b.join('.'); 1424 1425 // Traverse each entry in the array setting the properties requested 1426 if ( $.isArray( val ) ) 1427 { 1428 for ( var j=0, jLen=val.length ; j<jLen ; j++ ) 1429 { 1430 o = {}; 1431 setData( o, val[j], innerSrc ); 1432 data[ a[i] ].push( o ); 1433 } 1434 } 1435 else 1436 { 1437 // We've been asked to save data to an array, but it 1438 // isn't array data to be saved. Best that can be done 1439 // is to just save the value. 1440 data[ a[i] ] = val; 1441 } 1442 1443 // The inner call to setData has already traversed through the remainder 1444 // of the source and has set the data, thus we can exit here 1445 return; 1446 } 1447 else if ( funcNotation ) 1448 { 1449 // Function call 1450 a[i] = a[i].replace(__reFn, ''); 1451 data = data[ a[i] ]( val ); 1452 } 1453 1454 // If the nested object doesn't currently exist - since we are 1455 // trying to set the value - create it 1456 if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) 1457 { 1458 data[ a[i] ] = {}; 1459 } 1460 data = data[ a[i] ]; 1461 } 1462 1463 // Last item in the input - i.e, the actual set 1464 if ( aLast.match(__reFn ) ) 1465 { 1466 // Function call 1467 data = data[ aLast.replace(__reFn, '') ]( val ); 1468 } 1469 else 1470 { 1471 // If array notation is used, we just want to strip it and use the property name 1472 // and assign the value. If it isn't used, then we get the result we want anyway 1473 data[ aLast.replace(__reArray, '') ] = val; 1474 } 1475 }; 1476 1477 return function (data, val) { // meta is also passed in, but not used 1478 return setData( data, val, mSource ); 1479 }; 1480 } 1481 else 1482 { 1483 /* Array or flat object mapping */ 1484 return function (data, val) { // meta is also passed in, but not used 1485 data[mSource] = val; 1486 }; 1487 } 1488 } 1489 1490 1491 /** 1492 * Return an array with the full table data 1493 * @param {object} oSettings dataTables settings object 1494 * @returns array {array} aData Master data array 1495 * @memberof DataTable#oApi 1496 */ 1497 function _fnGetDataMaster ( settings ) 1498 { 1499 return _pluck( settings.aoData, '_aData' ); 1500 } 1501 1502 1503 /** 1504 * Nuke the table 1505 * @param {object} oSettings dataTables settings object 1506 * @memberof DataTable#oApi 1507 */ 1508 function _fnClearTable( settings ) 1509 { 1510 settings.aoData.length = 0; 1511 settings.aiDisplayMaster.length = 0; 1512 settings.aiDisplay.length = 0; 1513 settings.aIds = {}; 1514 } 1515 1516 1517 /** 1518 * Take an array of integers (index array) and remove a target integer (value - not 1519 * the key!) 1520 * @param {array} a Index array to target 1521 * @param {int} iTarget value to find 1522 * @memberof DataTable#oApi 1523 */ 1524 function _fnDeleteIndex( a, iTarget, splice ) 1525 { 1526 var iTargetIndex = -1; 1527 1528 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) 1529 { 1530 if ( a[i] == iTarget ) 1531 { 1532 iTargetIndex = i; 1533 } 1534 else if ( a[i] > iTarget ) 1535 { 1536 a[i]--; 1537 } 1538 } 1539 1540 if ( iTargetIndex != -1 && splice === undefined ) 1541 { 1542 a.splice( iTargetIndex, 1 ); 1543 } 1544 } 1545 1546 1547 /**
1548 * Mark cached data as invalid such that a re-read of the data will occur when 1549 * the cached data is next requested. Also update from the data source object. 1550 * 1551 * @param {object} settings DataTables settings object 1552 * @param {int} rowIdx Row index to invalidate 1553 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom' 1554 * or 'data' 1555 * @param {int} [colIdx] Column index to invalidate. If undefined the whole 1556 * row will be invalidated 1557 * @memberof DataTable#oApi 1558 * 1559 * @todo For the modularisation of v1.11 this will need to become a callback, so 1560 * the sort and filter methods can subscribe to it. That will required 1561 * initialisation options for sorting, which is why it is not already baked in 1562 */ 1563 function _fnInvalidate( settings, rowIdx, src, colIdx ) 1564 { 1565 var row = settings.aoData[ rowIdx ]; 1566 var i, ien; 1567 var cellWrite = function ( cell, col ) { 1568 // This is very frustrating, but in IE if you just write directly 1569 // to innerHTML, and elements that are overwritten are GC'ed, 1570 // even if there is a reference to them elsewhere 1571 while ( cell.childNodes.length ) { 1572 cell.removeChild( cell.firstChild ); 1573 } 1574 1575 cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' ); 1576 }; 1577 1578 // Are we reading last data from DOM or the data object? 1579 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) { 1580 // Read the data from the DOM 1581 row._aData = _fnGetRowElements( 1582 settings, row, colIdx, colIdx === undefined ? undefined : row._aData 1583 ) 1584 .data; 1585 } 1586 else { 1587 // Reading from data object, update the DOM 1588 var cells = row.anCells; 1589 1590 if ( cells ) { 1591 if ( colIdx !== undefined ) { 1592 cellWrite( cells[colIdx], colIdx ); 1593 } 1594 else { 1595 for ( i=0, ien=cells.length ; i<ien ; i++ ) { 1596 cellWrite( cells[i], i ); 1597 } 1598 } 1599 } 1600 } 1601 1602 // For both row and cell invalidation, the cached data for sorting and 1603 // filtering is nulled out 1604 row._aSortData = null; 1605 row._aFilterData = null; 1606 1607 // Invalidate the type for a specific column (if given) or all columns since 1608 // the data might have changed 1609 var cols = settings.aoColumns; 1610 if ( colIdx !== undefined ) { 1611 cols[ colIdx ].sType = null; 1612 } 1613 else { 1614 for ( i=0, ien=cols.length ; i<ien ; i++ ) { 1615 cols[i].sType = null; 1616 } 1617 1618 // Update DataTables special `DT_*` attributes for the row 1619 _fnRowAttributes( settings, row ); 1620 } 1621 } 1622 1623 1624 /** 1625 * Build a data source object from an HTML row, reading the contents of the 1626 * cells that are in the row. 1627 * 1628 * @param {object} settings DataTables settings object 1629 * @param {node|object} TR element from which to read data or existing row 1630 * object from which to re-read the data from the cells 1631 * @param {int} [colIdx] Optional column index 1632 * @param {array|object} [d] Data source object. If `colIdx` is given then this 1633 * parameter should also be given and will be used to write the data into. 1634 * Only the column in question will be written 1635 * @returns {object} Object with two parameters: `data` the data read, in 1636 * document order, and `cells` and array of nodes (they can be useful to the 1637 * caller, so rather than needing a second traversal to get them, just return 1638 * them from here). 1639 * @memberof DataTable#oApi 1640 */ 1641 function _fnGetRowElements( settings, row, colIdx, d ) 1642 { 1643 var 1644 tds = [], 1645 td = row.firstChild, 1646 name, col, o, i=0, contents, 1647 columns = settings.aoColumns, 1648 objectRead = settings._rowReadObject; 1649 1650 // Allow the data object to be passed in, or construct 1651 d = d !== undefined ? 1652 d : 1653 objectRead ? 1654 {} : 1655 []; 1656 1657 var attr = function ( str, td ) { 1658 if ( typeof str === 'string' ) { 1659 var idx = str.indexOf('@'); 1660 1661 if ( idx !== -1 ) { 1662 var attr = str.substring( idx+1 ); 1663 var setter = _fnSetObjectDataFn( str ); 1664 setter( d, td.getAttribute( attr ) ); 1665 } 1666 } 1667 }; 1668 1669 // Read data from a cell and store into the data object 1670 var cellProcess = function ( cell ) { 1671 if ( colIdx === undefined || colIdx === i ) { 1672 col = columns[i]; 1673 contents = $.trim(cell.innerHTML); 1674 1675 if ( col && col._bAttrSrc ) { 1676 var setter = _fnSetObjectDataFn( col.mData._ ); 1677 setter( d, contents ); 1678 1679 attr( col.mData.sort, cell ); 1680 attr( col.mData.type, cell ); 1681 attr( col.mData.filter, cell ); 1682 } 1683 else { 1684 // Depending on the `data` option for the columns the data can 1685 // be read to either an object or an array.
1686 if ( objectRead ) { 1687 if ( ! col._setter ) { 1688 // Cache the setter function 1689 col._setter = _fnSetObjectDataFn( col.mData ); 1690 } 1691 col._setter( d, contents ); 1692 } 1693 else { 1694 d[i] = contents; 1695 } 1696 } 1697 } 1698 1699 i++; 1700 }; 1701 1702 if ( td ) { 1703 // `tr` element was passed in 1704 while ( td ) { 1705 name = td.nodeName.toUpperCase(); 1706 1707 if ( name == "TD" || name == "TH" ) { 1708 cellProcess( td ); 1709 tds.push( td ); 1710 } 1711 1712 td = td.nextSibling; 1713 } 1714 } 1715 else { 1716 // Existing row object passed in 1717 tds = row.anCells; 1718 1719 for ( var j=0, jen=tds.length ; j<jen ; j++ ) { 1720 cellProcess( tds[j] ); 1721 } 1722 } 1723 1724 // Read the ID from the DOM if present 1725 var rowNode = row.firstChild ? row : row.nTr; 1726 1727 if ( rowNode ) { 1728 var id = rowNode.getAttribute( 'id' ); 1729 1730 if ( id ) { 1731 _fnSetObjectDataFn( settings.rowId )( d, id ); 1732 } 1733 } 1734 1735 return { 1736 data: d, 1737 cells: tds 1738 }; 1739 } 1740 /** 1741 * Create a new TR element (and it's TD children) for a row 1742 * @param {object} oSettings dataTables settings object 1743 * @param {int} iRow Row to consider 1744 * @param {node} [nTrIn] TR element to add to the table - optional. If not given, 1745 * DataTables will create a row automatically 1746 * @param {array} [anTds] Array of TD|TH elements for the row - must be given 1747 * if nTr is. 1748 * @memberof DataTable#oApi 1749 */ 1750 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds ) 1751 { 1752 var 1753 row = oSettings.aoData[iRow], 1754 rowData = row._aData, 1755 cells = [], 1756 nTr, nTd, oCol, 1757 i, iLen; 1758 1759 if ( row.nTr === null ) 1760 { 1761 nTr = nTrIn || document.createElement('tr'); 1762 1763 row.nTr = nTr; 1764 row.anCells = cells; 1765 1766 /* Use a private property on the node to allow reserve mapping from the node 1767 * to the aoData array for fast look up 1768 */ 1769 nTr._DT_RowIndex = iRow; 1770 1771 /* Special parameters can be given by the data source to be used on the row */ 1772 _fnRowAttributes( oSettings, row ); 1773 1774 /* Process each column */ 1775 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 1776 { 1777 oCol = oSettings.aoColumns[i]; 1778 1779 nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType ); 1780 nTd._DT_CellIndex = { 1781 row: iRow, 1782 column: i 1783 }; 1784 1785 cells.push( nTd ); 1786 1787 // Need to create the HTML if new, or if a rendering function is defined 1788 if ( (!nTrIn || oCol.mRender || oCol.mData !== i) && 1789 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display') 1790 ) { 1791 nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' ); 1792 } 1793 1794 /* Add user defined class */ 1795 if ( oCol.sClass ) 1796 { 1797 nTd.className += ' '+oCol.sClass; 1798 } 1799 1800 // Visibility - add or remove as required 1801 if ( oCol.bVisible && ! nTrIn ) 1802 { 1803 nTr.appendChild( nTd ); 1804 } 1805 else if ( ! oCol.bVisible && nTrIn ) 1806 { 1807 nTd.parentNode.removeChild( nTd ); 1808 } 1809 1810 if ( oCol.fnCreatedCell ) 1811 { 1812 oCol.fnCreatedCell.call( oSettings.oInstance, 1813 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i 1814 ); 1815 } 1816 } 1817 1818 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] ); 1819 } 1820 1821 // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved 1822 // and deployed 1823 row.nTr.setAttribute( 'role', 'row' ); 1824 } 1825 1826 1827 /** 1828 * Add attributes to a row based on the special `DT_*` parameters in a data 1829 * source object. 1830 * @param {object} settings DataTables settings object 1831 * @param {object} DataTables row object for the row to be modified 1832 * @memberof DataTable#oApi 1833 */ 1834 function _fnRowAttributes( settings, row ) 1835 { 1836 var tr = row.nTr; 1837 var data = row._aData; 1838 1839 if ( tr ) { 1840 var id = settings.rowIdFn( data ); 1841 1842 if ( id ) { 1843 tr.id = id; 1844 } 1845 1846 if ( data.DT_RowClass ) { 1847 // Remove any classes added by DT_RowClass before 1848 var a = data.DT_RowClass.split(' '); 1849 row.__rowc = row.__rowc ? 1850 _unique( row.__rowc.concat( a ) ) : 1851 a; 1852 1853 $(tr) 1854 .removeClass( row.__rowc.join(' ') ) 1855 .addClass( data.DT_RowClass ); 1856 } 1857 1858 if ( data.DT_RowAttr ) { 1859 $(tr).attr( data.DT_RowAttr ); 1860 } 1861 1862 if ( data.DT_RowData ) { 1863 $(tr).data( data.DT_RowData ); 1864 } 1865 } 1866 } 1867 1868 1869 /** 1870 * Create the HTML header for the table 1871 * @param {object} oSettings dataTables settings object 1872 * @memberof DataTable#oApi 1873 */ 1874 function _fnBuildHead( oSettings ) 1875 { 1876 var i, ien, cell, row, column; 1877 var thead = oSettings.nTHead; 1878 var tfoot = oSettings.nTFoot; 1879 var createHeader = $('th, td', thead).length === 0; 1880 var classes = oSettings.oClasses; 1881 var columns = oSettings.aoColumns; 1882 1883 if ( createHeader ) { 1884 row = $('<tr/>').appendTo( thead ); 1885 } 1886 1887 for ( i=0, ien=columns.length ; i<ien ; i++ ) { 1888 column = columns[i]; 1889 cell = $( column.nTh ).addClass( column.sClass ); 1890 1891 if ( createHeader ) { 1892 cell.appendTo( row ); 1893 } 1894 1895 // 1.11 move into sorting 1896 if ( oSettings.oFeatures.bSort ) { 1897 cell.addClass( column.sSortingClass ); 1898 1899 if ( column.bSortable !== false ) { 1900 cell 1901 .attr( 'tabindex', oSettings.iTabIndex ) 1902 .attr( 'aria-controls', oSettings.sTableId ); 1903 1904 _fnSortAttachListener( oSettings, column.nTh, i ); 1905 } 1906 } 1907 1908 if ( column.sTitle != cell[0].innerHTML ) { 1909 cell.html( column.sTitle ); 1910 } 1911 1912 _fnRenderer( oSettings, 'header' )( 1913 oSettings, cell, column, classes 1914 ); 1915 } 1916 1917 if ( createHeader ) { 1918 _fnDetectHeader( oSettings.aoHeader, thead ); 1919 } 1920 1921 /* ARIA role for the rows */ 1922 $(thead).find('>tr').attr('role', 'row'); 1923 1924 /* Deal with the footer - add classes if required */ 1925 $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH ); 1926 $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH ); 1927 1928 // Cache the footer cells. Note that we only take the cells from the first 1929 // row in the footer. If there is more than one row the user wants to 1930 // interact with, they need to use the table().foot() method. Note also this 1931 // allows cells to be used for multiple columns using colspan 1932 if ( tfoot !== null ) { 1933 var cells = oSettings.aoFooter[0]; 1934 1935 for ( i=0, ien=cells.length ; i<ien ; i++ ) { 1936 column = columns[i]; 1937 column.nTf = cells[i].cell; 1938 1939 if ( column.sClass ) { 1940 $(column.nTf).addClass( column.sClass ); 1941 } 1942 } 1943 } 1944 } 1945 1946 1947 /** 1948 * Draw the header (or footer) element based on the column visibility states. The 1949 * methodology here is to use the layout array from _fnDetectHeader, modified for 1950 * the instantaneous column visibility, to construct the new layout. The grid is 1951 * traversed over cell at a time in a rows x columns grid fashion, although each 1952 * cell insert can cover multiple elements in the grid - which is tracks using the 1953 * aApplied array. Cell inserts in the grid will only occur where there isn't 1954 * already a cell in that position. 1955 * @param {object} oSettings dataTables settings object 1956 * @param array {objects} aoSource Layout array from _fnDetectHeader 1957 * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc, 1958 * @memberof DataTable#oApi 1959 */ 1960 function _fnDrawHead( oSettings, aoSource, bIncludeHidden ) 1961 { 1962 var i, iLen, j, jLen, k, kLen, n, nLocalTr; 1963 var aoLocal = []; 1964 var aApplied = []; 1965 var iColumns = oSettings.aoColumns.length; 1966 var iRowspan, iColspan; 1967 1968 if ( ! aoSource ) 1969 { 1970 return; 1971 } 1972 1973 if ( bIncludeHidden === undefined ) 1974 { 1975 bIncludeHidden = false;
1976 } 1977 1978 /* Make a copy of the master layout array, but without the visible columns in it */ 1979 for ( i=0, iLen=aoSource.length ; i<iLen ; i++ ) 1980 { 1981 aoLocal[i] = aoSource[i].slice(); 1982 aoLocal[i].nTr = aoSource[i].nTr; 1983 1984 /* Remove any columns which are currently hidden */ 1985 for ( j=iColumns-1 ; j>=0 ; j-- ) 1986 { 1987 if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden ) 1988 { 1989 aoLocal[i].splice( j, 1 ); 1990 } 1991 } 1992 1993 /* Prep the applied array - it needs an element for each row */ 1994 aApplied.push( [] ); 1995 } 1996 1997 for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ ) 1998 { 1999 nLocalTr = aoLocal[i].nTr; 2000 2001 /* All cells are going to be replaced, so empty out the row */ 2002 if ( nLocalTr ) 2003 { 2004 while( (n = nLocalTr.firstChild) ) 2005 { 2006 nLocalTr.removeChild( n ); 2007 } 2008 } 2009 2010 for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ ) 2011 { 2012 iRowspan = 1; 2013 iColspan = 1; 2014 2015 /* Check to see if there is already a cell (row/colspan) covering our target 2016 * insert point. If there is, then there is nothing to do. 2017 */ 2018 if ( aApplied[i][j] === undefined ) 2019 { 2020 nLocalTr.appendChild( aoLocal[i][j].cell ); 2021 aApplied[i][j] = 1; 2022 2023 /* Expand the cell to cover as many rows as needed */ 2024 while ( aoLocal[i+iRowspan] !== undefined && 2025 aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell ) 2026 { 2027 aApplied[i+iRowspan][j] = 1; 2028 iRowspan++; 2029 } 2030 2031 /* Expand the cell to cover as many columns as needed */ 2032 while ( aoLocal[i][j+iColspan] !== undefined && 2033 aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell ) 2034 { 2035 /* Must update the applied array over the rows for the columns */ 2036 for ( k=0 ; k<iRowspan ; k++ ) 2037 { 2038 aApplied[i+k][j+iColspan] = 1; 2039 } 2040 iColspan++; 2041 } 2042 2043 /* Do the actual expansion in the DOM */ 2044 $(aoLocal[i][j].cell) 2045 .attr('rowspan', iRowspan) 2046 .attr('colspan', iColspan); 2047 } 2048 } 2049 } 2050 } 2051 2052 2053 /** 2054 * Insert the required TR nodes into the table for display 2055 * @param {object} oSettings dataTables settings object 2056 * @memberof DataTable#oApi 2057 */ 2058 function _fnDraw( oSettings ) 2059 { 2060 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */ 2061 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] ); 2062 if ( $.inArray( false, aPreDraw ) !== -1 ) 2063 { 2064 _fnProcessingDisplay( oSettings, false ); 2065 return; 2066 } 2067 2068 var i, iLen, n; 2069 var anRows = []; 2070 var iRowCount = 0; 2071 var asStripeClasses = oSettings.asStripeClasses; 2072 var iStripes = asStripeClasses.length; 2073 var iOpenRows = oSettings.aoOpenRows.length; 2074 var oLang = oSettings.oLanguage; 2075 var iInitDisplayStart = oSettings.iInitDisplayStart; 2076 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 2077 var aiDisplay = oSettings.aiDisplay; 2078 2079 oSettings.bDrawing = true; 2080 2081 /* Check and see if we have an initial draw position from state saving */ 2082 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 ) 2083 { 2084 oSettings._iDisplayStart = bServerSide ? 2085 iInitDisplayStart : 2086 iInitDisplayStart >= oSettings.fnRecordsDisplay() ? 2087 0 : 2088 iInitDisplayStart; 2089 2090 oSettings.iInitDisplayStart = -1; 2091 } 2092 2093 var iDisplayStart = oSettings._iDisplayStart; 2094 var iDisplayEnd = oSettings.fnDisplayEnd(); 2095 2096 /* Server-side processing draw intercept */ 2097 if ( oSettings.bDeferLoading ) 2098 { 2099 oSettings.bDeferLoading = false; 2100 oSettings.iDraw++; 2101 _fnProcessingDisplay( oSettings, false ); 2102 } 2103 else if ( !bServerSide ) 2104 { 2105 oSettings.iDraw++; 2106 } 2107 else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) ) 2108 { 2109 return; 2110 } 2111 2112 if ( aiDisplay.length !== 0 ) 2113 { 2114 var iStart = bServerSide ? 0 : iDisplayStart; 2115 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd; 2116 2117 for ( var j=iStart ; j<iEnd ; j++ ) 2118 { 2119 var iDataIndex = aiDisplay[j]; 2120 var aoData = oSettings.aoData[ iDataIndex ]; 2121 if ( aoData.nTr === null ) 2122 { 2123 _fnCreateTr( oSettings, iDataIndex ); 2124 } 2125 2126 var nRow = aoData.nTr; 2127 2128 /* Remove the old striping classes and then add the new one */ 2129 if ( iStripes !== 0 ) 2130 { 2131 var sStripe = asStripeClasses[ iRowCount % iStripes ]; 2132 if ( aoData._sRowStripe != sStripe ) 2133 { 2134 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe ); 2135 aoData._sRowStripe = sStripe; 2136 } 2137 } 2138 2139 // Row callback functions - might want to manipulate the row 2140 // iRowCount and j are not currently documented. Are they at all 2141 // useful? 2142 _fnCallbackFire( oSettings, 'aoRowCallback', null, 2143 [nRow, aoData._aData, iRowCount, j] ); 2144 2145 anRows.push( nRow ); 2146 iRowCount++; 2147 } 2148 } 2149 else 2150 { 2151 /* Table is empty - create a row with an empty message in it */ 2152 var sZero = oLang.sZeroRecords; 2153 if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' ) 2154 { 2155 sZero = oLang.sLoadingRecords; 2156 } 2157 else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 ) 2158 { 2159 sZero = oLang.sEmptyTable; 2160 } 2161 2162 anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } ) 2163 .append( $('<td />', { 2164 'valign': 'top', 2165 'colSpan': _fnVisbleColumns( oSettings ), 2166 'class': oSettings.oClasses.sRowEmpty 2167 } ).html( sZero ) )[0]; 2168 } 2169 2170 /* Header and footer callbacks */ 2171 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], 2172 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 2173 2174 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], 2175 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 2176 2177 var body = $(oSettings.nTBody); 2178 2179 body.children().detach(); 2180 body.append( $(anRows) ); 2181 2182 /* Call all required callback functions for the end of a draw */ 2183 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] ); 2184 2185 /* Draw is complete, sorting and filtering must be as well */ 2186 oSettings.bSorted = false;
2187 oSettings.bFiltered = false; 2188 oSettings.bDrawing = false; 2189 } 2190 2191 2192 /** 2193 * Redraw the table - taking account of the various features which are enabled 2194 * @param {object} oSettings dataTables settings object 2195 * @param {boolean} [holdPosition] Keep the current paging position. By default 2196 * the paging is reset to the first page 2197 * @memberof DataTable#oApi 2198 */ 2199 function _fnReDraw( settings, holdPosition ) 2200 { 2201 var 2202 features = settings.oFeatures, 2203 sort = features.bSort, 2204 filter = features.bFilter; 2205 2206 if ( sort ) { 2207 _fnSort( settings ); 2208 } 2209 2210 if ( filter ) { 2211 _fnFilterComplete( settings, settings.oPreviousSearch ); 2212 } 2213 else { 2214 // No filtering, so we want to just use the display master 2215 settings.aiDisplay = settings.aiDisplayMaster.slice(); 2216 } 2217 2218 if ( holdPosition !== true ) { 2219 settings._iDisplayStart = 0; 2220 } 2221 2222 // Let any modules know about the draw hold position state (used by 2223 // scrolling internally) 2224 settings._drawHold = holdPosition; 2225 2226 _fnDraw( settings ); 2227 2228 settings._drawHold = false; 2229 } 2230 2231 2232 /** 2233 * Add the options to the page HTML for the table 2234 * @param {object} oSettings dataTables settings object 2235 * @memberof DataTable#oApi 2236 */ 2237 function _fnAddOptionsHtml ( oSettings ) 2238 { 2239 var classes = oSettings.oClasses; 2240 var table = $(oSettings.nTable); 2241 var holding = $('<div/>').insertBefore( table ); // Holding element for speed 2242 var features = oSettings.oFeatures; 2243 2244 // All DataTables are wrapped in a div 2245 var insert = $('<div/>', { 2246 id: oSettings.sTableId+'_wrapper', 2247 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter) 2248 } ); 2249 2250 oSettings.nHolding = holding[0]; 2251 oSettings.nTableWrapper = insert[0]; 2252 oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling; 2253
2254 /* Loop over the user set positioning and place the elements as needed */ 2255 var aDom = oSettings.sDom.split(''); 2256 var featureNode, cOption, nNewNode, cNext, sAttr, j; 2257 for ( var i=0 ; i<aDom.length ; i++ ) 2258 { 2259 featureNode = null; 2260 cOption = aDom[i]; 2261 2262 if ( cOption == '<' ) 2263 { 2264 /* New container div */ 2265 nNewNode = $('<div/>')[0]; 2266 2267 /* Check to see if we should append an id and/or a class name to the container */ 2268 cNext = aDom[i+1]; 2269 if ( cNext == "'" || cNext == '"' ) 2270 { 2271 sAttr = ""; 2272 j = 2; 2273 while ( aDom[i+j] != cNext ) 2274 { 2275 sAttr += aDom[i+j]; 2276 j++; 2277 } 2278 2279 /* Replace jQuery UI constants @todo depreciated */ 2280 if ( sAttr == "H" ) 2281 { 2282 sAttr = classes.sJUIHeader; 2283 } 2284 else if ( sAttr == "F" ) 2285 { 2286 sAttr = classes.sJUIFooter; 2287 } 2288 2289 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic 2290 * breaks the string into parts and applies them as needed 2291 */ 2292 if ( sAttr.indexOf('.') != -1 ) 2293 { 2294 var aSplit = sAttr.split('.'); 2295 nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1); 2296 nNewNode.className = aSplit[1]; 2297 } 2298 else if ( sAttr.charAt(0) == "#" ) 2299 { 2300 nNewNode.id = sAttr.substr(1, sAttr.length-1); 2301 } 2302 else 2303 { 2304 nNewNode.className = sAttr; 2305 } 2306 2307 i += j; /* Move along the position array */ 2308 } 2309 2310 insert.append( nNewNode ); 2311 insert = $(nNewNode); 2312 } 2313 else if ( cOption == '>' ) 2314 { 2315 /* End container div */ 2316 insert = insert.parent(); 2317 } 2318 // @todo Move options into their own plugins? 2319 else if ( cOption == 'l' && features.bPaginate && features.bLengthChange ) 2320 { 2321 /* Length */ 2322 featureNode = _fnFeatureHtmlLength( oSettings ); 2323 } 2324 else if ( cOption == 'f' && features.bFilter ) 2325 { 2326 /* Filter */ 2327 featureNode = _fnFeatureHtmlFilter( oSettings ); 2328 } 2329 else if ( cOption == 'r' && features.bProcessing ) 2330 { 2331 /* pRocessing */ 2332 featureNode = _fnFeatureHtmlProcessing( oSettings ); 2333 } 2334 else if ( cOption == 't' ) 2335 { 2336 /* Table */ 2337 featureNode = _fnFeatureHtmlTable( oSettings ); 2338 } 2339 else if ( cOption == 'i' && features.bInfo ) 2340 { 2341 /* Info */ 2342 featureNode = _fnFeatureHtmlInfo( oSettings ); 2343 } 2344 else if ( cOption == 'p' && features.bPaginate ) 2345 { 2346 /* Pagination */ 2347 featureNode = _fnFeatureHtmlPaginate( oSettings ); 2348 } 2349 else if ( DataTable.ext.feature.length !== 0 ) 2350 { 2351 /* Plug-in features */ 2352 var aoFeatures = DataTable.ext.feature; 2353 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ ) 2354 { 2355 if ( cOption == aoFeatures[k].cFeature ) 2356 { 2357 featureNode = aoFeatures[k].fnInit( oSettings ); 2358 break; 2359 } 2360 } 2361 } 2362 2363 /* Add to the 2D features array */ 2364 if ( featureNode ) 2365 { 2366 var aanFeatures = oSettings.aanFeatures; 2367 2368 if ( ! aanFeatures[cOption] ) 2369 { 2370 aanFeatures[cOption] = []; 2371 } 2372 2373 aanFeatures[cOption].push( featureNode ); 2374 insert.append( featureNode ); 2375 } 2376 } 2377 2378 /* Built our DOM structure - replace the holding div with what we want */ 2379 holding.replaceWith( insert ); 2380 oSettings.nHolding = null; 2381 } 2382 2383 2384 /** 2385 * Use the DOM source to create up an array of header cells. The idea here is to 2386 * create a layout grid (array) of rows x columns, which contains a reference 2387 * to the cell that that point in the grid (regardless of col/rowspan), such that 2388 * any column / row could be removed and the new grid constructed 2389 * @param array {object} aLayout Array to store the calculated layout in 2390 * @param {node} nThead The header/footer element for the table 2391 * @memberof DataTable#oApi 2392 */ 2393 function _fnDetectHeader ( aLayout, nThead ) 2394 { 2395 var nTrs = $(nThead).children('tr'); 2396 var nTr, nCell; 2397 var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan; 2398 var bUnique; 2399 var fnShiftCol = function ( a, i, j ) { 2400 var k = a[i]; 2401 while ( k[j] ) { 2402 j++; 2403 } 2404 return j; 2405 }; 2406 2407 aLayout.splice( 0, aLayout.length ); 2408 2409 /* We know how many rows there are in the layout - so prep it */ 2410 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 2411 { 2412 aLayout.push( [] ); 2413 } 2414 2415 /* Calculate a layout array */ 2416 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 2417 { 2418 nTr = nTrs[i]; 2419 iColumn = 0; 2420 2421 /* For every cell in the row... */ 2422 nCell = nTr.firstChild; 2423 while ( nCell ) { 2424 if ( nCell.nodeName.toUpperCase() == "TD" || 2425 nCell.nodeName.toUpperCase() == "TH" ) 2426 { 2427 /* Get the col and rowspan attributes from the DOM and sanitise them */ 2428 iColspan = nCell.getAttribute('colspan') * 1;
2429 iRowspan = nCell.getAttribute('rowspan') * 1; 2430 iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan; 2431 iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan; 2432 2433 /* There might be colspan cells already in this row, so shift our target 2434 * accordingly 2435 */ 2436 iColShifted = fnShiftCol( aLayout, i, iColumn ); 2437 2438 /* Cache calculation for unique columns */ 2439 bUnique = iColspan === 1 ? true : false; 2440 2441 /* If there is col / rowspan, copy the information into the layout grid */ 2442 for ( l=0 ; l<iColspan ; l++ ) 2443 { 2444 for ( k=0 ; k<iRowspan ; k++ ) 2445 { 2446 aLayout[i+k][iColShifted+l] = { 2447 "cell": nCell, 2448 "unique": bUnique 2449 }; 2450 aLayout[i+k].nTr = nTr; 2451 } 2452 } 2453 } 2454 nCell = nCell.nextSibling; 2455 } 2456 } 2457 } 2458 2459 2460 /** 2461 * Get an array of unique th elements, one for each column 2462 * @param {object} oSettings dataTables settings object 2463 * @param {node} nHeader automatically detect the layout from this node - optional 2464 * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional 2465 * @returns array {node} aReturn list of unique th's 2466 * @memberof DataTable#oApi 2467 */ 2468 function _fnGetUniqueThs ( oSettings, nHeader, aLayout ) 2469 { 2470 var aReturn = []; 2471 if ( !aLayout ) 2472 { 2473 aLayout = oSettings.aoHeader; 2474 if ( nHeader ) 2475 { 2476 aLayout = []; 2477 _fnDetectHeader( aLayout, nHeader ); 2478 } 2479 } 2480 2481 for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ ) 2482 { 2483 for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ ) 2484 { 2485 if ( aLayout[i][j].unique && 2486 (!aReturn[j] || !oSettings.bSortCellsTop) ) 2487 { 2488 aReturn[j] = aLayout[i][j].cell; 2489 } 2490 } 2491 } 2492 2493 return aReturn; 2494 } 2495 2496 /** 2497 * Create an Ajax call based on the table's settings, taking into account that 2498 * parameters can have multiple forms, and backwards compatibility. 2499 * 2500 * @param {object} oSettings dataTables settings object 2501 * @param {array} data Data to send to the server, required by 2502 * DataTables - may be augmented by developer callbacks 2503 * @param {function} fn Callback function to run when data is obtained 2504 */ 2505 function _fnBuildAjax( oSettings, data, fn ) 2506 { 2507 // Compatibility with 1.9-, allow fnServerData and event to manipulate 2508 _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] ); 2509 2510 // Convert to object based for 1.10+ if using the old array scheme which can 2511 // come from server-side processing or serverParams 2512 if ( data && $.isArray(data) ) { 2513 var tmp = {}; 2514 var rbracket = /(.*?)\[\]$/; 2515 2516 $.each( data, function (key, val) { 2517 var match = val.name.match(rbracket); 2518 2519 if ( match ) { 2520 // Support for arrays 2521 var name = match[0]; 2522 2523 if ( ! tmp[ name ] ) { 2524 tmp[ name ] = []; 2525 } 2526 tmp[ name ].push( val.value ); 2527 } 2528 else { 2529 tmp[val.name] = val.value; 2530 } 2531 } ); 2532 data = tmp; 2533 } 2534 2535 var ajaxData; 2536 var ajax = oSettings.ajax; 2537 var instance = oSettings.oInstance; 2538 var callback = function ( json ) { 2539 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] ); 2540 fn( json ); 2541 }; 2542 2543 if ( $.isPlainObject( ajax ) && ajax.data ) 2544 { 2545 ajaxData = ajax.data; 2546 2547 var newData = $.isFunction( ajaxData ) ? 2548 ajaxData( data, oSettings ) : // fn can manipulate data or return 2549 ajaxData; // an object object or array to merge 2550 2551 // If the function returned something, use that alone 2552 data = $.isFunction( ajaxData ) && newData ? 2553 newData : 2554 $.extend( true, data, newData ); 2555 2556 // Remove the data property as we've resolved it already and don't want 2557 // jQuery to do it again (it is restored at the end of the function) 2558 delete ajax.data; 2559 } 2560 2561 var baseAjax = { 2562 "data": data, 2563 "success": function (json) { 2564 var error = json.error || json.sError; 2565 if ( error ) { 2566 _fnLog( oSettings, 0, error ); 2567 } 2568 2569 oSettings.json = json; 2570 callback( json ); 2571 }, 2572 "dataType": "json", 2573 "cache": false, 2574 "type": oSettings.sServerMethod, 2575 "error": function (xhr, error, thrown) { 2576 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] ); 2577 2578 if ( $.inArray( true, ret ) === -1 ) { 2579 if ( error == "parsererror" ) { 2580 _fnLog( oSettings, 0, 'Invalid JSON response', 1 ); 2581 } 2582 else if ( xhr.readyState === 4 ) { 2583 _fnLog( oSettings, 0, 'Ajax error', 7 ); 2584 } 2585 } 2586 2587 _fnProcessingDisplay( oSettings, false ); 2588 } 2589 }; 2590 2591 // Store the data submitted for the API 2592 oSettings.oAjaxData = data; 2593 2594 // Allow plug-ins and external processes to modify the data 2595 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] ); 2596 2597 if ( oSettings.fnServerData ) 2598 { 2599 // DataTables 1.9- compatibility 2600 oSettings.fnServerData.call( instance, 2601 oSettings.sAjaxSource, 2602 $.map( data, function (val, key) { // Need to convert back to 1.9 trad format 2603 return { name: key, value: val }; 2604 } ), 2605 callback, 2606 oSettings 2607 ); 2608 } 2609 else if ( oSettings.sAjaxSource || typeof ajax === 'string' ) 2610 { 2611 // DataTables 1.9- compatibility 2612 oSettings.jqXHR = $.ajax( $.extend( baseAjax, { 2613 url: ajax || oSettings.sAjaxSource 2614 } ) ); 2615 } 2616 else if ( $.isFunction( ajax ) ) 2617 { 2618 // Is a function - let the caller define what needs to be done 2619 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings ); 2620 } 2621 else 2622 { 2623 // Object to extend the base settings 2624 oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) ); 2625 2626 // Restore for next time around 2627 ajax.data = ajaxData; 2628 } 2629 } 2630 2631 2632 /** 2633 * Update the table using an Ajax call 2634 * @param {object} settings dataTables settings object 2635 * @returns {boolean} Block the table drawing or not 2636 * @memberof DataTable#oApi 2637 */ 2638 function _fnAjaxUpdate( settings ) 2639 { 2640 if ( settings.bAjaxDataGet ) { 2641 settings.iDraw++; 2642 _fnProcessingDisplay( settings, true ); 2643 2644 _fnBuildAjax( 2645 settings, 2646 _fnAjaxParameters( settings ), 2647 function(json) { 2648 _fnAjaxUpdateDraw( settings, json ); 2649 } 2650 ); 2651 2652 return false;
2653 } 2654 return true; 2655 } 2656 2657 2658 /** 2659 * Build up the parameters in an object needed for a server-side processing 2660 * request. Note that this is basically done twice, is different ways - a modern 2661 * method which is used by default in DataTables 1.10 which uses objects and 2662 * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if 2663 * the sAjaxSource option is used in the initialisation, or the legacyAjax 2664 * option is set. 2665 * @param {object} oSettings dataTables settings object 2666 * @returns {bool} block the table drawing or not 2667 * @memberof DataTable#oApi 2668 */ 2669 function _fnAjaxParameters( settings ) 2670 { 2671 var 2672 columns = settings.aoColumns, 2673 columnCount = columns.length, 2674 features = settings.oFeatures, 2675 preSearch = settings.oPreviousSearch, 2676 preColSearch = settings.aoPreSearchCols, 2677 i, data = [], dataProp, column, columnSearch, 2678 sort = _fnSortFlatten( settings ), 2679 displayStart = settings._iDisplayStart, 2680 displayLength = features.bPaginate !== false ? 2681 settings._iDisplayLength : 2682 -1; 2683 2684 var param = function ( name, value ) { 2685 data.push( { 'name': name, 'value': value } ); 2686 }; 2687 2688 // DataTables 1.9- compatible method 2689 param( 'sEcho', settings.iDraw ); 2690 param( 'iColumns', columnCount ); 2691 param( 'sColumns', _pluck( columns, 'sName' ).join(',') ); 2692 param( 'iDisplayStart', displayStart ); 2693 param( 'iDisplayLength', displayLength ); 2694 2695 // DataTables 1.10+ method 2696 var d = { 2697 draw: settings.iDraw, 2698 columns: [], 2699 order: [], 2700 start: displayStart, 2701 length: displayLength, 2702 search: { 2703 value: preSearch.sSearch, 2704 regex: preSearch.bRegex 2705 } 2706 }; 2707 2708 for ( i=0 ; i<columnCount ; i++ ) { 2709 column = columns[i]; 2710 columnSearch = preColSearch[i]; 2711 dataProp = typeof column.mData=="function" ? 'function' : column.mData ; 2712 2713 d.columns.push( { 2714 data: dataProp, 2715 name: column.sName, 2716 searchable: column.bSearchable, 2717 orderable: column.bSortable, 2718 search: { 2719 value: columnSearch.sSearch, 2720 regex: columnSearch.bRegex 2721 } 2722 } ); 2723 2724 param( "mDataProp_"+i, dataProp ); 2725 2726 if ( features.bFilter ) { 2727 param( 'sSearch_'+i, columnSearch.sSearch ); 2728 param( 'bRegex_'+i, columnSearch.bRegex ); 2729 param( 'bSearchable_'+i, column.bSearchable ); 2730 } 2731 2732 if ( features.bSort ) { 2733 param( 'bSortable_'+i, column.bSortable ); 2734 } 2735 } 2736 2737 if ( features.bFilter ) { 2738 param( 'sSearch', preSearch.sSearch ); 2739 param( 'bRegex', preSearch.bRegex ); 2740 } 2741 2742 if ( features.bSort ) { 2743 $.each( sort, function ( i, val ) { 2744 d.order.push( { column: val.col, dir: val.dir } ); 2745 2746 param( 'iSortCol_'+i, val.col ); 2747 param( 'sSortDir_'+i, val.dir ); 2748 } ); 2749 2750 param( 'iSortingCols', sort.length ); 2751 } 2752 2753 // If the legacy.ajax parameter is null, then we automatically decide which 2754 // form to use, based on sAjaxSource 2755 var legacy = DataTable.ext.legacy.ajax; 2756 if ( legacy === null ) { 2757 return settings.sAjaxSource ? data : d; 2758 } 2759 2760 // Otherwise, if legacy has been specified then we use that to decide on the 2761 // form 2762 return legacy ? data : d; 2763 } 2764 2765 2766 /** 2767 * Data the data from the server (nuking the old) and redraw the table 2768 * @param {object} oSettings dataTables settings object 2769 * @param {object} json json data return from the server. 2770 * @param {string} json.sEcho Tracking flag for DataTables to match requests 2771 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering 2772 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering 2773 * @param {array} json.aaData The data to display on this page 2774 * @param {string} [json.sColumns] Column ordering (sName, comma separated) 2775 * @memberof DataTable#oApi 2776 */ 2777 function _fnAjaxUpdateDraw ( settings, json ) 2778 { 2779 // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
2780 // Support both 2781 var compat = function ( old, modern ) { 2782 return json[old] !== undefined ? json[old] : json[modern]; 2783 }; 2784 2785 var data = _fnAjaxDataSrc( settings, json ); 2786 var draw = compat( 'sEcho', 'draw' ); 2787 var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' ); 2788 var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' ); 2789 2790 if ( draw ) { 2791 // Protect against out of sequence returns 2792 if ( draw*1 < settings.iDraw ) { 2793 return; 2794 } 2795 settings.iDraw = draw * 1; 2796 } 2797 2798 _fnClearTable( settings ); 2799 settings._iRecordsTotal = parseInt(recordsTotal, 10); 2800 settings._iRecordsDisplay = parseInt(recordsFiltered, 10); 2801 2802 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 2803 _fnAddData( settings, data[i] ); 2804 } 2805 settings.aiDisplay = settings.aiDisplayMaster.slice(); 2806 2807 settings.bAjaxDataGet = false; 2808 _fnDraw( settings ); 2809 2810 if ( ! settings._bInitComplete ) { 2811 _fnInitComplete( settings, json ); 2812 } 2813 2814 settings.bAjaxDataGet = true; 2815 _fnProcessingDisplay( settings, false ); 2816 } 2817 2818 2819 /** 2820 * Get the data from the JSON data source to use for drawing a table. Using 2821 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the 2822 * source object, or from a processing function. 2823 * @param {object} oSettings dataTables settings object 2824 * @param {object} json Data source object / array from the server 2825 * @return {array} Array of data to use 2826 */ 2827 function _fnAjaxDataSrc ( oSettings, json ) 2828 { 2829 var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ? 2830 oSettings.ajax.dataSrc : 2831 oSettings.sAjaxDataProp; // Compatibility with 1.9-. 2832 2833 // Compatibility with 1.9-. In order to read from aaData, check if the 2834 // default has been changed, if not, check for aaData 2835 if ( dataSrc === 'data' ) { 2836 return json.aaData || json[dataSrc]; 2837 } 2838 2839 return dataSrc !== "" ? 2840 _fnGetObjectDataFn( dataSrc )( json ) : 2841 json; 2842 } 2843 2844 /** 2845 * Generate the node required for filtering text 2846 * @returns {node} Filter control element 2847 * @param {object} oSettings dataTables settings object 2848 * @memberof DataTable#oApi 2849 */ 2850 function _fnFeatureHtmlFilter ( settings ) 2851 { 2852 var classes = settings.oClasses; 2853 var tableId = settings.sTableId; 2854 var language = settings.oLanguage; 2855 var previousSearch = settings.oPreviousSearch; 2856 var features = settings.aanFeatures; 2857 var input = '<input type="search" class="'+classes.sFilterInput+'"/>'; 2858 2859 var str = language.sSearch; 2860 str = str.match(/_INPUT_/) ? 2861 str.replace('_INPUT_', input) : 2862 str+input; 2863 2864 var filter = $('<div/>', { 2865 'id': ! features.f ? tableId+'_filter' : null, 2866 'class': classes.sFilter 2867 } ) 2868 .append( $('<label/>' ).append( str ) ); 2869 2870 var searchFn = function() { 2871 /* Update all other filter input elements for the new display */ 2872 var n = features.f; 2873 var val = !this.value ? "" : this.value; // mental IE8 fix :-( 2874 2875 /* Now do the filter */ 2876 if ( val != previousSearch.sSearch ) { 2877 _fnFilterComplete( settings, { 2878 "sSearch": val, 2879 "bRegex": previousSearch.bRegex, 2880 "bSmart": previousSearch.bSmart , 2881 "bCaseInsensitive": previousSearch.bCaseInsensitive 2882 } ); 2883 2884 // Need to redraw, without resorting 2885 settings._iDisplayStart = 0; 2886 _fnDraw( settings ); 2887 } 2888 }; 2889 2890 var searchDelay = settings.searchDelay !== null ? 2891 settings.searchDelay : 2892 _fnDataSource( settings ) === 'ssp' ? 2893 400 : 2894 0; 2895 2896 var jqFilter = $('input', filter) 2897 .val( previousSearch.sSearch ) 2898 .attr( 'placeholder', language.sSearchPlaceholder ) 2899 .bind( 2900 'keyup.DT search.DT input.DT paste.DT cut.DT', 2901 searchDelay ? 2902 _fnThrottle( searchFn, searchDelay ) : 2903 searchFn 2904 ) 2905 .bind( 'keypress.DT', function(e) { 2906 /* Prevent form submission */ 2907 if ( e.keyCode == 13 ) { 2908 return false; 2909 } 2910 } ) 2911 .attr('aria-controls', tableId); 2912 2913 // Update the input elements whenever the table is filtered 2914 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) { 2915 if ( settings === s ) { 2916 // IE9 throws an 'unknown error' if document.activeElement is used 2917 // inside an iframe or frame...
2918 try { 2919 if ( jqFilter[0] !== document.activeElement ) { 2920 jqFilter.val( previousSearch.sSearch ); 2921 } 2922 } 2923 catch ( e ) {} 2924 } 2925 } ); 2926 2927 return filter[0]; 2928 } 2929 2930 2931 /** 2932 * Filter the table using both the global filter and column based filtering 2933 * @param {object} oSettings dataTables settings object 2934 * @param {object} oSearch search information 2935 * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0) 2936 * @memberof DataTable#oApi 2937 */ 2938 function _fnFilterComplete ( oSettings, oInput, iForce ) 2939 { 2940 var oPrevSearch = oSettings.oPreviousSearch; 2941 var aoPrevSearch = oSettings.aoPreSearchCols; 2942 var fnSaveFilter = function ( oFilter ) { 2943 /* Save the filtering values */ 2944 oPrevSearch.sSearch = oFilter.sSearch; 2945 oPrevSearch.bRegex = oFilter.bRegex; 2946 oPrevSearch.bSmart = oFilter.bSmart; 2947 oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive; 2948 }; 2949 var fnRegex = function ( o ) { 2950 // Backwards compatibility with the bEscapeRegex option 2951 return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex; 2952 }; 2953 2954 // Resolve any column types that are unknown due to addition or invalidation 2955 // @todo As per sort - can this be moved into an event handler? 2956 _fnColumnTypes( oSettings ); 2957 2958 /* In server-side processing all filtering is done by the server, so no point hanging around here */ 2959 if ( _fnDataSource( oSettings ) != 'ssp' ) 2960 { 2961 /* Global filter */ 2962 _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive ); 2963 fnSaveFilter( oInput ); 2964 2965 /* Now do the individual column filter */ 2966 for ( var i=0 ; i<aoPrevSearch.length ; i++ ) 2967 { 2968 _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]), 2969 aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive ); 2970 } 2971 2972 /* Custom filtering */ 2973 _fnFilterCustom( oSettings ); 2974 } 2975 else 2976 { 2977 fnSaveFilter( oInput ); 2978 } 2979 2980 /* Tell the draw function we have been filtering */ 2981 oSettings.bFiltered = true; 2982 _fnCallbackFire( oSettings, null, 'search', [oSettings] ); 2983 } 2984 2985 2986 /** 2987 * Apply custom filtering functions 2988 * @param {object} oSettings dataTables settings object 2989 * @memberof DataTable#oApi 2990 */ 2991 function _fnFilterCustom( settings ) 2992 { 2993 var filters = DataTable.ext.search; 2994 var displayRows = settings.aiDisplay; 2995 var row, rowIdx; 2996 2997 for ( var i=0, ien=filters.length ; i<ien ; i++ ) { 2998 var rows = []; 2999 3000 // Loop over each row and see if it should be included 3001 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) { 3002 rowIdx = displayRows[ j ]; 3003 row = settings.aoData[ rowIdx ]; 3004 3005 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) { 3006 rows.push( rowIdx ); 3007 } 3008 } 3009 3010 // So the array reference doesn't break set the results into the 3011 // existing array 3012 displayRows.length = 0; 3013 $.merge( displayRows, rows ); 3014 } 3015 } 3016 3017 3018 /** 3019 * Filter the table on a per-column basis 3020 * @param {object} oSettings dataTables settings object 3021 * @param {string} sInput string to filter on 3022 * @param {int} iColumn column to filter 3023 * @param {bool} bRegex treat search string as a regular expression or not 3024 * @param {bool} bSmart use smart filtering or not 3025 * @param {bool} bCaseInsensitive Do case insenstive matching or not 3026 * @memberof DataTable#oApi 3027 */ 3028 function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive ) 3029 { 3030 if ( searchStr === '' ) { 3031 return; 3032 } 3033 3034 var data; 3035 var display = settings.aiDisplay; 3036 var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive ); 3037 3038 for ( var i=display.length-1 ; i>=0 ; i-- ) { 3039 data = settings.aoData[ display[i] ]._aFilterData[ colIdx ]; 3040 3041 if ( ! rpSearch.test( data ) ) { 3042 display.splice( i, 1 ); 3043 } 3044 } 3045 } 3046 3047 3048 /** 3049 * Filter the data table based on user input and draw the table 3050 * @param {object} settings dataTables settings object 3051 * @param {string} input string to filter on 3052 * @param {int} force optional - force a research of the master array (1) or not (undefined or 0) 3053 * @param {bool} regex treat as a regular expression or not 3054 * @param {bool} smart perform smart filtering or not 3055 * @param {bool} caseInsensitive Do case insenstive matching or not 3056 * @memberof DataTable#oApi 3057 */ 3058 function _fnFilter( settings, input, force, regex, smart, caseInsensitive ) 3059 { 3060 var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive ); 3061 var prevSearch = settings.oPreviousSearch.sSearch; 3062 var displayMaster = settings.aiDisplayMaster; 3063 var display, invalidated, i; 3064 3065 // Need to take account of custom filtering functions - always filter 3066 if ( DataTable.ext.search.length !== 0 ) { 3067 force = true; 3068 } 3069 3070 // Check if any of the rows were invalidated 3071 invalidated = _fnFilterData( settings ); 3072 3073 // If the input is blank - we just want the full data set 3074 if ( input.length <= 0 ) { 3075 settings.aiDisplay = displayMaster.slice(); 3076 } 3077 else { 3078 // New search - start from the master array 3079 if ( invalidated || 3080 force || 3081 prevSearch.length > input.length || 3082 input.indexOf(prevSearch) !== 0 || 3083 settings.bSorted // On resort, the display master needs to be 3084 // re-filtered since indexes will have changed 3085 ) { 3086 settings.aiDisplay = displayMaster.slice(); 3087 } 3088 3089 // Search the display array 3090 display = settings.aiDisplay; 3091 3092 for ( i=display.length-1 ; i>=0 ; i-- ) { 3093 if ( ! rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) { 3094 display.splice( i, 1 ); 3095 } 3096 } 3097 } 3098 } 3099 3100 3101 /** 3102 * Build a regular expression object suitable for searching a table 3103 * @param {string} sSearch string to search for 3104 * @param {bool} bRegex treat as a regular expression or not 3105 * @param {bool} bSmart perform smart filtering or not 3106 * @param {bool} bCaseInsensitive Do case insensitive matching or not 3107 * @returns {RegExp} constructed object 3108 * @memberof DataTable#oApi 3109 */ 3110 function _fnFilterCreateSearch( search, regex, smart, caseInsensitive ) 3111 { 3112 search = regex ? 3113 search : 3114 _fnEscapeRegex( search ); 3115 3116 if ( smart ) { 3117 /* For smart filtering we want to allow the search to work regardless of 3118 * word order. We also want double quoted text to be preserved, so word 3119 * order is important - a la google. So this is what we want to 3120 * generate: 3121 * 3122 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$ 3123 */ 3124 var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) { 3125 if ( word.charAt(0) === '"' ) { 3126 var m = word.match( /^"(.*)"$/ ); 3127 word = m ? m[1] : word; 3128 } 3129 3130 return word.replace('"', ''); 3131 } ); 3132 3133 search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$'; 3134 } 3135 3136 return new RegExp( search, caseInsensitive ? 'i' : '' ); 3137 } 3138 3139 3140 /** 3141 * Escape a string such that it can be used in a regular expression 3142 * @param {string} sVal string to escape 3143 * @returns {string} escaped string 3144 * @memberof DataTable#oApi 3145 */ 3146 function _fnEscapeRegex ( sVal ) 3147 { 3148 return sVal.replace( _re_escape_regex, '\\$1' ); 3149 } 3150 3151 3152
3153 var __filter_div = $('<div>')[0]; 3154 var __filter_div_textContent = __filter_div.textContent !== undefined; 3155 3156 // Update the filtering data for each row if needed (by invalidation or first run) 3157 function _fnFilterData ( settings ) 3158 { 3159 var columns = settings.aoColumns; 3160 var column; 3161 var i, j, ien, jen, filterData, cellData, row; 3162 var fomatters = DataTable.ext.type.search; 3163 var wasInvalidated = false; 3164 3165 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 3166 row = settings.aoData[i]; 3167 3168 if ( ! row._aFilterData ) { 3169 filterData = []; 3170 3171 for ( j=0, jen=columns.length ; j<jen ; j++ ) { 3172 column = columns[j]; 3173 3174 if ( column.bSearchable ) { 3175 cellData = _fnGetCellData( settings, i, j, 'filter' ); 3176 3177 if ( fomatters[ column.sType ] ) { 3178 cellData = fomatters[ column.sType ]( cellData ); 3179 } 3180 3181 // Search in DataTables 1.10 is string based. In 1.11 this 3182 // should be altered to also allow strict type checking. 3183 if ( cellData === null ) { 3184 cellData = ''; 3185 } 3186 3187 if ( typeof cellData !== 'string' && cellData.toString ) { 3188 cellData = cellData.toString(); 3189 } 3190 } 3191 else { 3192 cellData = ''; 3193 } 3194 3195 // If it looks like there is an HTML entity in the string, 3196 // attempt to decode it so sorting works as expected. Note that 3197 // we could use a single line of jQuery to do this, but the DOM 3198 // method used here is much faster http://jsperf.com/html-decode 3199 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) { 3200 __filter_div.innerHTML = cellData; 3201 cellData = __filter_div_textContent ? 3202 __filter_div.textContent : 3203 __filter_div.innerText; 3204 } 3205 3206 if ( cellData.replace ) { 3207 cellData = cellData.replace(/[\r\n]/g, ''); 3208 } 3209 3210 filterData.push( cellData ); 3211 } 3212 3213 row._aFilterData = filterData; 3214 row._sFilterRow = filterData.join(' '); 3215 wasInvalidated = true; 3216 } 3217 } 3218 3219 return wasInvalidated; 3220 } 3221 3222 3223 /** 3224 * Convert from the internal Hungarian notation to camelCase for external 3225 * interaction 3226 * @param {object} obj Object to convert 3227 * @returns {object} Inverted object 3228 * @memberof DataTable#oApi 3229 */ 3230 function _fnSearchToCamel ( obj ) 3231 { 3232 return { 3233 search: obj.sSearch, 3234 smart: obj.bSmart, 3235 regex: obj.bRegex, 3236 caseInsensitive: obj.bCaseInsensitive 3237 }; 3238 } 3239 3240 3241 3242 /** 3243 * Convert from camelCase notation to the internal Hungarian. We could use the 3244 * Hungarian convert function here, but this is cleaner 3245 * @param {object} obj Object to convert 3246 * @returns {object} Inverted object 3247 * @memberof DataTable#oApi 3248 */ 3249 function _fnSearchToHung ( obj ) 3250 { 3251 return { 3252 sSearch: obj.search, 3253 bSmart: obj.smart, 3254 bRegex: obj.regex, 3255 bCaseInsensitive: obj.caseInsensitive 3256 }; 3257 } 3258 3259 /** 3260 * Generate the node required for the info display 3261 * @param {object} oSettings dataTables settings object 3262 * @returns {node} Information element 3263 * @memberof DataTable#oApi 3264 */ 3265 function _fnFeatureHtmlInfo ( settings ) 3266 { 3267 var 3268 tid = settings.sTableId, 3269 nodes = settings.aanFeatures.i, 3270 n = $('<div/>', { 3271 'class': settings.oClasses.sInfo, 3272 'id': ! nodes ? tid+'_info' : null 3273 } ); 3274 3275 if ( ! nodes ) { 3276 // Update display on each draw 3277 settings.aoDrawCallback.push( { 3278 "fn": _fnUpdateInfo, 3279 "sName": "information" 3280 } ); 3281 3282 n 3283 .attr( 'role', 'status' ) 3284 .attr( 'aria-live', 'polite' ); 3285 3286 // Table is described by our info div 3287 $(settings.nTable).attr( 'aria-describedby', tid+'_info' ); 3288 } 3289 3290 return n[0]; 3291 } 3292 3293 3294 /** 3295 * Update the information elements in the display 3296 * @param {object} settings dataTables settings object 3297 * @memberof DataTable#oApi 3298 */ 3299 function _fnUpdateInfo ( settings ) 3300 { 3301 /* Show information about the table */ 3302 var nodes = settings.aanFeatures.i; 3303 if ( nodes.length === 0 ) { 3304 return; 3305 } 3306 3307 var 3308 lang = settings.oLanguage, 3309 start = settings._iDisplayStart+1, 3310 end = settings.fnDisplayEnd(), 3311 max = settings.fnRecordsTotal(), 3312 total = settings.fnRecordsDisplay(), 3313 out = total ? 3314 lang.sInfo : 3315 lang.sInfoEmpty; 3316 3317 if ( total !== max ) { 3318 /* Record set after filtering */ 3319 out += ' ' + lang.sInfoFiltered; 3320 } 3321 3322 // Convert the macros 3323 out += lang.sInfoPostFix; 3324 out = _fnInfoMacros( settings, out ); 3325 3326 var callback = lang.fnInfoCallback; 3327 if ( callback !== null ) { 3328 out = callback.call( settings.oInstance, 3329 settings, start, end, max, total, out 3330 ); 3331 } 3332 3333 $(nodes).html( out ); 3334 } 3335 3336 3337 function _fnInfoMacros ( settings, str ) 3338 { 3339 // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only 3340 // internally 3341 var
3342 formatter = settings.fnFormatNumber, 3343 start = settings._iDisplayStart+1, 3344 len = settings._iDisplayLength, 3345 vis = settings.fnRecordsDisplay(), 3346 all = len === -1; 3347 3348 return str. 3349 replace(/_START_/g, formatter.call( settings, start ) ). 3350 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ). 3351 replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ). 3352 replace(/_TOTAL_/g, formatter.call( settings, vis ) ). 3353 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ). 3354 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ); 3355 } 3356 3357 3358 3359 /** 3360 * Draw the table for the first time, adding all required features 3361 * @param {object} settings dataTables settings object 3362 * @memberof DataTable#oApi 3363 */ 3364 function _fnInitialise ( settings ) 3365 { 3366 var i, iLen, iAjaxStart=settings.iInitDisplayStart; 3367 var columns = settings.aoColumns, column; 3368 var features = settings.oFeatures; 3369 var deferLoading = settings.bDeferLoading; // value modified by the draw 3370 3371 /* Ensure that the table data is fully initialised */ 3372 if ( ! settings.bInitialised ) { 3373 setTimeout( function(){ _fnInitialise( settings ); }, 200 ); 3374 return; 3375 } 3376 3377 /* Show the display HTML options */ 3378 _fnAddOptionsHtml( settings ); 3379 3380 /* Build and draw the header / footer for the table */ 3381 _fnBuildHead( settings ); 3382 _fnDrawHead( settings, settings.aoHeader ); 3383 _fnDrawHead( settings, settings.aoFooter ); 3384 3385 /* Okay to show that something is going on now */ 3386 _fnProcessingDisplay( settings, true ); 3387 3388 /* Calculate sizes for columns */ 3389 if ( features.bAutoWidth ) { 3390 _fnCalculateColumnWidths( settings ); 3391 } 3392 3393 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) { 3394 column = columns[i]; 3395 3396 if ( column.sWidth ) { 3397 column.nTh.style.width = _fnStringToCss( column.sWidth ); 3398 } 3399 } 3400 3401 _fnCallbackFire( settings, null, 'preInit', [settings] ); 3402 3403 // If there is default sorting required - let's do it. The sort function 3404 // will do the drawing for us. Otherwise we draw the table regardless of the 3405 // Ajax source - this allows the table to look initialised for Ajax sourcing 3406 // data (show 'loading' message possibly) 3407 _fnReDraw( settings ); 3408 3409 // Server-side processing init complete is done by _fnAjaxUpdateDraw 3410 var dataSrc = _fnDataSource( settings ); 3411 if ( dataSrc != 'ssp' || deferLoading ) { 3412 // if there is an ajax source load the data 3413 if ( dataSrc == 'ajax' ) { 3414 _fnBuildAjax( settings, [], function(json) { 3415 var aData = _fnAjaxDataSrc( settings, json ); 3416 3417 // Got the data - add it to the table 3418 for ( i=0 ; i<aData.length ; i++ ) { 3419 _fnAddData( settings, aData[i] ); 3420 } 3421 3422 // Reset the init display for cookie saving. We've already done 3423 // a filter, and therefore cleared it before. So we need to make 3424 // it appear 'fresh' 3425 settings.iInitDisplayStart = iAjaxStart; 3426 3427 _fnReDraw( settings ); 3428 3429 _fnProcessingDisplay( settings, false ); 3430 _fnInitComplete( settings, json ); 3431 }, settings ); 3432 } 3433 else { 3434 _fnProcessingDisplay( settings, false ); 3435 _fnInitComplete( settings ); 3436 } 3437 } 3438 } 3439 3440 3441 /** 3442 * Draw the table for the first time, adding all required features 3443 * @param {object} oSettings dataTables settings object 3444 * @param {object} [json] JSON from the server that completed the table, if using Ajax source 3445 * with client-side processing (optional) 3446 * @memberof DataTable#oApi 3447 */ 3448 function _fnInitComplete ( settings, json ) 3449 { 3450 settings._bInitComplete = true; 3451 3452 // When data was added after the initialisation (data or Ajax) we need to 3453 // calculate the column sizing 3454 if ( json || settings.oInit.aaData ) { 3455 _fnAdjustColumnSizing( settings ); 3456 } 3457 3458 _fnCallbackFire( settings, null, 'plugin-init', [settings, json] ); 3459 _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] ); 3460 } 3461 3462 3463 function _fnLengthChange ( settings, val ) 3464 { 3465 var len = parseInt( val, 10 ); 3466 settings._iDisplayLength = len; 3467 3468 _fnLengthOverflow( settings ); 3469 3470 // Fire length change event 3471 _fnCallbackFire( settings, null, 'length', [settings, len] ); 3472 } 3473 3474 3475 /** 3476 * Generate the node required for user display length changing 3477 * @param {object} settings dataTables settings object 3478 * @returns {node} Display length feature node 3479 * @memberof DataTable#oApi 3480 */ 3481 function _fnFeatureHtmlLength ( settings ) 3482 { 3483 var 3484 classes = settings.oClasses, 3485 tableId = settings.sTableId, 3486 menu = settings.aLengthMenu, 3487 d2 = $.isArray( menu[0] ), 3488 lengths = d2 ? menu[0] : menu, 3489 language = d2 ? menu[1] : menu; 3490 3491 var select = $('<select/>', { 3492 'name': tableId+'_length', 3493 'aria-controls': tableId, 3494 'class': classes.sLengthSelect 3495 } ); 3496 3497 for ( var i=0, ien=lengths.length ; i<ien ; i++ ) { 3498 select[0][ i ] = new Option( language[i], lengths[i] ); 3499 } 3500
3501 var div = $('<div><label/></div>').addClass( classes.sLength ); 3502 if ( ! settings.aanFeatures.l ) { 3503 div[0].id = tableId+'_length'; 3504 } 3505 3506 div.children().append( 3507 settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML ) 3508 ); 3509 3510 // Can't use `select` variable as user might provide their own and the 3511 // reference is broken by the use of outerHTML 3512 $('select', div) 3513 .val( settings._iDisplayLength ) 3514 .bind( 'change.DT', function(e) { 3515 _fnLengthChange( settings, $(this).val() ); 3516 _fnDraw( settings ); 3517 } ); 3518 3519 // Update node value whenever anything changes the table's length 3520 $(settings.nTable).bind( 'length.dt.DT', function (e, s, len) { 3521 if ( settings === s ) { 3522 $('select', div).val( len ); 3523 } 3524 } ); 3525 3526 return div[0]; 3527 } 3528 3529 3530 3531 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 3532 * Note that most of the paging logic is done in 3533 * DataTable.ext.pager 3534 */ 3535 3536 /** 3537 * Generate the node required for default pagination 3538 * @param {object} oSettings dataTables settings object 3539 * @returns {node} Pagination feature node 3540 * @memberof DataTable#oApi 3541 */ 3542 function _fnFeatureHtmlPaginate ( settings ) 3543 { 3544 var 3545 type = settings.sPaginationType, 3546 plugin = DataTable.ext.pager[ type ], 3547 modern = typeof plugin === 'function', 3548 redraw = function( settings ) { 3549 _fnDraw( settings ); 3550 }, 3551 node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0], 3552 features = settings.aanFeatures; 3553 3554 if ( ! modern ) { 3555 plugin.fnInit( settings, node, redraw ); 3556 } 3557 3558 /* Add a draw callback for the pagination on first instance, to update the paging display */ 3559 if ( ! features.p ) 3560 { 3561 node.id = settings.sTableId+'_paginate'; 3562 3563 settings.aoDrawCallback.push( { 3564 "fn": function( settings ) { 3565 if ( modern ) { 3566 var 3567 start = settings._iDisplayStart, 3568 len = settings._iDisplayLength, 3569 visRecords = settings.fnRecordsDisplay(), 3570 all = len === -1, 3571 page = all ? 0 : Math.ceil( start / len ), 3572 pages = all ? 1 : Math.ceil( visRecords / len ), 3573 buttons = plugin(page, pages), 3574 i, ien; 3575 3576 for ( i=0, ien=features.p.length ; i<ien ; i++ ) { 3577 _fnRenderer( settings, 'pageButton' )( 3578 settings, features.p[i], i, buttons, page, pages 3579 ); 3580 } 3581 } 3582 else { 3583 plugin.fnUpdate( settings, redraw ); 3584 } 3585 }, 3586 "sName": "pagination" 3587 } ); 3588 } 3589 3590 return node; 3591 } 3592 3593 3594 /** 3595 * Alter the display settings to change the page 3596 * @param {object} settings DataTables settings object 3597 * @param {string|int} action Paging action to take: "first", "previous", 3598 * "next" or "last" or page number to jump to (integer) 3599 * @param [bool] redraw Automatically draw the update or not 3600 * @returns {bool} true page has changed, false - no change 3601 * @memberof DataTable#oApi 3602 */ 3603 function _fnPageChange ( settings, action, redraw ) 3604 { 3605 var 3606 start = settings._iDisplayStart, 3607 len = settings._iDisplayLength, 3608 records = settings.fnRecordsDisplay(); 3609 3610 if ( records === 0 || len === -1 ) 3611 { 3612 start = 0; 3613 } 3614 else if ( typeof action === "number" ) 3615 { 3616 start = action * len; 3617 3618 if ( start > records ) 3619 { 3620 start = 0; 3621 } 3622 } 3623 else if ( action == "first" ) 3624 { 3625 start = 0; 3626 } 3627 else if ( action == "previous" ) 3628 { 3629 start = len >= 0 ? 3630 start - len : 3631 0; 3632 3633 if ( start < 0 ) 3634 { 3635 start = 0; 3636 } 3637 } 3638 else if ( action == "next" ) 3639 { 3640 if ( start + len < records ) 3641 { 3642 start += len; 3643 } 3644 } 3645 else if ( action == "last" ) 3646 { 3647 start = Math.floor( (records-1) / len) * len; 3648 } 3649 else 3650 { 3651 _fnLog( settings, 0, "Unknown paging action: "+action, 5 ); 3652 } 3653 3654 var changed = settings._iDisplayStart !== start; 3655 settings._iDisplayStart = start; 3656 3657 if ( changed ) { 3658 _fnCallbackFire( settings, null, 'page', [settings] ); 3659 3660 if ( redraw ) { 3661 _fnDraw( settings ); 3662 } 3663 } 3664 3665 return changed; 3666 } 3667 3668 3669 3670 /** 3671 * Generate the node required for the processing node 3672 * @param {object} settings dataTables settings object 3673 * @returns {node} Processing element 3674 * @memberof DataTable#oApi 3675 */ 3676 function _fnFeatureHtmlProcessing ( settings ) 3677 { 3678 return $('<div/>', { 3679 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null, 3680 'class': settings.oClasses.sProcessing 3681 } ) 3682 .html( settings.oLanguage.sProcessing ) 3683 .insertBefore( settings.nTable )[0]; 3684 } 3685 3686 3687 /** 3688 * Display or hide the processing indicator 3689 * @param {object} settings dataTables settings object 3690 * @param {bool} show Show the processing indicator (true) or not (false) 3691 * @memberof DataTable#oApi 3692 */ 3693 function _fnProcessingDisplay ( settings, show ) 3694 { 3695 if ( settings.oFeatures.bProcessing ) { 3696 $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' ); 3697 } 3698 3699 _fnCallbackFire( settings, null, 'processing', [settings, show] ); 3700 } 3701 3702 /** 3703 * Add any control elements for the table - specifically scrolling 3704 * @param {object} settings dataTables settings object 3705 * @returns {node} Node to add to the DOM 3706 * @memberof DataTable#oApi 3707 */ 3708 function _fnFeatureHtmlTable ( settings ) 3709 { 3710 var table = $(settings.nTable); 3711 3712 // Add the ARIA grid role to the table 3713 table.attr( 'role', 'grid' ); 3714 3715 // Scrolling from here on in 3716 var scroll = settings.oScroll; 3717
3718 if ( scroll.sX === '' && scroll.sY === '' ) { 3719 return settings.nTable; 3720 } 3721 3722 var scrollX = scroll.sX; 3723 var scrollY = scroll.sY; 3724 var classes = settings.oClasses; 3725 var caption = table.children('caption'); 3726 var captionSide = caption.length ? caption[0]._captionSide : null; 3727 var headerClone = $( table[0].cloneNode(false) ); 3728 var footerClone = $( table[0].cloneNode(false) ); 3729 var footer = table.children('tfoot'); 3730 var _div = '<div/>'; 3731 var size = function ( s ) { 3732 return !s ? null : _fnStringToCss( s ); 3733 }; 3734 3735 if ( ! footer.length ) { 3736 footer = null; 3737 } 3738 3739 /* 3740 * The HTML structure that we want to generate in this function is: 3741 * div - scroller 3742 * div - scroll head 3743 * div - scroll head inner 3744 * table - scroll head table 3745 * thead - thead 3746 * div - scroll body 3747 * table - table (master table) 3748 * thead - thead clone for sizing 3749 * tbody - tbody 3750 * div - scroll foot 3751 * div - scroll foot inner 3752 * table - scroll foot table 3753 * tfoot - tfoot 3754 */ 3755 var scroller = $( _div, { 'class': classes.sScrollWrapper } ) 3756 .append( 3757 $(_div, { 'class': classes.sScrollHead } ) 3758 .css( { 3759 overflow: 'hidden', 3760 position: 'relative', 3761 border: 0, 3762 width: scrollX ? size(scrollX) : '100%' 3763 } ) 3764 .append( 3765 $(_div, { 'class': classes.sScrollHeadInner } ) 3766 .css( { 3767 'box-sizing': 'content-box', 3768 width: scroll.sXInner || '100%' 3769 } ) 3770 .append( 3771 headerClone 3772 .removeAttr('id') 3773 .css( 'margin-left', 0 ) 3774 .append( captionSide === 'top' ? caption : null ) 3775 .append( 3776 table.children('thead') 3777 ) 3778 ) 3779 ) 3780 ) 3781 .append( 3782 $(_div, { 'class': classes.sScrollBody } ) 3783 .css( { 3784 position: 'relative', 3785 overflow: 'auto', 3786 width: size( scrollX ) 3787 } ) 3788 .append( table ) 3789 ); 3790 3791 if ( footer ) { 3792 scroller.append( 3793 $(_div, { 'class': classes.sScrollFoot } ) 3794 .css( { 3795 overflow: 'hidden', 3796 border: 0, 3797 width: scrollX ? size(scrollX) : '100%' 3798 } ) 3799 .append( 3800 $(_div, { 'class': classes.sScrollFootInner } ) 3801 .append( 3802 footerClone 3803 .removeAttr('id') 3804 .css( 'margin-left', 0 ) 3805 .append( captionSide === 'bottom' ? caption : null ) 3806 .append( 3807 table.children('tfoot') 3808 ) 3809 ) 3810 ) 3811 ); 3812 } 3813 3814 var children = scroller.children(); 3815 var scrollHead = children[0]; 3816 var scrollBody = children[1]; 3817 var scrollFoot = footer ? children[2] : null; 3818 3819 // When the body is scrolled, then we also want to scroll the headers 3820 if ( scrollX ) { 3821 $(scrollBody).on( 'scroll.DT', function (e) { 3822 var scrollLeft = this.scrollLeft; 3823 3824 scrollHead.scrollLeft = scrollLeft; 3825 3826 if ( footer ) { 3827 scrollFoot.scrollLeft = scrollLeft; 3828 } 3829 } ); 3830 } 3831 3832 $(scrollBody).css( 3833 scrollY && scroll.bCollapse ? 'max-height' : 'height', 3834 scrollY 3835 ); 3836 3837 settings.nScrollHead = scrollHead; 3838 settings.nScrollBody = scrollBody; 3839 settings.nScrollFoot = scrollFoot; 3840 3841 // On redraw - align columns 3842 settings.aoDrawCallback.push( { 3843 "fn": _fnScrollDraw, 3844 "sName": "scrolling" 3845 } ); 3846 3847 return scroller[0]; 3848 } 3849 3850 3851 3852 /** 3853 * Update the header, footer and body tables for resizing - i.e. column 3854 * alignment. 3855 * 3856 * Welcome to the most horrible function DataTables. The process that this 3857 * function follows is basically: 3858 * 1. Re-create the table inside the scrolling div 3859 * 2. Take live measurements from the DOM 3860 * 3. Apply the measurements to align the columns 3861 * 4. Clean up 3862 * 3863 * @param {object} settings dataTables settings object 3864 * @memberof DataTable#oApi 3865 */ 3866 function _fnScrollDraw ( settings ) 3867 { 3868 // Given that this is such a monster function, a lot of variables are use 3869 // to try and keep the minimised size as small as possible 3870 var 3871 scroll = settings.oScroll, 3872 scrollX = scroll.sX, 3873 scrollXInner = scroll.sXInner, 3874 scrollY = scroll.sY, 3875 barWidth = scroll.iBarWidth, 3876 divHeader = $(settings.nScrollHead), 3877 divHeaderStyle = divHeader[0].style, 3878 divHeaderInner = divHeader.children('div'), 3879 divHeaderInnerStyle = divHeaderInner[0].style, 3880 divHeaderTable = divHeaderInner.children('table'), 3881 divBodyEl = settings.nScrollBody,
3882 divBody = $(divBodyEl), 3883 divBodyStyle = divBodyEl.style, 3884 divFooter = $(settings.nScrollFoot), 3885 divFooterInner = divFooter.children('div'), 3886 divFooterTable = divFooterInner.children('table'), 3887 header = $(settings.nTHead), 3888 table = $(settings.nTable), 3889 tableEl = table[0], 3890 tableStyle = tableEl.style, 3891 footer = settings.nTFoot ? $(settings.nTFoot) : null, 3892 browser = settings.oBrowser, 3893 ie67 = browser.bScrollOversize, 3894 dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ), 3895 headerTrgEls, footerTrgEls, 3896 headerSrcEls, footerSrcEls, 3897 headerCopy, footerCopy, 3898 headerWidths=[], footerWidths=[], 3899 headerContent=[], footerContent=[], 3900 idx, correction, sanityWidth, 3901 zeroOut = function(nSizer) { 3902 var style = nSizer.style; 3903 style.paddingTop = "0"; 3904 style.paddingBottom = "0"; 3905 style.borderTopWidth = "0"; 3906 style.borderBottomWidth = "0"; 3907 style.height = 0; 3908 }; 3909 3910 // If the scrollbar visibility has changed from the last draw, we need to 3911 // adjust the column sizes as the table width will have changed to account 3912 // for the scrollbar 3913 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight; 3914 3915 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) { 3916 settings.scrollBarVis = scrollBarVis; 3917 _fnAdjustColumnSizing( settings ); 3918 return; // adjust column sizing will call this function again 3919 } 3920 else { 3921 settings.scrollBarVis = scrollBarVis; 3922 } 3923 3924 /* 3925 * 1. Re-create the table inside the scrolling div 3926 */ 3927 3928 // Remove the old minimised thead and tfoot elements in the inner table 3929 table.children('thead, tfoot').remove(); 3930 3931 if ( footer ) { 3932 footerCopy = footer.clone().prependTo( table ); 3933 footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized 3934 footerSrcEls = footerCopy.find('tr'); 3935 } 3936 3937 // Clone the current header and footer elements and then place it into the inner table 3938 headerCopy = header.clone().prependTo( table ); 3939 headerTrgEls = header.find('tr'); // original header is in its own table 3940 headerSrcEls = headerCopy.find('tr'); 3941 headerCopy.find('th, td').removeAttr('tabindex'); 3942 3943 3944 /* 3945 * 2. Take live measurements from the DOM - do not alter the DOM itself! 3946 */ 3947 3948 // Remove old sizing and apply the calculated column widths 3949 // Get the unique column headers in the newly created (cloned) header. We want to apply the 3950 // calculated sizes to this header 3951 if ( ! scrollX ) 3952 { 3953 divBodyStyle.width = '100%'; 3954 divHeader[0].style.width = '100%'; 3955 } 3956 3957 $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) { 3958 idx = _fnVisibleToColumnIndex( settings, i ); 3959 el.style.width = settings.aoColumns[idx].sWidth; 3960 } ); 3961 3962 if ( footer ) { 3963 _fnApplyToChildren( function(n) { 3964 n.style.width = ""; 3965 }, footerSrcEls ); 3966 } 3967 3968 // Size the table as a whole 3969 sanityWidth = table.outerWidth(); 3970 if ( scrollX === "" ) { 3971 // No x scrolling 3972 tableStyle.width = "100%"; 3973 3974 // IE7 will make the width of the table when 100% include the scrollbar 3975 // - which is shouldn't. When there is a scrollbar we need to take this 3976 // into account. 3977 if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight || 3978 divBody.css('overflow-y') == "scroll") 3979 ) { 3980 tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth); 3981 } 3982 3983 // Recalculate the sanity width 3984 sanityWidth = table.outerWidth(); 3985 } 3986 else if ( scrollXInner !== "" ) { 3987 // legacy x scroll inner has been given - use it 3988 tableStyle.width = _fnStringToCss(scrollXInner); 3989 3990 // Recalculate the sanity width 3991 sanityWidth = table.outerWidth(); 3992 } 3993 3994 // Hidden header should have zero height, so remove padding and borders. Then 3995 // set the width based on the real headers 3996 3997 // Apply all styles in one pass 3998 _fnApplyToChildren( zeroOut, headerSrcEls ); 3999 4000 // Read all widths in next pass 4001 _fnApplyToChildren( function(nSizer) { 4002 headerContent.push( nSizer.innerHTML ); 4003 headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) ); 4004 }, headerSrcEls ); 4005 4006 // Apply all widths in final pass 4007 _fnApplyToChildren( function(nToSize, i) { 4008 // Only apply widths to the DataTables detected header cells - this 4009 // prevents complex headers from having contradictory sizes applied 4010 if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) { 4011 nToSize.style.width = headerWidths[i]; 4012 } 4013 }, headerTrgEls ); 4014 4015 $(headerSrcEls).height(0); 4016 4017 /* Same again with the footer if we have one */ 4018 if ( footer ) 4019 { 4020 _fnApplyToChildren( zeroOut, footerSrcEls ); 4021 4022 _fnApplyToChildren( function(nSizer) { 4023 footerContent.push( nSizer.innerHTML );
4024 footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) ); 4025 }, footerSrcEls ); 4026 4027 _fnApplyToChildren( function(nToSize, i) { 4028 nToSize.style.width = footerWidths[i]; 4029 }, footerTrgEls ); 4030 4031 $(footerSrcEls).height(0); 4032 } 4033 4034 4035 /* 4036 * 3. Apply the measurements 4037 */ 4038 4039 // "Hide" the header and footer that we used for the sizing. We need to keep 4040 // the content of the cell so that the width applied to the header and body 4041 // both match, but we want to hide it completely. We want to also fix their 4042 // width to what they currently are 4043 _fnApplyToChildren( function(nSizer, i) { 4044 nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>'; 4045 nSizer.style.width = headerWidths[i]; 4046 }, headerSrcEls ); 4047 4048 if ( footer ) 4049 { 4050 _fnApplyToChildren( function(nSizer, i) { 4051 nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+footerContent[i]+'</div>'; 4052 nSizer.style.width = footerWidths[i]; 4053 }, footerSrcEls ); 4054 } 4055 4056 // Sanity check that the table is of a sensible width. If not then we are going to get 4057 // misalignment - try to prevent this by not allowing the table to shrink below its min width 4058 if ( table.outerWidth() < sanityWidth ) 4059 { 4060 // The min width depends upon if we have a vertical scrollbar visible or not */ 4061 correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight || 4062 divBody.css('overflow-y') == "scroll")) ? 4063 sanityWidth+barWidth : 4064 sanityWidth; 4065 4066 // IE6/7 are a law unto themselves... 4067 if ( ie67 && (divBodyEl.scrollHeight > 4068 divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll") 4069 ) { 4070 tableStyle.width = _fnStringToCss( correction-barWidth ); 4071 } 4072 4073 // And give the user a warning that we've stopped the table getting too small 4074 if ( scrollX === "" || scrollXInner !== "" ) { 4075 _fnLog( settings, 1, 'Possible column misalignment', 6 ); 4076 } 4077 } 4078 else 4079 { 4080 correction = '100%'; 4081 } 4082 4083 // Apply to the container elements 4084 divBodyStyle.width = _fnStringToCss( correction ); 4085 divHeaderStyle.width = _fnStringToCss( correction ); 4086 4087 if ( footer ) { 4088 settings.nScrollFoot.style.width = _fnStringToCss( correction ); 4089 } 4090 4091 4092 /* 4093 * 4. Clean up 4094 */ 4095 if ( ! scrollY ) { 4096 /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting 4097 * the scrollbar height from the visible display, rather than adding it on. We need to 4098 * set the height in order to sort this. Don't want to do it in any other browsers. 4099 */ 4100 if ( ie67 ) { 4101 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth ); 4102 } 4103 } 4104 4105 /* Finally set the width's of the header and footer tables */ 4106 var iOuterWidth = table.outerWidth(); 4107 divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth ); 4108 divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth ); 4109 4110 // Figure out if there are scrollbar present - if so then we need a the header and footer to 4111 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) 4112 var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll"; 4113 var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' ); 4114 divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px"; 4115 4116 if ( footer ) { 4117 divFooterTable[0].style.width = _fnStringToCss( iOuterWidth ); 4118 divFooterInner[0].style.width = _fnStringToCss( iOuterWidth ); 4119 divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px"; 4120 } 4121 4122 // Correct DOM ordering for colgroup - comes before the thead 4123 table.children('colgroup').insertBefore( table.children('thead') ); 4124 4125 /* Adjust the position of the header in case we loose the y-scrollbar */ 4126 divBody.scroll(); 4127 4128 // If sorting or filtering has occurred, jump the scrolling back to the top 4129 // only if we aren't holding the position 4130 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) { 4131 divBodyEl.scrollTop = 0; 4132 } 4133 } 4134 4135 4136 4137 /** 4138 * Apply a given function to the display child nodes of an element array (typically 4139 * TD children of TR rows 4140 * @param {function} fn Method to apply to the objects 4141 * @param array {nodes} an1 List of elements to look through for display children 4142 * @param array {nodes} an2 Another list (identical structure to the first) - optional 4143 * @memberof DataTable#oApi 4144 */ 4145 function _fnApplyToChildren( fn, an1, an2 ) 4146 { 4147 var index=0, i=0, iLen=an1.length; 4148 var nNode1, nNode2; 4149 4150 while ( i < iLen ) { 4151 nNode1 = an1[i].firstChild; 4152 nNode2 = an2 ? an2[i].firstChild : null; 4153 4154 while ( nNode1 ) { 4155 if ( nNode1.nodeType === 1 ) { 4156 if ( an2 ) { 4157 fn( nNode1, nNode2, index ); 4158 } 4159 else { 4160 fn( nNode1, index ); 4161 } 4162 4163 index++; 4164 } 4165 4166 nNode1 = nNode1.nextSibling; 4167 nNode2 = an2 ? nNode2.nextSibling : null; 4168 } 4169 4170 i++; 4171 } 4172 } 4173 4174 4175 4176 var __re_html_remove = /<.*?>/g; 4177 4178 4179 /** 4180 * Calculate the width of columns for the table 4181 * @param {object} oSettings dataTables settings object 4182 * @memberof DataTable#oApi 4183 */ 4184 function _fnCalculateColumnWidths ( oSettings ) 4185 { 4186 var 4187 table = oSettings.nTable, 4188 columns = oSettings.aoColumns, 4189 scroll = oSettings.oScroll, 4190 scrollY = scroll.sY, 4191 scrollX = scroll.sX, 4192 scrollXInner = scroll.sXInner, 4193 columnCount = columns.length, 4194 visibleColumns = _fnGetColumns( oSettings, 'bVisible' ), 4195 headerCells = $('th', oSettings.nTHead), 4196 tableWidthAttr = table.getAttribute('width'), // from DOM element 4197 tableContainer = table.parentNode, 4198 userInputs = false, 4199 i, column, columnIdx, width, outerWidth, 4200 browser = oSettings.oBrowser, 4201 ie67 = browser.bScrollOversize; 4202 4203 var styleWidth = table.style.width; 4204 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) { 4205 tableWidthAttr = styleWidth; 4206 } 4207 4208 /* Convert any user input sizes into pixel sizes */ 4209 for ( i=0 ; i<visibleColumns.length ; i++ ) { 4210 column = columns[ visibleColumns[i] ]; 4211 4212 if ( column.sWidth !== null ) { 4213 column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer ); 4214 4215 userInputs = true; 4216 } 4217 } 4218 4219 /* If the number of columns in the DOM equals the number that we have to 4220 * process in DataTables, then we can use the offsets that are created by 4221 * the web- browser. No custom sizes can be set in order for this to happen, 4222 * nor scrolling used 4223 */ 4224 if ( ie67 || ! userInputs && ! scrollX && ! scrollY && 4225 columnCount == _fnVisbleColumns( oSettings ) && 4226 columnCount == headerCells.length 4227 ) { 4228 for ( i=0 ; i<columnCount ; i++ ) { 4229 var colIdx = _fnVisibleToColumnIndex( oSettings, i ); 4230 4231 if ( colIdx !== null ) { 4232 columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() ); 4233 } 4234 } 4235 } 4236 else 4237 { 4238 // Otherwise construct a single row, worst case, table with the widest 4239 // node in the data, assign any user defined widths, then insert it into
4240 // the DOM and allow the browser to do all the hard work of calculating 4241 // table widths 4242 var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table 4243 .css( 'visibility', 'hidden' ) 4244 .removeAttr( 'id' ); 4245 4246 // Clean up the table body 4247 tmpTable.find('tbody tr').remove(); 4248 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') ); 4249 4250 // Clone the table header and footer - we can't use the header / footer 4251 // from the cloned table, since if scrolling is active, the table's 4252 // real header and footer are contained in different table tags 4253 tmpTable.find('thead, tfoot').remove(); 4254 tmpTable 4255 .append( $(oSettings.nTHead).clone() ) 4256 .append( $(oSettings.nTFoot).clone() ); 4257 4258 // Remove any assigned widths from the footer (from scrolling) 4259 tmpTable.find('tfoot th, tfoot td').css('width', ''); 4260 4261 // Apply custom sizing to the cloned header 4262 headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] ); 4263 4264 for ( i=0 ; i<visibleColumns.length ; i++ ) { 4265 column = columns[ visibleColumns[i] ]; 4266 4267 headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ? 4268 _fnStringToCss( column.sWidthOrig ) : 4269 ''; 4270 4271 // For scrollX we need to force the column width otherwise the 4272 // browser will collapse it. If this width is smaller than the 4273 // width the column requires, then it will have no effect 4274 if ( column.sWidthOrig && scrollX ) { 4275 $( headerCells[i] ).append( $('<div/>').css( { 4276 width: column.sWidthOrig, 4277 margin: 0, 4278 padding: 0, 4279 border: 0, 4280 height: 1 4281 } ) ); 4282 } 4283 } 4284 4285 // Find the widest cell for each column and put it into the table 4286 if ( oSettings.aoData.length ) { 4287 for ( i=0 ; i<visibleColumns.length ; i++ ) { 4288 columnIdx = visibleColumns[i]; 4289 column = columns[ columnIdx ]; 4290 4291 $( _fnGetWidestNode( oSettings, columnIdx ) ) 4292 .clone( false ) 4293 .append( column.sContentPadding ) 4294 .appendTo( tr ); 4295 } 4296 } 4297 4298 // Tidy the temporary table - remove name attributes so there aren't 4299 // duplicated in the dom (radio elements for example) 4300 $('[name]', tmpTable).removeAttr('name'); 4301 4302 // Table has been built, attach to the document so we can work with it. 4303 // A holding element is used, positioned at the top of the container 4304 // with minimal height, so it has no effect on if the container scrolls 4305 // or not. Otherwise it might trigger scrolling when it actually isn't 4306 // needed 4307 var holder = $('<div/>').css( scrollX || scrollY ? 4308 { 4309 position: 'absolute', 4310 top: 0, 4311 left: 0, 4312 height: 1, 4313 right: 0, 4314 overflow: 'hidden' 4315 } : 4316 {} 4317 ) 4318 .append( tmpTable ) 4319 .appendTo( tableContainer ); 4320 4321 // When scrolling (X or Y) we want to set the width of the table as 4322 // appropriate. However, when not scrolling leave the table width as it 4323 // is. This results in slightly different, but I think correct behaviour 4324 if ( scrollX && scrollXInner ) { 4325 tmpTable.width( scrollXInner ); 4326 } 4327 else if ( scrollX ) { 4328 tmpTable.css( 'width', 'auto' ); 4329 tmpTable.removeAttr('width'); 4330 4331 // If there is no width attribute or style, then allow the table to 4332 // collapse 4333 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) { 4334 tmpTable.width( tableContainer.clientWidth ); 4335 } 4336 } 4337 else if ( scrollY ) { 4338 tmpTable.width( tableContainer.clientWidth ); 4339 } 4340 else if ( tableWidthAttr ) { 4341 tmpTable.width( tableWidthAttr ); 4342 } 4343 4344 // Get the width of each column in the constructed table - we need to 4345 // know the inner width (so it can be assigned to the other table's 4346 // cells) and the outer width so we can calculate the full width of the 4347 // table. This is safe since DataTables requires a unique cell for each 4348 // column, but if ever a header can span multiple columns, this will 4349 // need to be modified. 4350 var total = 0; 4351 for ( i=0 ; i<visibleColumns.length ; i++ ) { 4352 var cell = $(headerCells[i]); 4353 var border = cell.outerWidth() - cell.width(); 4354 4355 // Use getBounding... where possible (not IE8-) because it can give 4356 // sub-pixel accuracy, which we then want to round up! 4357 var bounding = browser.bBounding ? 4358 Math.ceil( headerCells[i].getBoundingClientRect().width ) : 4359 cell.outerWidth(); 4360 4361 // Total is tracked to remove any sub-pixel errors as the outerWidth 4362 // of the table might not equal the total given here (IE!). 4363 total += bounding; 4364
4365 // Width for each column to use 4366 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border ); 4367 } 4368 4369 table.style.width = _fnStringToCss( total ); 4370 4371 // Finished with the table - ditch it 4372 holder.remove(); 4373 } 4374 4375 // If there is a width attr, we want to attach an event listener which 4376 // allows the table sizing to automatically adjust when the window is 4377 // resized. Use the width attr rather than CSS, since we can't know if the 4378 // CSS is a relative value or absolute - DOM read is always px. 4379 if ( tableWidthAttr ) { 4380 table.style.width = _fnStringToCss( tableWidthAttr ); 4381 } 4382 4383 if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) { 4384 var bindResize = function () { 4385 $(window).bind('resize.DT-'+oSettings.sInstance, _fnThrottle( function () { 4386 _fnAdjustColumnSizing( oSettings ); 4387 } ) ); 4388 }; 4389 4390 // IE6/7 will crash if we bind a resize event handler on page load. 4391 // To be removed in 1.11 which drops IE6/7 support 4392 if ( ie67 ) { 4393 setTimeout( bindResize, 1000 ); 4394 } 4395 else { 4396 bindResize(); 4397 } 4398 4399 oSettings._reszEvt = true; 4400 } 4401 } 4402 4403 4404 /** 4405 * Throttle the calls to a function. Arguments and context are maintained for 4406 * the throttled function 4407 * @param {function} fn Function to be called 4408 * @param {int} [freq=200] call frequency in mS 4409 * @returns {function} wrapped function 4410 * @memberof DataTable#oApi 4411 */ 4412 function _fnThrottle( fn, freq ) { 4413 var 4414 frequency = freq !== undefined ? freq : 200, 4415 last, 4416 timer; 4417 4418 return function () { 4419 var 4420 that = this, 4421 now = +new Date(), 4422 args = arguments; 4423 4424 if ( last && now < last + frequency ) { 4425 clearTimeout( timer ); 4426 4427 timer = setTimeout( function () { 4428 last = undefined; 4429 fn.apply( that, args ); 4430 }, frequency ); 4431 } 4432 else { 4433 last = now; 4434 fn.apply( that, args ); 4435 } 4436 }; 4437 } 4438 4439 4440 /** 4441 * Convert a CSS unit width to pixels (e.g. 2em) 4442 * @param {string} width width to be converted 4443 * @param {node} parent parent to get the with for (required for relative widths) - optional 4444 * @returns {int} width in pixels 4445 * @memberof DataTable#oApi 4446 */ 4447 function _fnConvertToWidth ( width, parent ) 4448 { 4449 if ( ! width ) { 4450 return 0; 4451 } 4452 4453 var n = $('<div/>') 4454 .css( 'width', _fnStringToCss( width ) ) 4455 .appendTo( parent || document.body ); 4456 4457 var val = n[0].offsetWidth; 4458 n.remove(); 4459 4460 return val; 4461 } 4462 4463 4464 /** 4465 * Get the widest node 4466 * @param {object} settings dataTables settings object 4467 * @param {int} colIdx column of interest 4468 * @returns {node} widest table node 4469 * @memberof DataTable#oApi 4470 */ 4471 function _fnGetWidestNode( settings, colIdx ) 4472 { 4473 var idx = _fnGetMaxLenString( settings, colIdx ); 4474 if ( idx < 0 ) { 4475 return null; 4476 } 4477 4478 var data = settings.aoData[ idx ]; 4479 return ! data.nTr ? // Might not have been created when deferred rendering 4480 $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] : 4481 data.anCells[ colIdx ]; 4482 } 4483 4484 4485 /** 4486 * Get the maximum strlen for each data column 4487 * @param {object} settings dataTables settings object 4488 * @param {int} colIdx column of interest 4489 * @returns {string} max string length for each column 4490 * @memberof DataTable#oApi 4491 */ 4492 function _fnGetMaxLenString( settings, colIdx ) 4493 { 4494 var s, max=-1, maxIdx = -1; 4495 4496 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 4497 s = _fnGetCellData( settings, i, colIdx, 'display' )+''; 4498 s = s.replace( __re_html_remove, '' ); 4499 s = s.replace( / /g, ' ' ); 4500 4501 if ( s.length > max ) { 4502 max = s.length; 4503 maxIdx = i; 4504 } 4505 } 4506 4507 return maxIdx; 4508 } 4509 4510 4511 /** 4512 * Append a CSS unit (only if required) to a string 4513 * @param {string} value to css-ify 4514 * @returns {string} value with css unit 4515 * @memberof DataTable#oApi 4516 */ 4517 function _fnStringToCss( s ) 4518 { 4519 if ( s === null ) { 4520 return '0px'; 4521 } 4522 4523 if ( typeof s == 'number' ) { 4524 return s < 0 ? 4525 '0px' : 4526 s+'px'; 4527 } 4528 4529 // Check it has a unit character already 4530 return s.match(/\d$/) ? 4531 s+'px' : 4532 s; 4533 } 4534 4535 4536 4537 function _fnSortFlatten ( settings ) 4538 { 4539 var 4540 i, iLen, k, kLen, 4541 aSort = [], 4542 aiOrig = [], 4543 aoColumns = settings.aoColumns, 4544 aDataSort, iCol, sType, srcCol, 4545 fixed = settings.aaSortingFixed, 4546 fixedObj = $.isPlainObject( fixed ), 4547 nestedSort = [], 4548 add = function ( a ) { 4549 if ( a.length && ! $.isArray( a[0] ) ) { 4550 // 1D array 4551 nestedSort.push( a ); 4552 } 4553 else { 4554 // 2D array 4555 $.merge( nestedSort, a ); 4556 } 4557 }; 4558 4559 // Build the sort array, with pre-fix and post-fix options if they have been 4560 // specified 4561 if ( $.isArray( fixed ) ) { 4562 add( fixed ); 4563 } 4564 4565 if ( fixedObj && fixed.pre ) { 4566 add( fixed.pre ); 4567 } 4568 4569 add( settings.aaSorting ); 4570 4571 if (fixedObj && fixed.post ) { 4572 add( fixed.post ); 4573 } 4574 4575 for ( i=0 ; i<nestedSort.length ; i++ ) 4576 { 4577 srcCol = nestedSort[i][0]; 4578 aDataSort = aoColumns[ srcCol ].aDataSort; 4579 4580 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ ) 4581 { 4582 iCol = aDataSort[k]; 4583 sType = aoColumns[ iCol ].sType || 'string'; 4584 4585 if ( nestedSort[i]._idx === undefined ) { 4586 nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting ); 4587 } 4588 4589 aSort.push( { 4590 src: srcCol, 4591 col: iCol, 4592 dir: nestedSort[i][1], 4593 index: nestedSort[i]._idx, 4594 type: sType, 4595 formatter: DataTable.ext.type.order[ sType+"-pre" ] 4596 } ); 4597 } 4598 } 4599 4600 return aSort; 4601 } 4602 4603 /** 4604 * Change the order of the table 4605 * @param {object} oSettings dataTables settings object 4606 * @memberof DataTable#oApi 4607 * @todo This really needs split up! 4608 */ 4609 function _fnSort ( oSettings ) 4610 { 4611 var 4612 i, ien, iLen, j, jLen, k, kLen, 4613 sDataType, nTh, 4614 aiOrig = [], 4615 oExtSort = DataTable.ext.type.order, 4616 aoData = oSettings.aoData, 4617 aoColumns = oSettings.aoColumns, 4618 aDataSort, data, iCol, sType, oSort, 4619 formatters = 0, 4620 sortCol, 4621 displayMaster = oSettings.aiDisplayMaster, 4622 aSort; 4623 4624 // Resolve any column types that are unknown due to addition or invalidation 4625 // @todo Can this be moved into a 'data-ready' handler which is called when 4626 // data is going to be used in the table? 4627 _fnColumnTypes( oSettings ); 4628 4629 aSort = _fnSortFlatten( oSettings ); 4630 4631 for ( i=0, ien=aSort.length ; i<ien ; i++ ) { 4632 sortCol = aSort[i]; 4633 4634 // Track if we can use the fast sort algorithm 4635 if ( sortCol.formatter ) { 4636 formatters++; 4637 } 4638 4639 // Load the data needed for the sort, for each cell 4640 _fnSortData( oSettings, sortCol.col ); 4641 } 4642 4643 /* No sorting required if server-side or no sorting array */ 4644 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 ) 4645 { 4646 // Create a value - key array of the current row positions such that we can use their 4647 // current position during the sort, if values match, in order to perform stable sorting 4648 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) { 4649 aiOrig[ displayMaster[i] ] = i; 4650 } 4651
4652 /* Do the sort - here we want multi-column sorting based on a given data source (column) 4653 * and sorting function (from oSort) in a certain direction. It's reasonably complex to 4654 * follow on it's own, but this is what we want (example two column sorting): 4655 * fnLocalSorting = function(a,b){ 4656 * var iTest; 4657 * iTest = oSort['string-asc']('data11', 'data12'); 4658 * if (iTest !== 0) 4659 * return iTest; 4660 * iTest = oSort['numeric-desc']('data21', 'data22'); 4661 * if (iTest !== 0) 4662 * return iTest; 4663 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); 4664 * } 4665 * Basically we have a test for each sorting column, if the data in that column is equal, 4666 * test the next column. If all columns match, then we use a numeric sort on the row 4667 * positions in the original data array to provide a stable sort. 4668 * 4669 * Note - I know it seems excessive to have two sorting methods, but the first is around 4670 * 15% faster, so the second is only maintained for backwards compatibility with sorting 4671 * methods which do not have a pre-sort formatting function. 4672 */ 4673 if ( formatters === aSort.length ) { 4674 // All sort types have formatting functions 4675 displayMaster.sort( function ( a, b ) { 4676 var 4677 x, y, k, test, sort, 4678 len=aSort.length, 4679 dataA = aoData[a]._aSortData, 4680 dataB = aoData[b]._aSortData; 4681 4682 for ( k=0 ; k<len ; k++ ) { 4683 sort = aSort[k]; 4684 4685 x = dataA[ sort.col ]; 4686 y = dataB[ sort.col ]; 4687 4688 test = x<y ? -1 : x>y ? 1 : 0; 4689 if ( test !== 0 ) { 4690 return sort.dir === 'asc' ? test : -test; 4691 } 4692 } 4693 4694 x = aiOrig[a]; 4695 y = aiOrig[b]; 4696 return x<y ? -1 : x>y ? 1 : 0; 4697 } ); 4698 } 4699 else { 4700 // Depreciated - remove in 1.11 (providing a plug-in option) 4701 // Not all sort types have formatting methods, so we have to call their sorting 4702 // methods. 4703 displayMaster.sort( function ( a, b ) { 4704 var 4705 x, y, k, l, test, sort, fn, 4706 len=aSort.length, 4707 dataA = aoData[a]._aSortData, 4708 dataB = aoData[b]._aSortData; 4709 4710 for ( k=0 ; k<len ; k++ ) { 4711 sort = aSort[k]; 4712 4713 x = dataA[ sort.col ]; 4714 y = dataB[ sort.col ]; 4715 4716 fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ]; 4717 test = fn( x, y ); 4718 if ( test !== 0 ) { 4719 return test; 4720 } 4721 } 4722 4723 x = aiOrig[a]; 4724 y = aiOrig[b]; 4725 return x<y ? -1 : x>y ? 1 : 0; 4726 } ); 4727 } 4728 } 4729 4730 /* Tell the draw function that we have sorted the data */ 4731 oSettings.bSorted = true; 4732 } 4733 4734 4735 function _fnSortAria ( settings ) 4736 { 4737 var label; 4738 var nextSort; 4739 var columns = settings.aoColumns; 4740 var aSort = _fnSortFlatten( settings ); 4741 var oAria = settings.oLanguage.oAria; 4742 4743 // ARIA attributes - need to loop all columns, to update all (removing old 4744 // attributes as needed) 4745 for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) 4746 { 4747 var col = columns[i]; 4748 var asSorting = col.asSorting; 4749 var sTitle = col.sTitle.replace( /<.*?>/g, "" ); 4750 var th = col.nTh; 4751 4752 // IE7 is throwing an error when setting these properties with jQuery's 4753 // attr() and removeAttr() methods... 4754 th.removeAttribute('aria-sort'); 4755 4756 /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */ 4757 if ( col.bSortable ) { 4758 if ( aSort.length > 0 && aSort[0].col == i ) { 4759 th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" ); 4760 nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0]; 4761 } 4762 else { 4763 nextSort = asSorting[0]; 4764 } 4765 4766 label = sTitle + ( nextSort === "asc" ? 4767 oAria.sSortAscending : 4768 oAria.sSortDescending 4769 ); 4770 } 4771 else { 4772 label = sTitle; 4773 } 4774 4775 th.setAttribute('aria-label', label); 4776 } 4777 } 4778 4779 4780 /** 4781 * Function to run on user sort request 4782 * @param {object} settings dataTables settings object 4783 * @param {node} attachTo node to attach the handler to 4784 * @param {int} colIdx column sorting index 4785 * @param {boolean} [append=false] Append the requested sort to the existing
4786 * sort if true (i.e. multi-column sort) 4787 * @param {function} [callback] callback function 4788 * @memberof DataTable#oApi 4789 */ 4790 function _fnSortListener ( settings, colIdx, append, callback ) 4791 { 4792 var col = settings.aoColumns[ colIdx ]; 4793 var sorting = settings.aaSorting; 4794 var asSorting = col.asSorting; 4795 var nextSortIdx; 4796 var next = function ( a, overflow ) { 4797 var idx = a._idx; 4798 if ( idx === undefined ) { 4799 idx = $.inArray( a[1], asSorting ); 4800 } 4801 4802 return idx+1 < asSorting.length ? 4803 idx+1 : 4804 overflow ? 4805 null : 4806 0; 4807 }; 4808 4809 // Convert to 2D array if needed 4810 if ( typeof sorting[0] === 'number' ) { 4811 sorting = settings.aaSorting = [ sorting ]; 4812 } 4813 4814 // If appending the sort then we are multi-column sorting 4815 if ( append && settings.oFeatures.bSortMulti ) { 4816 // Are we already doing some kind of sort on this column? 4817 var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') ); 4818 4819 if ( sortIdx !== -1 ) { 4820 // Yes, modify the sort 4821 nextSortIdx = next( sorting[sortIdx], true ); 4822 4823 if ( nextSortIdx === null && sorting.length === 1 ) { 4824 nextSortIdx = 0; // can't remove sorting completely 4825 } 4826 4827 if ( nextSortIdx === null ) { 4828 sorting.splice( sortIdx, 1 ); 4829 } 4830 else { 4831 sorting[sortIdx][1] = asSorting[ nextSortIdx ]; 4832 sorting[sortIdx]._idx = nextSortIdx; 4833 } 4834 } 4835 else { 4836 // No sort on this column yet 4837 sorting.push( [ colIdx, asSorting[0], 0 ] ); 4838 sorting[sorting.length-1]._idx = 0; 4839 } 4840 } 4841 else if ( sorting.length && sorting[0][0] == colIdx ) { 4842 // Single column - already sorting on this column, modify the sort 4843 nextSortIdx = next( sorting[0] ); 4844 4845 sorting.length = 1; 4846 sorting[0][1] = asSorting[ nextSortIdx ]; 4847 sorting[0]._idx = nextSortIdx; 4848 } 4849 else { 4850 // Single column - sort only on this column 4851 sorting.length = 0; 4852 sorting.push( [ colIdx, asSorting[0] ] ); 4853 sorting[0]._idx = 0; 4854 } 4855 4856 // Run the sort by calling a full redraw 4857 _fnReDraw( settings ); 4858 4859 // callback used for async user interaction 4860 if ( typeof callback == 'function' ) { 4861 callback( settings ); 4862 } 4863 } 4864 4865 4866 /** 4867 * Attach a sort handler (click) to a node 4868 * @param {object} settings dataTables settings object 4869 * @param {node} attachTo node to attach the handler to 4870 * @param {int} colIdx column sorting index 4871 * @param {function} [callback] callback function 4872 * @memberof DataTable#oApi 4873 */ 4874 function _fnSortAttachListener ( settings, attachTo, colIdx, callback ) 4875 { 4876 var col = settings.aoColumns[ colIdx ]; 4877 4878 _fnBindAction( attachTo, {}, function (e) { 4879 /* If the column is not sortable - don't to anything */ 4880 if ( col.bSortable === false ) { 4881 return; 4882 } 4883 4884 // If processing is enabled use a timeout to allow the processing 4885 // display to be shown - otherwise to it synchronously 4886 if ( settings.oFeatures.bProcessing ) { 4887 _fnProcessingDisplay( settings, true ); 4888 4889 setTimeout( function() { 4890 _fnSortListener( settings, colIdx, e.shiftKey, callback ); 4891 4892 // In server-side processing, the draw callback will remove the 4893 // processing display 4894 if ( _fnDataSource( settings ) !== 'ssp' ) { 4895 _fnProcessingDisplay( settings, false ); 4896 } 4897 }, 0 ); 4898 } 4899 else { 4900 _fnSortListener( settings, colIdx, e.shiftKey, callback ); 4901 } 4902 } ); 4903 } 4904 4905 4906 /** 4907 * Set the sorting classes on table's body, Note: it is safe to call this function 4908 * when bSort and bSortClasses are false 4909 * @param {object} oSettings dataTables settings object 4910 * @memberof DataTable#oApi 4911 */ 4912 function _fnSortingClasses( settings ) 4913 { 4914 var oldSort = settings.aLastSort; 4915 var sortClass = settings.oClasses.sSortColumn; 4916 var sort = _fnSortFlatten( settings ); 4917 var features = settings.oFeatures; 4918 var i, ien, colIdx; 4919 4920 if ( features.bSort && features.bSortClasses ) { 4921 // Remove old sorting classes 4922 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) { 4923 colIdx = oldSort[i].src; 4924 4925 // Remove column sorting 4926 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 4927 .removeClass( sortClass + (i<2 ? i+1 : 3) ); 4928 } 4929 4930 // Add new column sorting 4931 for ( i=0, ien=sort.length ; i<ien ; i++ ) { 4932 colIdx = sort[i].src; 4933 4934 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 4935 .addClass( sortClass + (i<2 ? i+1 : 3) ); 4936 } 4937 } 4938 4939 settings.aLastSort = sort; 4940 } 4941 4942 4943 // Get the data to sort a column, be it from cache, fresh (populating the 4944 // cache), or from a sort formatter 4945 function _fnSortData( settings, idx ) 4946 { 4947 // Custom sorting function - provided by the sort data type 4948 var column = settings.aoColumns[ idx ]; 4949 var customSort = DataTable.ext.order[ column.sSortDataType ]; 4950 var customData; 4951 4952 if ( customSort ) { 4953 customData = customSort.call( settings.oInstance, settings, idx, 4954 _fnColumnIndexToVisible( settings, idx ) 4955 ); 4956 } 4957 4958 // Use / populate cache 4959 var row, cellData; 4960 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ]; 4961 4962 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 4963 row = settings.aoData[i]; 4964 4965 if ( ! row._aSortData ) { 4966 row._aSortData = []; 4967 } 4968 4969 if ( ! row._aSortData[idx] || customSort ) { 4970 cellData = customSort ? 4971 customData[i] : // If there was a custom sort function, use data from there 4972 _fnGetCellData( settings, i, idx, 'sort' ); 4973 4974 row._aSortData[ idx ] = formatter ? 4975 formatter( cellData ) : 4976 cellData; 4977 } 4978 } 4979 } 4980 4981 4982 4983 /** 4984 * Save the state of a table 4985 * @param {object} oSettings dataTables settings object 4986 * @memberof DataTable#oApi 4987 */ 4988 function _fnSaveState ( settings ) 4989 { 4990 if ( !settings.oFeatures.bStateSave || settings.bDestroying ) 4991 { 4992 return; 4993 } 4994 4995 /* Store the interesting variables */ 4996 var state = { 4997 time: +new Date(), 4998 start: settings._iDisplayStart, 4999 length: settings._iDisplayLength, 5000 order: $.extend( true, [], settings.aaSorting ), 5001 search: _fnSearchToCamel( settings.oPreviousSearch ),
5002 columns: $.map( settings.aoColumns, function ( col, i ) { 5003 return { 5004 visible: col.bVisible, 5005 search: _fnSearchToCamel( settings.aoPreSearchCols[i] ) 5006 }; 5007 } ) 5008 }; 5009 5010 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] ); 5011 5012 settings.oSavedState = state; 5013 settings.fnStateSaveCallback.call( settings.oInstance, settings, state ); 5014 } 5015 5016 5017 /** 5018 * Attempt to load a saved table state 5019 * @param {object} oSettings dataTables settings object 5020 * @param {object} oInit DataTables init object so we can override settings 5021 * @memberof DataTable#oApi 5022 */ 5023 function _fnLoadState ( settings, oInit ) 5024 { 5025 var i, ien; 5026 var columns = settings.aoColumns; 5027 5028 if ( ! settings.oFeatures.bStateSave ) { 5029 return; 5030 } 5031 5032 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings ); 5033 if ( ! state || ! state.time ) { 5034 return; 5035 } 5036 5037 /* Allow custom and plug-in manipulation functions to alter the saved data set and 5038 * cancelling of loading by returning false 5039 */ 5040 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] ); 5041 if ( $.inArray( false, abStateLoad ) !== -1 ) { 5042 return; 5043 } 5044 5045 /* Reject old data */ 5046 var duration = settings.iStateDuration; 5047 if ( duration > 0 && state.time < +new Date() - (duration*1000) ) { 5048 return; 5049 } 5050 5051 // Number of columns have changed - all bets are off, no restore of settings 5052 if ( columns.length !== state.columns.length ) { 5053 return; 5054 } 5055 5056 // Store the saved state so it might be accessed at any time 5057 settings.oLoadedState = $.extend( true, {}, state ); 5058 5059 // Restore key features - todo - for 1.11 this needs to be done by 5060 // subscribed events 5061 if ( state.start !== undefined ) { 5062 settings._iDisplayStart = state.start; 5063 settings.iInitDisplayStart = state.start; 5064 } 5065 if ( state.length !== undefined ) { 5066 settings._iDisplayLength = state.length; 5067 } 5068 5069 // Order 5070 if ( state.order !== undefined ) { 5071 settings.aaSorting = []; 5072 $.each( state.order, function ( i, col ) { 5073 settings.aaSorting.push( col[0] >= columns.length ? 5074 [ 0, col[1] ] : 5075 col 5076 ); 5077 } ); 5078 } 5079 5080 // Search 5081 if ( state.search !== undefined ) { 5082 $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) ); 5083 } 5084 5085 // Columns 5086 for ( i=0, ien=state.columns.length ; i<ien ; i++ ) { 5087 var col = state.columns[i]; 5088 5089 // Visibility 5090 if ( col.visible !== undefined ) { 5091 columns[i].bVisible = col.visible; 5092 } 5093 5094 // Search 5095 if ( col.search !== undefined ) { 5096 $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) ); 5097 } 5098 } 5099 5100 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] ); 5101 } 5102 5103 5104 /** 5105 * Return the settings object for a particular table 5106 * @param {node} table table we are using as a dataTable 5107 * @returns {object} Settings object - or null if not found 5108 * @memberof DataTable#oApi 5109 */ 5110 function _fnSettingsFromNode ( table ) 5111 { 5112 var settings = DataTable.settings; 5113 var idx = $.inArray( table, _pluck( settings, 'nTable' ) ); 5114 5115 return idx !== -1 ? 5116 settings[ idx ] : 5117 null; 5118 } 5119 5120 5121 /** 5122 * Log an error message 5123 * @param {object} settings dataTables settings object 5124 * @param {int} level log error messages, or display them to the user 5125 * @param {string} msg error message 5126 * @param {int} tn Technical note id to get more information about the error. 5127 * @memberof DataTable#oApi 5128 */ 5129 function _fnLog( settings, level, msg, tn ) 5130 { 5131 msg = 'DataTables warning: '+ 5132 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg; 5133 5134 if ( tn ) { 5135 msg += '. For more information about this error, please see '+ 5136 'http://datatables.net/tn/'+tn; 5137 } 5138 5139 if ( ! level ) { 5140 // Backwards compatibility pre 1.10 5141 var ext = DataTable.ext; 5142 var type = ext.sErrMode || ext.errMode; 5143 5144 if ( settings ) { 5145 _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] ); 5146 } 5147 5148 if ( type == 'alert' ) { 5149 alert( msg ); 5150 } 5151 else if ( type == 'throw' ) { 5152 throw new Error(msg); 5153 } 5154 else if ( typeof type == 'function' ) { 5155 type( settings, tn, msg ); 5156 } 5157 } 5158 else if ( window.console && console.log ) { 5159 console.log( msg ); 5160 } 5161 } 5162 5163 5164 /** 5165 * See if a property is defined on one object, if so assign it to the other object 5166 * @param {object} ret target object 5167 * @param {object} src source object 5168 * @param {string} name property 5169 * @param {string} [mappedName] name to map too - optional, name used if not given 5170 * @memberof DataTable#oApi 5171 */ 5172 function _fnMap( ret, src, name, mappedName ) 5173 { 5174 if ( $.isArray( name ) ) { 5175 $.each( name, function (i, val) { 5176 if ( $.isArray( val ) ) { 5177 _fnMap( ret, src, val[0], val[1] ); 5178 } 5179 else { 5180 _fnMap( ret, src, val ); 5181 } 5182 } ); 5183 5184 return; 5185 } 5186 5187 if ( mappedName === undefined ) { 5188 mappedName = name; 5189 } 5190 5191 if ( src[name] !== undefined ) { 5192 ret[mappedName] = src[name]; 5193 } 5194 } 5195 5196 5197 /** 5198 * Extend objects - very similar to jQuery.extend, but deep copy objects, and 5199 * shallow copy arrays. The reason we need to do this, is that we don't want to 5200 * deep copy array init values (such as aaSorting) since the dev wouldn't be 5201 * able to override them, but we do want to deep copy arrays. 5202 * @param {object} out Object to extend 5203 * @param {object} extender Object from which the properties will be applied to 5204 * out 5205 * @param {boolean} breakRefs If true, then arrays will be sliced to take an 5206 * independent copy with the exception of the `data` or `aaData` parameters 5207 * if they are present. This is so you can pass in a collection to 5208 * DataTables and have that used as your data source without breaking the 5209 * references 5210 * @returns {object} out Reference, just for convenience - out === the return. 5211 * @memberof DataTable#oApi 5212 * @todo This doesn't take account of arrays inside the deep copied objects. 5213 */ 5214 function _fnExtend( out, extender, breakRefs ) 5215 { 5216 var val; 5217 5218 for ( var prop in extender ) { 5219 if ( extender.hasOwnProperty(prop) ) { 5220 val = extender[prop]; 5221 5222 if ( $.isPlainObject( val ) ) { 5223 if ( ! $.isPlainObject( out[prop] ) ) { 5224 out[prop] = {}; 5225 } 5226 $.extend( true, out[prop], val ); 5227 } 5228 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) { 5229 out[prop] = val.slice(); 5230 } 5231 else { 5232 out[prop] = val; 5233 } 5234 } 5235 } 5236 5237 return out; 5238 } 5239 5240 5241 /** 5242 * Bind an event handers to allow a click or return key to activate the callback. 5243 * This is good for accessibility since a return on the keyboard will have the 5244 * same effect as a click, if the element has focus. 5245 * @param {element} n Element to bind the action to 5246 * @param {object} oData Data object to pass to the triggered function 5247 * @param {function} fn Callback function for when the event is triggered 5248 * @memberof DataTable#oApi 5249 */ 5250 function _fnBindAction( n, oData, fn ) 5251 { 5252 $(n) 5253 .bind( 'click.DT', oData, function (e) { 5254 n.blur(); // Remove focus outline for mouse users 5255 fn(e); 5256 } ) 5257 .bind( 'keypress.DT', oData, function (e){ 5258 if ( e.which === 13 ) { 5259 e.preventDefault(); 5260 fn(e); 5261 } 5262 } ) 5263 .bind( 'selectstart.DT', function () { 5264 /* Take the brutal approach to cancelling text selection */ 5265 return false;
5266 } ); 5267 } 5268 5269 5270 /** 5271 * Register a callback function. Easily allows a callback function to be added to 5272 * an array store of callback functions that can then all be called together. 5273 * @param {object} oSettings dataTables settings object 5274 * @param {string} sStore Name of the array storage for the callbacks in oSettings 5275 * @param {function} fn Function to be called back 5276 * @param {string} sName Identifying name for the callback (i.e. a label) 5277 * @memberof DataTable#oApi 5278 */ 5279 function _fnCallbackReg( oSettings, sStore, fn, sName ) 5280 { 5281 if ( fn ) 5282 { 5283 oSettings[sStore].push( { 5284 "fn": fn, 5285 "sName": sName 5286 } ); 5287 } 5288 } 5289 5290 5291 /** 5292 * Fire callback functions and trigger events. Note that the loop over the 5293 * callback array store is done backwards! Further note that you do not want to 5294 * fire off triggers in time sensitive applications (for example cell creation) 5295 * as its slow. 5296 * @param {object} settings dataTables settings object 5297 * @param {string} callbackArr Name of the array storage for the callbacks in 5298 * oSettings 5299 * @param {string} eventName Name of the jQuery custom event to trigger. If 5300 * null no trigger is fired 5301 * @param {array} args Array of arguments to pass to the callback function / 5302 * trigger 5303 * @memberof DataTable#oApi 5304 */ 5305 function _fnCallbackFire( settings, callbackArr, eventName, args ) 5306 { 5307 var ret = []; 5308 5309 if ( callbackArr ) { 5310 ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) { 5311 return val.fn.apply( settings.oInstance, args ); 5312 } ); 5313 } 5314 5315 if ( eventName !== null ) { 5316 var e = $.Event( eventName+'.dt' ); 5317 5318 $(settings.nTable).trigger( e, args ); 5319 5320 ret.push( e.result ); 5321 } 5322 5323 return ret; 5324 } 5325 5326 5327 function _fnLengthOverflow ( settings ) 5328 { 5329 var 5330 start = settings._iDisplayStart, 5331 end = settings.fnDisplayEnd(), 5332 len = settings._iDisplayLength; 5333 5334 /* If we have space to show extra rows (backing up from the end point - then do so */ 5335 if ( start >= end ) 5336 { 5337 start = end - len; 5338 } 5339 5340 // Keep the start record on the current page 5341 start -= (start % len); 5342 5343 if ( len === -1 || start < 0 ) 5344 { 5345 start = 0; 5346 } 5347 5348 settings._iDisplayStart = start; 5349 } 5350 5351 5352 function _fnRenderer( settings, type ) 5353 { 5354 var renderer = settings.renderer; 5355 var host = DataTable.ext.renderer[type]; 5356 5357 if ( $.isPlainObject( renderer ) && renderer[type] ) { 5358 // Specific renderer for this type. If available use it, otherwise use 5359 // the default. 5360 return host[renderer[type]] || host._; 5361 } 5362 else if ( typeof renderer === 'string' ) { 5363 // Common renderer - if there is one available for this type use it, 5364 // otherwise use the default 5365 return host[renderer] || host._; 5366 } 5367 5368 // Use the default 5369 return host._; 5370 } 5371 5372 5373 /** 5374 * Detect the data source being used for the table. Used to simplify the code 5375 * a little (ajax) and to make it compress a little smaller. 5376 * 5377 * @param {object} settings dataTables settings object 5378 * @returns {string} Data source 5379 * @memberof DataTable#oApi 5380 */ 5381 function _fnDataSource ( settings ) 5382 { 5383 if ( settings.oFeatures.bServerSide ) { 5384 return 'ssp'; 5385 } 5386 else if ( settings.ajax || settings.sAjaxSource ) { 5387 return 'ajax'; 5388 } 5389 return 'dom'; 5390 } 5391 5392 5393 DataTable = function( options ) 5394 { 5395 /** 5396 * Perform a jQuery selector action on the table's TR elements (from the tbody) and 5397 * return the resulting jQuery object. 5398 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on 5399 * @param {object} [oOpts] Optional parameters for modifying the rows to be included 5400 * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter 5401 * criterion ("applied") or all TR elements (i.e. no filter). 5402 * @param {string} [oOpts.order=current] Order of the TR elements in the processed array. 5403 * Can be either 'current', whereby the current sorting of the table is used, or 5404 * 'original' whereby the original order the data was read into the table is used. 5405 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page 5406 * ("current") or not ("all"). If 'current' is given, then order is assumed to be 5407 * 'current' and filter is 'applied', regardless of what they might be given as. 5408 * @returns {object} jQuery object, filtered by the given selector. 5409 * @dtopt API 5410 * @deprecated Since v1.10 5411 * 5412 * @example 5413 * $(document).ready(function() { 5414 * var oTable = $('#example').dataTable(); 5415 * 5416 * // Highlight every second row 5417 * oTable.$('tr:odd').css('backgroundColor', 'blue'); 5418 * } ); 5419 * 5420 * @example 5421 * $(document).ready(function() { 5422 * var oTable = $('#example').dataTable(); 5423 * 5424 * // Filter to rows with 'Webkit' in them, add a background colour and then 5425 * // remove the filter, thus highlighting the 'Webkit' rows only. 5426 * oTable.fnFilter('Webkit'); 5427 * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue'); 5428 * oTable.fnFilter(''); 5429 * } ); 5430 */ 5431 this.$ = function ( sSelector, oOpts ) 5432 { 5433 return this.api(true).$( sSelector, oOpts ); 5434 }; 5435
5436 5437 /** 5438 * Almost identical to $ in operation, but in this case returns the data for the matched 5439 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes 5440 * rather than any descendants, so the data can be obtained for the row/cell. If matching 5441 * rows are found, the data returned is the original data array/object that was used to 5442 * create the row (or a generated array if from a DOM source). 5443 * 5444 * This method is often useful in-combination with $ where both functions are given the 5445 * same parameters and the array indexes will match identically. 5446 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on 5447 * @param {object} [oOpts] Optional parameters for modifying the rows to be included 5448 * @param {string} [oOpts.filter=none] Select elements that meet the current filter 5449 * criterion ("applied") or all elements (i.e. no filter). 5450 * @param {string} [oOpts.order=current] Order of the data in the processed array. 5451 * Can be either 'current', whereby the current sorting of the table is used, or 5452 * 'original' whereby the original order the data was read into the table is used. 5453 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page 5454 * ("current") or not ("all"). If 'current' is given, then order is assumed to be 5455 * 'current' and filter is 'applied', regardless of what they might be given as. 5456 * @returns {array} Data for the matched elements. If any elements, as a result of the 5457 * selector, were not TR, TD or TH elements in the DataTable, they will have a null 5458 * entry in the array. 5459 * @dtopt API 5460 * @deprecated Since v1.10 5461 * 5462 * @example 5463 * $(document).ready(function() { 5464 * var oTable = $('#example').dataTable(); 5465 * 5466 * // Get the data from the first row in the table 5467 * var data = oTable._('tr:first'); 5468 * 5469 * // Do something useful with the data 5470 * alert( "First cell is: "+data[0] ); 5471 * } ); 5472 * 5473 * @example 5474 * $(document).ready(function() { 5475 * var oTable = $('#example').dataTable(); 5476 * 5477 * // Filter to 'Webkit' and get all data for 5478 * oTable.fnFilter('Webkit'); 5479 * var data = oTable._('tr', {"search": "applied"}); 5480 * 5481 * // Do something with the data 5482 * alert( data.length+" rows matched the search" ); 5483 * } ); 5484 */ 5485 this._ = function ( sSelector, oOpts ) 5486 { 5487 return this.api(true).rows( sSelector, oOpts ).data(); 5488 }; 5489 5490 5491 /** 5492 * Create a DataTables Api instance, with the currently selected tables for 5493 * the Api's context. 5494 * @param {boolean} [traditional=false] Set the API instance's context to be 5495 * only the table referred to by the `DataTable.ext.iApiIndex` option, as was 5496 * used in the API presented by DataTables 1.9- (i.e. the traditional mode), 5497 * or if all tables captured in the jQuery object should be used. 5498 * @return {DataTables.Api} 5499 */ 5500 this.api = function ( traditional ) 5501 { 5502 return traditional ? 5503 new _Api( 5504 _fnSettingsFromNode( this[ _ext.iApiIndex ] ) 5505 ) : 5506 new _Api( this ); 5507 }; 5508 5509 5510 /** 5511 * Add a single new row or multiple rows of data to the table. Please note 5512 * that this is suitable for client-side processing only - if you are using 5513 * server-side processing (i.e. "bServerSide": true), then to add data, you 5514 * must add it to the data source, i.e. the server-side, through an Ajax call. 5515 * @param {array|object} data The data to be added to the table. This can be: 5516 * <ul> 5517 * <li>1D array of data - add a single row with the data provided</li> 5518 * <li>2D array of arrays - add multiple rows in a single call</li> 5519 * <li>object - data object when using <i>mData</i></li> 5520 * <li>array of objects - multiple data objects when using <i>mData</i></li> 5521 * </ul> 5522 * @param {bool} [redraw=true] redraw the table or not 5523 * @returns {array} An array of integers, representing the list of indexes in 5524 * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to 5525 * the table. 5526 * @dtopt API 5527 * @deprecated Since v1.10 5528 * 5529 * @example 5530 * // Global var for counter 5531 * var giCount = 2; 5532 * 5533 * $(document).ready(function() { 5534 * $('#example').dataTable(); 5535 * } ); 5536 * 5537 * function fnClickAddRow() { 5538 * $('#example').dataTable().fnAddData( [ 5539 * giCount+".1", 5540 * giCount+".2", 5541 * giCount+".3", 5542 * giCount+".4" ] 5543 * ); 5544 * 5545 * giCount++; 5546 * } 5547 */ 5548 this.fnAddData = function( data, redraw ) 5549 { 5550 var api = this.api( true ); 5551 5552 /* Check if we want to add multiple rows or not */ 5553 var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ? 5554 api.rows.add( data ) : 5555 api.row.add( data ); 5556 5557 if ( redraw === undefined || redraw ) { 5558 api.draw(); 5559 } 5560 5561 return rows.flatten().toArray(); 5562 }; 5563
5564 5565 /** 5566 * This function will make DataTables recalculate the column sizes, based on the data 5567 * contained in the table and the sizes applied to the columns (in the DOM, CSS or 5568 * through the sWidth parameter). This can be useful when the width of the table's 5569 * parent element changes (for example a window resize). 5570 * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to 5571 * @dtopt API 5572 * @deprecated Since v1.10 5573 * 5574 * @example 5575 * $(document).ready(function() { 5576 * var oTable = $('#example').dataTable( { 5577 * "sScrollY": "200px", 5578 * "bPaginate": false 5579 * } ); 5580 * 5581 * $(window).bind('resize', function () { 5582 * oTable.fnAdjustColumnSizing(); 5583 * } ); 5584 * } ); 5585 */ 5586 this.fnAdjustColumnSizing = function ( bRedraw ) 5587 { 5588 var api = this.api( true ).columns.adjust(); 5589 var settings = api.settings()[0]; 5590 var scroll = settings.oScroll; 5591 5592 if ( bRedraw === undefined || bRedraw ) { 5593 api.draw( false ); 5594 } 5595 else if ( scroll.sX !== "" || scroll.sY !== "" ) { 5596 /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */ 5597 _fnScrollDraw( settings ); 5598 } 5599 }; 5600 5601 5602 /** 5603 * Quickly and simply clear a table 5604 * @param {bool} [bRedraw=true] redraw the table or not 5605 * @dtopt API 5606 * @deprecated Since v1.10 5607 * 5608 * @example 5609 * $(document).ready(function() { 5610 * var oTable = $('#example').dataTable(); 5611 * 5612 * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...) 5613 * oTable.fnClearTable(); 5614 * } ); 5615 */ 5616 this.fnClearTable = function( bRedraw ) 5617 { 5618 var api = this.api( true ).clear(); 5619 5620 if ( bRedraw === undefined || bRedraw ) { 5621 api.draw(); 5622 } 5623 }; 5624 5625 5626 /** 5627 * The exact opposite of 'opening' a row, this function will close any rows which 5628 * are currently 'open'. 5629 * @param {node} nTr the table row to 'close' 5630 * @returns {int} 0 on success, or 1 if failed (can't find the row) 5631 * @dtopt API 5632 * @deprecated Since v1.10 5633 * 5634 * @example 5635 * $(document).ready(function() { 5636 * var oTable; 5637 * 5638 * // 'open' an information row when a row is clicked on 5639 * $('#example tbody tr').click( function () { 5640 * if ( oTable.fnIsOpen(this) ) { 5641 * oTable.fnClose( this ); 5642 * } else { 5643 * oTable.fnOpen( this, "Temporary row opened", "info_row" ); 5644 * } 5645 * } ); 5646 * 5647 * oTable = $('#example').dataTable(); 5648 * } ); 5649 */ 5650 this.fnClose = function( nTr ) 5651 { 5652 this.api( true ).row( nTr ).child.hide(); 5653 }; 5654 5655 5656 /** 5657 * Remove a row for the table 5658 * @param {mixed} target The index of the row from aoData to be deleted, or 5659 * the TR element you want to delete 5660 * @param {function|null} [callBack] Callback function 5661 * @param {bool} [redraw=true] Redraw the table or not 5662 * @returns {array} The row that was deleted 5663 * @dtopt API 5664 * @deprecated Since v1.10 5665 * 5666 * @example 5667 * $(document).ready(function() { 5668 * var oTable = $('#example').dataTable(); 5669 * 5670 * // Immediately remove the first row 5671 * oTable.fnDeleteRow( 0 ); 5672 * } ); 5673 */ 5674 this.fnDeleteRow = function( target, callback, redraw ) 5675 { 5676 var api = this.api( true ); 5677 var rows = api.rows( target ); 5678 var settings = rows.settings()[0]; 5679 var data = settings.aoData[ rows[0][0] ]; 5680 5681 rows.remove(); 5682 5683 if ( callback ) { 5684 callback.call( this, settings, data ); 5685 } 5686 5687 if ( redraw === undefined || redraw ) { 5688 api.draw(); 5689 } 5690 5691 return data; 5692 }; 5693 5694 5695 /** 5696 * Restore the table to it's original state in the DOM by removing all of DataTables 5697 * enhancements, alterations to the DOM structure of the table and event listeners. 5698 * @param {boolean} [remove=false] Completely remove the table from the DOM 5699 * @dtopt API 5700 * @deprecated Since v1.10 5701 * 5702 * @example 5703 * $(document).ready(function() { 5704 * // This example is fairly pointless in reality, but shows how fnDestroy can be used 5705 * var oTable = $('#example').dataTable(); 5706 * oTable.fnDestroy(); 5707 * } ); 5708 */ 5709 this.fnDestroy = function ( remove ) 5710 { 5711 this.api( true ).destroy( remove ); 5712 }; 5713
5714 5715 /** 5716 * Redraw the table 5717 * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw. 5718 * @dtopt API 5719 * @deprecated Since v1.10 5720 * 5721 * @example 5722 * $(document).ready(function() { 5723 * var oTable = $('#example').dataTable(); 5724 * 5725 * // Re-draw the table - you wouldn't want to do it here, but it's an example :-) 5726 * oTable.fnDraw(); 5727 * } ); 5728 */ 5729 this.fnDraw = function( complete ) 5730 { 5731 // Note that this isn't an exact match to the old call to _fnDraw - it takes 5732 // into account the new data, but can hold position. 5733 this.api( true ).draw( complete ); 5734 }; 5735 5736 5737 /** 5738 * Filter the input based on data 5739 * @param {string} sInput String to filter the table on 5740 * @param {int|null} [iColumn] Column to limit filtering to 5741 * @param {bool} [bRegex=false] Treat as regular expression or not 5742 * @param {bool} [bSmart=true] Perform smart filtering or not 5743 * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es) 5744 * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false) 5745 * @dtopt API 5746 * @deprecated Since v1.10 5747 * 5748 * @example 5749 * $(document).ready(function() { 5750 * var oTable = $('#example').dataTable(); 5751 * 5752 * // Sometime later - filter... 5753 * oTable.fnFilter( 'test string' ); 5754 * } ); 5755 */ 5756 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive ) 5757 { 5758 var api = this.api( true ); 5759 5760 if ( iColumn === null || iColumn === undefined ) { 5761 api.search( sInput, bRegex, bSmart, bCaseInsensitive ); 5762 } 5763 else { 5764 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive ); 5765 } 5766 5767 api.draw(); 5768 }; 5769 5770 5771 /** 5772 * Get the data for the whole table, an individual row or an individual cell based on the 5773 * provided parameters. 5774 * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as 5775 * a TR node then the data source for the whole row will be returned. If given as a 5776 * TD/TH cell node then iCol will be automatically calculated and the data for the 5777 * cell returned. If given as an integer, then this is treated as the aoData internal 5778 * data index for the row (see fnGetPosition) and the data for that row used. 5779 * @param {int} [col] Optional column index that you want the data of. 5780 * @returns {array|object|string} If mRow is undefined, then the data for all rows is 5781 * returned. If mRow is defined, just data for that row, and is iCol is 5782 * defined, only data for the designated cell is returned. 5783 * @dtopt API 5784 * @deprecated Since v1.10 5785 * 5786 * @example 5787 * // Row data 5788 * $(document).ready(function() { 5789 * oTable = $('#example').dataTable(); 5790 * 5791 * oTable.$('tr').click( function () { 5792 * var data = oTable.fnGetData( this ); 5793 * // ... do something with the array / object of data for the row 5794 * } ); 5795 * } ); 5796 * 5797 * @example 5798 * // Individual cell data 5799 * $(document).ready(function() { 5800 * oTable = $('#example').dataTable(); 5801 * 5802 * oTable.$('td').click( function () { 5803 * var sData = oTable.fnGetData( this ); 5804 * alert( 'The cell clicked on had the value of '+sData ); 5805 * } ); 5806 * } ); 5807 */ 5808 this.fnGetData = function( src, col ) 5809 { 5810 var api = this.api( true ); 5811 5812 if ( src !== undefined ) { 5813 var type = src.nodeName ? src.nodeName.toLowerCase() : ''; 5814 5815 return col !== undefined || type == 'td' || type == 'th' ? 5816 api.cell( src, col ).data() : 5817 api.row( src ).data() || null; 5818 } 5819 5820 return api.data().toArray(); 5821 }; 5822 5823 5824 /** 5825 * Get an array of the TR nodes that are used in the table's body. Note that you will 5826 * typically want to use the '$' API method in preference to this as it is more 5827 * flexible. 5828 * @param {int} [iRow] Optional row index for the TR element you want 5829 * @returns {array|node} If iRow is undefined, returns an array of all TR elements 5830 * in the table's body, or iRow is defined, just the TR element requested. 5831 * @dtopt API 5832 * @deprecated Since v1.10 5833 * 5834 * @example 5835 * $(document).ready(function() { 5836 * var oTable = $('#example').dataTable(); 5837 * 5838 * // Get the nodes from the table 5839 * var nNodes = oTable.fnGetNodes( ); 5840 * } ); 5841 */ 5842 this.fnGetNodes = function( iRow ) 5843 { 5844 var api = this.api( true ); 5845 5846 return iRow !== undefined ? 5847 api.row( iRow ).node() : 5848 api.rows().nodes().flatten().toArray(); 5849 }; 5850
5851 5852 /** 5853 * Get the array indexes of a particular cell from it's DOM element 5854 * and column index including hidden columns 5855 * @param {node} node this can either be a TR, TD or TH in the table's body 5856 * @returns {int} If nNode is given as a TR, then a single index is returned, or 5857 * if given as a cell, an array of [row index, column index (visible), 5858 * column index (all)] is given. 5859 * @dtopt API 5860 * @deprecated Since v1.10 5861 * 5862 * @example 5863 * $(document).ready(function() { 5864 * $('#example tbody td').click( function () { 5865 * // Get the position of the current data from the node 5866 * var aPos = oTable.fnGetPosition( this ); 5867 * 5868 * // Get the data array for this row 5869 * var aData = oTable.fnGetData( aPos[0] ); 5870 * 5871 * // Update the data array and return the value 5872 * aData[ aPos[1] ] = 'clicked'; 5873 * this.innerHTML = 'clicked'; 5874 * } ); 5875 * 5876 * // Init DataTables 5877 * oTable = $('#example').dataTable(); 5878 * } ); 5879 */ 5880 this.fnGetPosition = function( node ) 5881 { 5882 var api = this.api( true ); 5883 var nodeName = node.nodeName.toUpperCase(); 5884 5885 if ( nodeName == 'TR' ) { 5886 return api.row( node ).index(); 5887 } 5888 else if ( nodeName == 'TD' || nodeName == 'TH' ) { 5889 var cell = api.cell( node ).index(); 5890 5891 return [ 5892 cell.row, 5893 cell.columnVisible, 5894 cell.column 5895 ]; 5896 } 5897 return null; 5898 }; 5899 5900 5901 /** 5902 * Check to see if a row is 'open' or not. 5903 * @param {node} nTr the table row to check 5904 * @returns {boolean} true if the row is currently open, false otherwise 5905 * @dtopt API 5906 * @deprecated Since v1.10 5907 * 5908 * @example 5909 * $(document).ready(function() { 5910 * var oTable; 5911 * 5912 * // 'open' an information row when a row is clicked on 5913 * $('#example tbody tr').click( function () { 5914 * if ( oTable.fnIsOpen(this) ) { 5915 * oTable.fnClose( this ); 5916 * } else { 5917 * oTable.fnOpen( this, "Temporary row opened", "info_row" ); 5918 * } 5919 * } ); 5920 * 5921 * oTable = $('#example').dataTable(); 5922 * } ); 5923 */ 5924 this.fnIsOpen = function( nTr ) 5925 { 5926 return this.api( true ).row( nTr ).child.isShown(); 5927 }; 5928 5929 5930 /** 5931 * This function will place a new row directly after a row which is currently 5932 * on display on the page, with the HTML contents that is passed into the 5933 * function. This can be used, for example, to ask for confirmation that a 5934 * particular record should be deleted. 5935 * @param {node} nTr The table row to 'open' 5936 * @param {string|node|jQuery} mHtml The HTML to put into the row 5937 * @param {string} sClass Class to give the new TD cell 5938 * @returns {node} The row opened. Note that if the table row passed in as the 5939 * first parameter, is not found in the table, this method will silently 5940 * return. 5941 * @dtopt API 5942 * @deprecated Since v1.10 5943 * 5944 * @example 5945 * $(document).ready(function() { 5946 * var oTable; 5947 * 5948 * // 'open' an information row when a row is clicked on 5949 * $('#example tbody tr').click( function () { 5950 * if ( oTable.fnIsOpen(this) ) { 5951 * oTable.fnClose( this ); 5952 * } else { 5953 * oTable.fnOpen( this, "Temporary row opened", "info_row" ); 5954 * } 5955 * } ); 5956 * 5957 * oTable = $('#example').dataTable(); 5958 * } ); 5959 */ 5960 this.fnOpen = function( nTr, mHtml, sClass ) 5961 { 5962 return this.api( true ) 5963 .row( nTr ) 5964 .child( mHtml, sClass ) 5965 .show() 5966 .child()[0]; 5967 }; 5968 5969 5970 /** 5971 * Change the pagination - provides the internal logic for pagination in a simple API 5972 * function. With this function you can have a DataTables table go to the next, 5973 * previous, first or last pages. 5974 * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" 5975 * or page number to jump to (integer), note that page 0 is the first page. 5976 * @param {bool} [bRedraw=true] Redraw the table or not 5977 * @dtopt API 5978 * @deprecated Since v1.10 5979 * 5980 * @example 5981 * $(document).ready(function() { 5982 * var oTable = $('#example').dataTable(); 5983 * oTable.fnPageChange( 'next' ); 5984 * } ); 5985 */ 5986 this.fnPageChange = function ( mAction, bRedraw ) 5987 { 5988 var api = this.api( true ).page( mAction ); 5989 5990 if ( bRedraw === undefined || bRedraw ) { 5991 api.draw(false); 5992 } 5993 }; 5994 5995 5996 /** 5997 * Show a particular column 5998 * @param {int} iCol The column whose display should be changed 5999 * @param {bool} bShow Show (true) or hide (false) the column 6000 * @param {bool} [bRedraw=true] Redraw the table or not 6001 * @dtopt API 6002 * @deprecated Since v1.10 6003 * 6004 * @example 6005 * $(document).ready(function() { 6006 * var oTable = $('#example').dataTable(); 6007 * 6008 * // Hide the second column after initialisation 6009 * oTable.fnSetColumnVis( 1, false ); 6010 * } ); 6011 */ 6012 this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) 6013 { 6014 var api = this.api( true ).column( iCol ).visible( bShow ); 6015 6016 if ( bRedraw === undefined || bRedraw ) { 6017 api.columns.adjust().draw(); 6018 } 6019 }; 6020 6021 6022 /** 6023 * Get the settings for a particular table for external manipulation 6024 * @returns {object} DataTables settings object. See 6025 * {@link DataTable.models.oSettings} 6026 * @dtopt API 6027 * @deprecated Since v1.10 6028 * 6029 * @example 6030 * $(document).ready(function() { 6031 * var oTable = $('#example').dataTable(); 6032 * var oSettings = oTable.fnSettings(); 6033 * 6034 * // Show an example parameter from the settings 6035 * alert( oSettings._iDisplayStart ); 6036 * } ); 6037 */ 6038 this.fnSettings = function() 6039 {
6040 return _fnSettingsFromNode( this[_ext.iApiIndex] ); 6041 }; 6042 6043 6044 /** 6045 * Sort the table by a particular column 6046 * @param {int} iCol the data index to sort on. Note that this will not match the 6047 * 'display index' if you have hidden data entries 6048 * @dtopt API 6049 * @deprecated Since v1.10 6050 * 6051 * @example 6052 * $(document).ready(function() { 6053 * var oTable = $('#example').dataTable(); 6054 * 6055 * // Sort immediately with columns 0 and 1 6056 * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] ); 6057 * } ); 6058 */ 6059 this.fnSort = function( aaSort ) 6060 { 6061 this.api( true ).order( aaSort ).draw(); 6062 }; 6063 6064 6065 /** 6066 * Attach a sort listener to an element for a given column 6067 * @param {node} nNode the element to attach the sort listener to 6068 * @param {int} iColumn the column that a click on this node will sort on 6069 * @param {function} [fnCallback] callback function when sort is run 6070 * @dtopt API 6071 * @deprecated Since v1.10 6072 * 6073 * @example 6074 * $(document).ready(function() { 6075 * var oTable = $('#example').dataTable(); 6076 * 6077 * // Sort on column 1, when 'sorter' is clicked on 6078 * oTable.fnSortListener( document.getElementById('sorter'), 1 ); 6079 * } ); 6080 */ 6081 this.fnSortListener = function( nNode, iColumn, fnCallback ) 6082 { 6083 this.api( true ).order.listener( nNode, iColumn, fnCallback ); 6084 }; 6085 6086 6087 /** 6088 * Update a table cell or row - this method will accept either a single value to 6089 * update the cell with, an array of values with one element for each column or 6090 * an object in the same format as the original data source. The function is 6091 * self-referencing in order to make the multi column updates easier. 6092 * @param {object|array|string} mData Data to update the cell/row with 6093 * @param {node|int} mRow TR element you want to update or the aoData index 6094 * @param {int} [iColumn] The column to update, give as null or undefined to 6095 * update a whole row. 6096 * @param {bool} [bRedraw=true] Redraw the table or not 6097 * @param {bool} [bAction=true] Perform pre-draw actions or not 6098 * @returns {int} 0 on success, 1 on error 6099 * @dtopt API 6100 * @deprecated Since v1.10 6101 * 6102 * @example 6103 * $(document).ready(function() { 6104 * var oTable = $('#example').dataTable(); 6105 * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell 6106 * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row 6107 * } ); 6108 */ 6109 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction ) 6110 { 6111 var api = this.api( true ); 6112 6113 if ( iColumn === undefined || iColumn === null ) { 6114 api.row( mRow ).data( mData ); 6115 } 6116 else { 6117 api.cell( mRow, iColumn ).data( mData ); 6118 } 6119 6120 if ( bAction === undefined || bAction ) { 6121 api.columns.adjust(); 6122 } 6123 6124 if ( bRedraw === undefined || bRedraw ) { 6125 api.draw(); 6126 } 6127 return 0; 6128 }; 6129 6130 6131 /** 6132 * Provide a common method for plug-ins to check the version of DataTables being used, in order 6133 * to ensure compatibility. 6134 * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the 6135 * formats "X" and "X.Y" are also acceptable. 6136 * @returns {boolean} true if this version of DataTables is greater or equal to the required 6137 * version, or false if this version of DataTales is not suitable 6138 * @method 6139 * @dtopt API 6140 * @deprecated Since v1.10 6141 * 6142 * @example 6143 * $(document).ready(function() { 6144 * var oTable = $('#example').dataTable(); 6145 * alert( oTable.fnVersionCheck( '1.9.0' ) ); 6146 * } ); 6147 */ 6148 this.fnVersionCheck = _ext.fnVersionCheck; 6149 6150 6151 var _that = this; 6152 var emptyInit = options === undefined; 6153 var len = this.length; 6154 6155 if ( emptyInit ) { 6156 options = {}; 6157 } 6158 6159 this.oApi = this.internal = _ext.internal; 6160 6161 // Extend with old style plug-in API methods 6162 for ( var fn in DataTable.ext.internal ) { 6163 if ( fn ) { 6164 this[fn] = _fnExternApiFunc(fn); 6165 } 6166 } 6167 6168 this.each(function() { 6169 // For each initialisation we want to give it a clean initialisation 6170 // object that can be bashed around 6171 var o = {}; 6172 var oInit = len > 1 ? // optimisation for single table case 6173 _fnExtend( o, options, true ) : 6174 options; 6175 6176 /*global oInit,_that,emptyInit*/ 6177 var i=0, iLen, j, jLen, k, kLen; 6178 var sId = this.getAttribute( 'id' ); 6179 var bInitHandedOff = false;
6180 var defaults = DataTable.defaults; 6181 var $this = $(this); 6182 6183 6184 /* Sanity check */ 6185 if ( this.nodeName.toLowerCase() != 'table' ) 6186 { 6187 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); 6188 return; 6189 } 6190 6191 /* Backwards compatibility for the defaults */ 6192 _fnCompatOpts( defaults ); 6193 _fnCompatCols( defaults.column ); 6194 6195 /* Convert the camel-case defaults to Hungarian */ 6196 _fnCamelToHungarian( defaults, defaults, true ); 6197 _fnCamelToHungarian( defaults.column, defaults.column, true ); 6198 6199 /* Setting up the initialisation object */ 6200 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) ); 6201 6202 6203 6204 /* Check to see if we are re-initialising a table */ 6205 var allSettings = DataTable.settings; 6206 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ ) 6207 { 6208 var s = allSettings[i]; 6209 6210 /* Base check on table node */ 6211 if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) ) 6212 { 6213 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve; 6214 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy; 6215 6216 if ( emptyInit || bRetrieve ) 6217 { 6218 return s.oInstance; 6219 } 6220 else if ( bDestroy ) 6221 { 6222 s.oInstance.fnDestroy(); 6223 break; 6224 } 6225 else 6226 { 6227 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 ); 6228 return; 6229 } 6230 } 6231 6232 /* If the element we are initialising has the same ID as a table which was previously 6233 * initialised, but the table nodes don't match (from before) then we destroy the old 6234 * instance by simply deleting it. This is under the assumption that the table has been 6235 * destroyed by other methods. Anyone using non-id selectors will need to do this manually 6236 */ 6237 if ( s.sTableId == this.id ) 6238 { 6239 allSettings.splice( i, 1 ); 6240 break; 6241 } 6242 } 6243 6244 /* Ensure the table has an ID - required for accessibility */ 6245 if ( sId === null || sId === "" ) 6246 { 6247 sId = "DataTables_Table_"+(DataTable.ext._unique++); 6248 this.id = sId; 6249 } 6250 6251 /* Create the settings object for this table and set some of the default parameters */ 6252 var oSettings = $.extend( true, {}, DataTable.models.oSettings, { 6253 "sDestroyWidth": $this[0].style.width, 6254 "sInstance": sId, 6255 "sTableId": sId 6256 } ); 6257 oSettings.nTable = this; 6258 oSettings.oApi = _that.internal; 6259 oSettings.oInit = oInit; 6260 6261 allSettings.push( oSettings ); 6262 6263 // Need to add the instance after the instance after the settings object has been added 6264 // to the settings array, so we can self reference the table instance if more than one 6265 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable(); 6266 6267 // Backwards compatibility, before we apply all the defaults 6268 _fnCompatOpts( oInit ); 6269 6270 if ( oInit.oLanguage ) 6271 { 6272 _fnLanguageCompat( oInit.oLanguage ); 6273 } 6274 6275 // If the length menu is given, but the init display length is not, use the length menu 6276 if ( oInit.aLengthMenu && ! oInit.iDisplayLength ) 6277 { 6278 oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ? 6279 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0]; 6280 } 6281 6282 // Apply the defaults and init options to make a single init object will all 6283 // options defined from defaults and instance options. 6284 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit ); 6285 6286 6287 // Map the initialisation options onto the settings object 6288 _fnMap( oSettings.oFeatures, oInit, [ 6289 "bPaginate", 6290 "bLengthChange", 6291 "bFilter", 6292 "bSort", 6293 "bSortMulti", 6294 "bInfo", 6295 "bProcessing", 6296 "bAutoWidth", 6297 "bSortClasses", 6298 "bServerSide", 6299 "bDeferRender" 6300 ] ); 6301 _fnMap( oSettings, oInit, [ 6302 "asStripeClasses", 6303 "ajax", 6304 "fnServerData", 6305 "fnFormatNumber", 6306 "sServerMethod", 6307 "aaSorting", 6308 "aaSortingFixed", 6309 "aLengthMenu", 6310 "sPaginationType", 6311 "sAjaxSource", 6312 "sAjaxDataProp", 6313 "iStateDuration", 6314 "sDom", 6315 "bSortCellsTop", 6316 "iTabIndex", 6317 "fnStateLoadCallback", 6318 "fnStateSaveCallback", 6319 "renderer", 6320 "searchDelay", 6321 "rowId", 6322 [ "iCookieDuration", "iStateDuration" ], // backwards compat 6323 [ "oSearch", "oPreviousSearch" ], 6324 [ "aoSearchCols", "aoPreSearchCols" ], 6325 [ "iDisplayLength", "_iDisplayLength" ], 6326 [ "bJQueryUI", "bJUI" ] 6327 ] ); 6328 _fnMap( oSettings.oScroll, oInit, [ 6329 [ "sScrollX", "sX" ], 6330 [ "sScrollXInner", "sXInner" ], 6331 [ "sScrollY", "sY" ],
6332 [ "bScrollCollapse", "bCollapse" ] 6333 ] ); 6334 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); 6335 6336 /* Callback functions which are array driven */ 6337 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' ); 6338 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' ); 6339 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' ); 6340 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' ); 6341 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' ); 6342 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' ); 6343 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' ); 6344 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' ); 6345 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' ); 6346 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' ); 6347 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' ); 6348 6349 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId ); 6350 6351 /* Browser support detection */ 6352 _fnBrowserDetect( oSettings ); 6353 6354 var oClasses = oSettings.oClasses; 6355 6356 // @todo Remove in 1.11 6357 if ( oInit.bJQueryUI ) 6358 { 6359 /* Use the JUI classes object for display. You could clone the oStdClasses object if 6360 * you want to have multiple tables with multiple independent classes 6361 */ 6362 $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses ); 6363 6364 if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" ) 6365 { 6366 /* Set the DOM to use a layout suitable for jQuery UI's theming */ 6367 oSettings.sDom = '<"H"lfr>t<"F"ip>'; 6368 } 6369 6370 if ( ! oSettings.renderer ) { 6371 oSettings.renderer = 'jqueryui'; 6372 } 6373 else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) { 6374 oSettings.renderer.header = 'jqueryui'; 6375 } 6376 } 6377 else 6378 { 6379 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses ); 6380 } 6381 $this.addClass( oClasses.sTable ); 6382 6383 6384 if ( oSettings.iInitDisplayStart === undefined ) 6385 { 6386 /* Display start point, taking into account the save saving */ 6387 oSettings.iInitDisplayStart = oInit.iDisplayStart; 6388 oSettings._iDisplayStart = oInit.iDisplayStart; 6389 } 6390 6391 if ( oInit.iDeferLoading !== null ) 6392 { 6393 oSettings.bDeferLoading = true; 6394 var tmp = $.isArray( oInit.iDeferLoading ); 6395 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading; 6396 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading; 6397 } 6398 6399 /* Language definitions */ 6400 var oLanguage = oSettings.oLanguage; 6401 $.extend( true, oLanguage, oInit.oLanguage ); 6402 6403 if ( oLanguage.sUrl !== "" ) 6404 { 6405 /* Get the language definitions from a file - because this Ajax call makes the language 6406 * get async to the remainder of this function we use bInitHandedOff to indicate that 6407 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor 6408 */ 6409 $.ajax( { 6410 dataType: 'json', 6411 url: oLanguage.sUrl, 6412 success: function ( json ) { 6413 _fnLanguageCompat( json ); 6414 _fnCamelToHungarian( defaults.oLanguage, json ); 6415 $.extend( true, oLanguage, json ); 6416 _fnInitialise( oSettings ); 6417 }, 6418 error: function () { 6419 // Error occurred loading language file, continue on as best we can 6420 _fnInitialise( oSettings ); 6421 } 6422 } ); 6423 bInitHandedOff = true; 6424 } 6425 6426 /* 6427 * Stripes 6428 */ 6429 if ( oInit.asStripeClasses === null ) 6430 { 6431 oSettings.asStripeClasses =[ 6432 oClasses.sStripeOdd, 6433 oClasses.sStripeEven 6434 ]; 6435 } 6436 6437 /* Remove row stripe classes if they are already on the table row */ 6438 var stripeClasses = oSettings.asStripeClasses; 6439 var rowOne = $this.children('tbody').find('tr').eq(0); 6440 if ( $.inArray( true, $.map( stripeClasses, function(el, i) { 6441 return rowOne.hasClass(el); 6442 } ) ) !== -1 ) { 6443 $('tbody tr', this).removeClass( stripeClasses.join(' ') ); 6444 oSettings.asDestroyStripes = stripeClasses.slice(); 6445 } 6446 6447 /* 6448 * Columns 6449 * See if we should load columns automatically or use defined ones 6450 */ 6451 var anThs = []; 6452 var aoColumnsInit; 6453 var nThead = this.getElementsByTagName('thead'); 6454 if ( nThead.length !== 0 ) 6455 { 6456 _fnDetectHeader( oSettings.aoHeader, nThead[0] ); 6457 anThs = _fnGetUniqueThs( oSettings ); 6458 } 6459 6460 /* If not given a column array, generate one with nulls */ 6461 if ( oInit.aoColumns === null ) 6462 { 6463 aoColumnsInit = []; 6464 for ( i=0, iLen=anThs.length ; i<iLen ; i++ ) 6465 { 6466 aoColumnsInit.push( null ); 6467 } 6468 } 6469 else 6470 {
6471 aoColumnsInit = oInit.aoColumns; 6472 } 6473 6474 /* Add the columns */ 6475 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ ) 6476 { 6477 _fnAddColumn( oSettings, anThs ? anThs[i] : null ); 6478 } 6479 6480 /* Apply the column definitions */ 6481 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) { 6482 _fnColumnOptions( oSettings, iCol, oDef ); 6483 } ); 6484 6485 /* HTML5 attribute detection - build an mData object automatically if the 6486 * attributes are found 6487 */ 6488 if ( rowOne.length ) { 6489 var a = function ( cell, name ) { 6490 return cell.getAttribute( 'data-'+name ) !== null ? name : null; 6491 }; 6492 6493 $( rowOne[0] ).children('th, td').each( function (i, cell) { 6494 var col = oSettings.aoColumns[i]; 6495 6496 if ( col.mData === i ) { 6497 var sort = a( cell, 'sort' ) || a( cell, 'order' ); 6498 var filter = a( cell, 'filter' ) || a( cell, 'search' ); 6499 6500 if ( sort !== null || filter !== null ) { 6501 col.mData = { 6502 _: i+'.display', 6503 sort: sort !== null ? i+'.@data-'+sort : undefined, 6504 type: sort !== null ? i+'.@data-'+sort : undefined, 6505 filter: filter !== null ? i+'.@data-'+filter : undefined 6506 }; 6507 6508 _fnColumnOptions( oSettings, i ); 6509 } 6510 } 6511 } ); 6512 } 6513 6514 var features = oSettings.oFeatures; 6515 6516 /* Must be done after everything which can be overridden by the state saving! */ 6517 if ( oInit.bStateSave ) 6518 { 6519 features.bStateSave = true; 6520 _fnLoadState( oSettings, oInit ); 6521 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' ); 6522 } 6523 6524 6525 /* 6526 * Sorting 6527 * @todo For modularisation (1.11) this needs to do into a sort start up handler 6528 */ 6529 6530 // If aaSorting is not defined, then we use the first indicator in asSorting 6531 // in case that has been altered, so the default sort reflects that option 6532 if ( oInit.aaSorting === undefined ) 6533 { 6534 var sorting = oSettings.aaSorting; 6535 for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) 6536 { 6537 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0]; 6538 } 6539 } 6540 6541 /* Do a first pass on the sorting classes (allows any size changes to be taken into 6542 * account, and also will apply sorting disabled classes if disabled 6543 */ 6544 _fnSortingClasses( oSettings ); 6545 6546 if ( features.bSort ) 6547 { 6548 _fnCallbackReg( oSettings, 'aoDrawCallback', function () { 6549 if ( oSettings.bSorted ) { 6550 var aSort = _fnSortFlatten( oSettings ); 6551 var sortedColumns = {}; 6552 6553 $.each( aSort, function (i, val) { 6554 sortedColumns[ val.src ] = val.dir; 6555 } ); 6556 6557 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] ); 6558 _fnSortAria( oSettings ); 6559 } 6560 } ); 6561 } 6562 6563 _fnCallbackReg( oSettings, 'aoDrawCallback', function () { 6564 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) { 6565 _fnSortingClasses( oSettings ); 6566 } 6567 }, 'sc' ); 6568 6569 6570 /* 6571 * Final init 6572 * Cache the header, body and footer as required, creating them if needed 6573 */ 6574 6575 // Work around for Webkit bug 83867 - store the caption-side before removing from doc 6576 var captions = $this.children('caption').each( function () { 6577 this._captionSide = $this.css('caption-side'); 6578 } ); 6579 6580 var thead = $this.children('thead'); 6581 if ( thead.length === 0 ) 6582 { 6583 thead = $('<thead/>').appendTo(this); 6584 } 6585 oSettings.nTHead = thead[0]; 6586 6587 var tbody = $this.children('tbody'); 6588 if ( tbody.length === 0 ) 6589 { 6590 tbody = $('<tbody/>').appendTo(this); 6591 } 6592 oSettings.nTBody = tbody[0]; 6593 6594 var tfoot = $this.children('tfoot'); 6595 if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) 6596 { 6597 // If we are a scrolling table, and no footer has been given, then we need to create 6598 // a tfoot element for the caption element to be appended to 6599 tfoot = $('<tfoot/>').appendTo(this); 6600 } 6601 6602 if ( tfoot.length === 0 || tfoot.children().length === 0 ) { 6603 $this.addClass( oClasses.sNoFooter ); 6604 } 6605 else if ( tfoot.length > 0 ) { 6606 oSettings.nTFoot = tfoot[0]; 6607 _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); 6608 } 6609 6610 /* Check if there is data passing into the constructor */ 6611 if ( oInit.aaData ) 6612 { 6613 for ( i=0 ; i<oInit.aaData.length ; i++ ) 6614 { 6615 _fnAddData( oSettings, oInit.aaData[ i ] ); 6616 } 6617 } 6618 else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) 6619 { 6620 /* Grab the data from the page - only do this when deferred loading or no Ajax 6621 * source since there is no point in reading the DOM data if we are then going 6622 * to replace it with Ajax data 6623 */ 6624 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') ); 6625 } 6626 6627 /* Copy the data index array */ 6628 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 6629 6630 /* Initialisation complete - table can be drawn */
6631 oSettings.bInitialised = true; 6632 6633 /* Check if we need to initialise the table (it might not have been handed off to the 6634 * language processor) 6635 */ 6636 if ( bInitHandedOff === false ) 6637 { 6638 _fnInitialise( oSettings ); 6639 } 6640 } ); 6641 _that = null; 6642 return this; 6643 }; 6644 6645 6646 6647 /** 6648 * Computed structure of the DataTables API, defined by the options passed to 6649 * `DataTable.Api.register()` when building the API. 6650 * 6651 * The structure is built in order to speed creation and extension of the Api 6652 * objects since the extensions are effectively pre-parsed. 6653 * 6654 * The array is an array of objects with the following structure, where this 6655 * base array represents the Api prototype base: 6656 * 6657 * [ 6658 * { 6659 * name: 'data' -- string - Property name 6660 * val: function () {}, -- function - Api method (or undefined if just an object 6661 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6662 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6663 * }, 6664 * { 6665 * name: 'row' 6666 * val: {}, 6667 * methodExt: [ ... ], 6668 * propExt: [ 6669 * { 6670 * name: 'data' 6671 * val: function () {}, 6672 * methodExt: [ ... ], 6673 * propExt: [ ... ] 6674 * }, 6675 * ... 6676 * ] 6677 * } 6678 * ] 6679 * 6680 * @type {Array} 6681 * @ignore 6682 */ 6683 var __apiStruct = []; 6684 6685 6686 /** 6687 * `Array.prototype` reference. 6688 * 6689 * @type object 6690 * @ignore 6691 */ 6692 var __arrayProto = Array.prototype; 6693 6694 6695 /** 6696 * Abstraction for `context` parameter of the `Api` constructor to allow it to 6697 * take several different forms for ease of use. 6698 * 6699 * Each of the input parameter types will be converted to a DataTables settings 6700 * object where possible. 6701 * 6702 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one 6703 * of: 6704 * 6705 * * `string` - jQuery selector. Any DataTables' matching the given selector 6706 * with be found and used. 6707 * * `node` - `TABLE` node which has already been formed into a DataTable. 6708 * * `jQuery` - A jQuery object of `TABLE` nodes. 6709 * * `object` - DataTables settings object 6710 * * `DataTables.Api` - API instance 6711 * @return {array|null} Matching DataTables settings objects. `null` or 6712 * `undefined` is returned if no matching DataTable is found. 6713 * @ignore 6714 */ 6715 var _toSettings = function ( mixed ) 6716 { 6717 var idx, jq; 6718 var settings = DataTable.settings; 6719 var tables = $.map( settings, function (el, i) { 6720 return el.nTable; 6721 } ); 6722 6723 if ( ! mixed ) { 6724 return []; 6725 } 6726 else if ( mixed.nTable && mixed.oApi ) { 6727 // DataTables settings object 6728 return [ mixed ]; 6729 } 6730 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) { 6731 // Table node 6732 idx = $.inArray( mixed, tables ); 6733 return idx !== -1 ? [ settings[idx] ] : null; 6734 } 6735 else if ( mixed && typeof mixed.settings === 'function' ) { 6736 return mixed.settings().toArray(); 6737 } 6738 else if ( typeof mixed === 'string' ) { 6739 // jQuery selector 6740 jq = $(mixed); 6741 } 6742 else if ( mixed instanceof $ ) { 6743 // jQuery object (also DataTables instance) 6744 jq = mixed; 6745 } 6746 6747 if ( jq ) { 6748 return jq.map( function(i) { 6749 idx = $.inArray( this, tables ); 6750 return idx !== -1 ? settings[idx] : null; 6751 } ).toArray(); 6752 } 6753 }; 6754 6755 6756 /** 6757 * DataTables API class - used to control and interface with one or more 6758 * DataTables enhanced tables. 6759 * 6760 * The API class is heavily based on jQuery, presenting a chainable interface 6761 * that you can use to interact with tables. Each instance of the API class has 6762 * a "context" - i.e. the tables that it will operate on. This could be a single 6763 * table, all tables on a page or a sub-set thereof. 6764 * 6765 * Additionally the API is designed to allow you to easily work with the data in 6766 * the tables, retrieving and manipulating it as required. This is done by 6767 * presenting the API class as an array like interface. The contents of the 6768 * array depend upon the actions requested by each method (for example 6769 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6770 * return an array of objects or arrays depending upon your table's 6771 * configuration). The API object has a number of array like methods (`push`, 6772 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`, 6773 * `unique` etc) to assist your working with the data held in a table. 6774 * 6775 * Most methods (those which return an Api instance) are chainable, which means 6776 * the return from a method call also has all of the methods available that the 6777 * top level object had. For example, these two calls are equivalent: 6778 * 6779 * // Not chained 6780 * api.row.add( {...} ); 6781 * api.draw(); 6782 * 6783 * // Chained 6784 * api.row.add( {...} ).draw(); 6785 * 6786 * @class DataTable.Api 6787 * @param {array|object|string|jQuery} context DataTable identifier. This is 6788 * used to define which DataTables enhanced tables this API will operate on. 6789 * Can be one of: 6790 * 6791 * * `string` - jQuery selector. Any DataTables' matching the given selector 6792 * with be found and used. 6793 * * `node` - `TABLE` node which has already been formed into a DataTable. 6794 * * `jQuery` - A jQuery object of `TABLE` nodes. 6795 * * `object` - DataTables settings object 6796 * @param {array} [data] Data to initialise the Api instance with. 6797 * 6798 * @example 6799 * // Direct initialisation during DataTables construction 6800 * var api = $('#example').DataTable(); 6801 * 6802 * @example 6803 * // Initialisation using a DataTables jQuery object 6804 * var api = $('#example').dataTable().api(); 6805 * 6806 * @example 6807 * // Initialisation as a constructor 6808 * var api = new $.fn.DataTable.Api( 'table.dataTable' ); 6809 */ 6810 _Api = function ( context, data ) 6811 { 6812 if ( ! (this instanceof _Api) ) { 6813 return new _Api( context, data ); 6814 } 6815 6816 var settings = []; 6817 var ctxSettings = function ( o ) { 6818 var a = _toSettings( o ); 6819 if ( a ) { 6820 settings = settings.concat( a ); 6821 } 6822 }; 6823 6824 if ( $.isArray( context ) ) { 6825 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 6826 ctxSettings( context[i] ); 6827 } 6828 } 6829 else { 6830 ctxSettings( context ); 6831 } 6832 6833 // Remove duplicates 6834 this.context = _unique( settings ); 6835 6836 // Initial data 6837 if ( data ) { 6838 $.merge( this, data ); 6839 } 6840 6841 // selector 6842 this.selector = { 6843 rows: null, 6844 cols: null, 6845 opts: null 6846 }; 6847 6848 _Api.extend( this, this, __apiStruct ); 6849 }; 6850 6851 DataTable.Api = _Api; 6852 6853 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's 6854 // isPlainObject. 6855 $.extend( _Api.prototype, { 6856 any: function () 6857 { 6858 return this.count() !== 0; 6859 }, 6860 6861 6862 concat: __arrayProto.concat, 6863 6864 6865 context: [], // array of table settings objects 6866 6867 6868 count: function () 6869 { 6870 return this.flatten().length; 6871 }, 6872 6873 6874 each: function ( fn ) 6875 { 6876 for ( var i=0, ien=this.length ; i<ien; i++ ) { 6877 fn.call( this, this[i], i, this ); 6878 } 6879 6880 return this; 6881 }, 6882 6883 6884 eq: function ( idx ) 6885 { 6886 var ctx = this.context; 6887 6888 return ctx.length > idx ? 6889 new _Api( ctx[idx], this[idx] ) : 6890 null; 6891 }, 6892 6893 6894 filter: function ( fn ) 6895 { 6896 var a = []; 6897 6898 if ( __arrayProto.filter ) { 6899 a = __arrayProto.filter.call( this, fn, this ); 6900 } 6901 else { 6902 // Compatibility for browsers without EMCA-252-5 (JS 1.6) 6903 for ( var i=0, ien=this.length ; i<ien ; i++ ) { 6904 if ( fn.call( this, this[i], i, this ) ) { 6905 a.push( this[i] ); 6906 } 6907 } 6908 } 6909 6910 return new _Api( this.context, a ); 6911 }, 6912 6913 6914 flatten: function () 6915 { 6916 var a = []; 6917 return new _Api( this.context, a.concat.apply( a, this.toArray() ) ); 6918 }, 6919 6920 6921 join: __arrayProto.join, 6922 6923 6924 indexOf: __arrayProto.indexOf || function (obj, start) 6925 { 6926 for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) { 6927 if ( this[i] === obj ) { 6928 return i; 6929 } 6930 } 6931 return -1; 6932 }, 6933 6934 iterator: function ( flatten, type, fn, alwaysNew ) { 6935 var 6936 a = [], ret, 6937 i, ien, j, jen, 6938 context = this.context, 6939 rows, items, item, 6940 selector = this.selector; 6941 6942 // Argument shifting 6943 if ( typeof flatten === 'string' ) { 6944 alwaysNew = fn; 6945 fn = type; 6946 type = flatten; 6947 flatten = false; 6948 } 6949 6950 for ( i=0, ien=context.length ; i<ien ; i++ ) { 6951 var apiInst = new _Api( context[i] ); 6952 6953 if ( type === 'table' ) { 6954 ret = fn.call( apiInst, context[i], i ); 6955 6956 if ( ret !== undefined ) { 6957 a.push( ret ); 6958 } 6959 } 6960 else if ( type === 'columns' || type === 'rows' ) { 6961 // this has same length as context - one entry for each table 6962 ret = fn.call( apiInst, context[i], this[i], i ); 6963 6964 if ( ret !== undefined ) { 6965 a.push( ret ); 6966 } 6967 } 6968 else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) { 6969 // columns and rows share the same structure. 6970 // 'this' is an array of column indexes for each context 6971 items = this[i]; 6972 6973 if ( type === 'column-rows' ) { 6974 rows = _selector_row_indexes( context[i], selector.opts ); 6975 } 6976 6977 for ( j=0, jen=items.length ; j<jen ; j++ ) { 6978 item = items[j]; 6979 6980 if ( type === 'cell' ) { 6981 ret = fn.call( apiInst, context[i], item.row, item.column, i, j ); 6982 } 6983 else { 6984 ret = fn.call( apiInst, context[i], item, i, j, rows ); 6985 } 6986 6987 if ( ret !== undefined ) { 6988 a.push( ret ); 6989 } 6990 } 6991 } 6992 } 6993 6994 if ( a.length || alwaysNew ) { 6995 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a ); 6996 var apiSelector = api.selector; 6997 apiSelector.rows = selector.rows; 6998 apiSelector.cols = selector.cols; 6999 apiSelector.opts = selector.opts; 7000 return api; 7001 } 7002 return this; 7003 }, 7004 7005 7006 lastIndexOf: __arrayProto.lastIndexOf || function (obj, start) 7007 { 7008 // Bit cheeky... 7009 return this.indexOf.apply( this.toArray.reverse(), arguments ); 7010 }, 7011 7012 7013 length: 0, 7014 7015 7016 map: function ( fn ) 7017 { 7018 var a = []; 7019 7020 if ( __arrayProto.map ) { 7021 a = __arrayProto.map.call( this, fn, this ); 7022 } 7023 else { 7024 // Compatibility for browsers without EMCA-252-5 (JS 1.6) 7025 for ( var i=0, ien=this.length ; i<ien ; i++ ) { 7026 a.push( fn.call( this, this[i], i ) ); 7027 } 7028 } 7029 7030 return new _Api( this.context, a ); 7031 }, 7032 7033 7034 pluck: function ( prop ) 7035 { 7036 return this.map( function ( el ) { 7037 return el[ prop ]; 7038 } ); 7039 }, 7040 7041 pop: __arrayProto.pop, 7042 7043 7044 push: __arrayProto.push, 7045 7046 7047 // Does not return an API instance 7048 reduce: __arrayProto.reduce || function ( fn, init ) 7049 { 7050 return _fnReduce( this, fn, init, 0, this.length, 1 ); 7051 }, 7052 7053 7054 reduceRight: __arrayProto.reduceRight || function ( fn, init ) 7055 { 7056 return _fnReduce( this, fn, init, this.length-1, -1, -1 ); 7057 }, 7058 7059 7060 reverse: __arrayProto.reverse, 7061 7062 7063 // Object with rows, columns and opts 7064 selector: null, 7065 7066 7067 shift: __arrayProto.shift, 7068 7069 7070 sort: __arrayProto.sort, // ? name - order? 7071 7072 7073 splice: __arrayProto.splice, 7074 7075 7076 toArray: function () 7077 { 7078 return __arrayProto.slice.call( this ); 7079 }, 7080 7081 7082 to$: function () 7083 { 7084 return $( this ); 7085 }, 7086 7087 7088 toJQuery: function () 7089 { 7090 return $( this ); 7091 }, 7092 7093 7094 unique: function () 7095 { 7096 return new _Api( this.context, _unique(this) ); 7097 }, 7098 7099 7100 unshift: __arrayProto.unshift 7101 } ); 7102 7103 7104 _Api.extend = function ( scope, obj, ext ) 7105 { 7106 // Only extend API instances and static properties of the API 7107 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) { 7108 return; 7109 } 7110 7111 var 7112 i, ien, 7113 j, jen, 7114 struct, inner, 7115 methodScoping = function ( scope, fn, struc ) { 7116 return function () { 7117 var ret = fn.apply( scope, arguments ); 7118 7119 // Method extension 7120 _Api.extend( ret, ret, struc.methodExt ); 7121 return ret; 7122 }; 7123 }; 7124 7125 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 7126 struct = ext[i]; 7127 7128 // Value 7129 obj[ struct.name ] = typeof struct.val === 'function' ? 7130 methodScoping( scope, struct.val, struct ) : 7131 $.isPlainObject( struct.val ) ? 7132 {} : 7133 struct.val; 7134 7135 obj[ struct.name ].__dt_wrapper = true; 7136 7137 // Property extension 7138 _Api.extend( scope, obj[ struct.name ], struct.propExt ); 7139 } 7140 }; 7141 7142 7143 // @todo - Is there need for an augment function? 7144 // _Api.augment = function ( inst, name ) 7145 // { 7146 // // Find src object in the structure from the name 7147 // var parts = name.split('.'); 7148 7149 // _Api.extend( inst, obj ); 7150 // }; 7151 7152 7153 // [ 7154 // { 7155 // name: 'data' -- string - Property name 7156 // val: function () {}, -- function - Api method (or undefined if just an object 7157 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 7158 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property 7159 // }, 7160 // { 7161 // name: 'row' 7162 // val: {}, 7163 // methodExt: [ ... ], 7164 // propExt: [ 7165 // { 7166 // name: 'data' 7167 // val: function () {}, 7168 // methodExt: [ ... ], 7169 // propExt: [ ... ] 7170 // }, 7171 // ... 7172 // ] 7173 // } 7174 // ] 7175 7176 _Api.register = _api_register = function ( name, val ) 7177 { 7178 if ( $.isArray( name ) ) { 7179 for ( var j=0, jen=name.length ; j<jen ; j++ ) { 7180 _Api.register( name[j], val ); 7181 } 7182 return; 7183 } 7184 7185 var 7186 i, ien, 7187 heir = name.split('.'), 7188 struct = __apiStruct, 7189 key, method; 7190 7191 var find = function ( src, name ) { 7192 for ( var i=0, ien=src.length ; i<ien ; i++ ) { 7193 if ( src[i].name === name ) { 7194 return src[i]; 7195 } 7196 } 7197 return null; 7198 }; 7199 7200 for ( i=0, ien=heir.length ; i<ien ; i++ ) { 7201 method = heir[i].indexOf('()') !== -1; 7202 key = method ?
7203 heir[i].replace('()', '') : 7204 heir[i]; 7205 7206 var src = find( struct, key ); 7207 if ( ! src ) { 7208 src = { 7209 name: key, 7210 val: {}, 7211 methodExt: [], 7212 propExt: [] 7213 }; 7214 struct.push( src ); 7215 } 7216 7217 if ( i === ien-1 ) { 7218 src.val = val; 7219 } 7220 else { 7221 struct = method ? 7222 src.methodExt : 7223 src.propExt; 7224 } 7225 } 7226 }; 7227 7228 7229 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) { 7230 _Api.register( pluralName, val ); 7231 7232 _Api.register( singularName, function () { 7233 var ret = val.apply( this, arguments ); 7234 7235 if ( ret === this ) { 7236 // Returned item is the API instance that was passed in, return it 7237 return this; 7238 } 7239 else if ( ret instanceof _Api ) { 7240 // New API instance returned, want the value from the first item 7241 // in the returned array for the singular result. 7242 return ret.length ? 7243 $.isArray( ret[0] ) ? 7244 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced' 7245 ret[0] : 7246 undefined; 7247 } 7248 7249 // Non-API return - just fire it back 7250 return ret; 7251 } ); 7252 }; 7253 7254 7255 /** 7256 * Selector for HTML tables. Apply the given selector to the give array of 7257 * DataTables settings objects. 7258 * 7259 * @param {string|integer} [selector] jQuery selector string or integer 7260 * @param {array} Array of DataTables settings objects to be filtered 7261 * @return {array} 7262 * @ignore 7263 */ 7264 var __table_selector = function ( selector, a ) 7265 { 7266 // Integer is used to pick out a table by index 7267 if ( typeof selector === 'number' ) { 7268 return [ a[ selector ] ]; 7269 } 7270 7271 // Perform a jQuery selector on the table nodes 7272 var nodes = $.map( a, function (el, i) { 7273 return el.nTable; 7274 } ); 7275 7276 return $(nodes) 7277 .filter( selector ) 7278 .map( function (i) { 7279 // Need to translate back from the table node to the settings 7280 var idx = $.inArray( this, nodes ); 7281 return a[ idx ]; 7282 } ) 7283 .toArray(); 7284 }; 7285 7286 7287 7288 /** 7289 * Context selector for the API's context (i.e. the tables the API instance 7290 * refers to. 7291 * 7292 * @name DataTable.Api#tables 7293 * @param {string|integer} [selector] Selector to pick which tables the iterator 7294 * should operate on. If not given, all tables in the current context are 7295 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to 7296 * select multiple tables or as an integer to select a single table. 7297 * @returns {DataTable.Api} Returns a new API instance if a selector is given. 7298 */ 7299 _api_register( 'tables()', function ( selector ) { 7300 // A new instance is created if there was a selector specified 7301 return selector ? 7302 new _Api( __table_selector( selector, this.context ) ) : 7303 this; 7304 } ); 7305 7306 7307 _api_register( 'table()', function ( selector ) { 7308 var tables = this.tables( selector ); 7309 var ctx = tables.context; 7310 7311 // Truncate to the first matched table 7312 return ctx.length ? 7313 new _Api( ctx[0] ) : 7314 tables; 7315 } ); 7316 7317 7318 _api_registerPlural( 'tables().nodes()', 'table().node()' , function () { 7319 return this.iterator( 'table', function ( ctx ) { 7320 return ctx.nTable; 7321 }, 1 ); 7322 } ); 7323 7324 7325 _api_registerPlural( 'tables().body()', 'table().body()' , function () { 7326 return this.iterator( 'table', function ( ctx ) { 7327 return ctx.nTBody; 7328 }, 1 ); 7329 } ); 7330 7331 7332 _api_registerPlural( 'tables().header()', 'table().header()' , function () { 7333 return this.iterator( 'table', function ( ctx ) { 7334 return ctx.nTHead; 7335 }, 1 ); 7336 } ); 7337 7338 7339 _api_registerPlural( 'tables().footer()', 'table().footer()' , function () { 7340 return this.iterator( 'table', function ( ctx ) { 7341 return ctx.nTFoot; 7342 }, 1 ); 7343 } ); 7344 7345 7346 _api_registerPlural( 'tables().containers()', 'table().container()' , function () { 7347 return this.iterator( 'table', function ( ctx ) { 7348 return ctx.nTableWrapper; 7349 }, 1 ); 7350 } ); 7351 7352 7353 7354 /** 7355 * Redraw the tables in the current context. 7356 */ 7357 _api_register( 'draw()', function ( paging ) { 7358 return this.iterator( 'table', function ( settings ) { 7359 if ( paging === 'page' ) { 7360 _fnDraw( settings ); 7361 } 7362 else { 7363 if ( typeof paging === 'string' ) { 7364 paging = paging === 'full-hold' ? 7365 false : 7366 true; 7367 } 7368 7369 _fnReDraw( settings, paging===false ); 7370 } 7371 } ); 7372 } ); 7373 7374 7375 7376 /** 7377 * Get the current page index. 7378 * 7379 * @return {integer} Current page index (zero based) 7380 *//** 7381 * Set the current page. 7382 * 7383 * Note that if you attempt to show a page which does not exist, DataTables will 7384 * not throw an error, but rather reset the paging. 7385 * 7386 * @param {integer|string} action The paging action to take. This can be one of:
7387 * * `integer` - The page index to jump to 7388 * * `string` - An action to take: 7389 * * `first` - Jump to first page. 7390 * * `next` - Jump to the next page 7391 * * `previous` - Jump to previous page 7392 * * `last` - Jump to the last page. 7393 * @returns {DataTables.Api} this 7394 */ 7395 _api_register( 'page()', function ( action ) { 7396 if ( action === undefined ) { 7397 return this.page.info().page; // not an expensive call 7398 } 7399 7400 // else, have an action to take on all tables 7401 return this.iterator( 'table', function ( settings ) { 7402 _fnPageChange( settings, action ); 7403 } ); 7404 } ); 7405 7406 7407 /** 7408 * Paging information for the first table in the current context. 7409 * 7410 * If you require paging information for another table, use the `table()` method 7411 * with a suitable selector. 7412 * 7413 * @return {object} Object with the following properties set: 7414 * * `page` - Current page index (zero based - i.e. the first page is `0`) 7415 * * `pages` - Total number of pages 7416 * * `start` - Display index for the first record shown on the current page 7417 * * `end` - Display index for the last record shown on the current page 7418 * * `length` - Display length (number of records). Note that generally `start 7419 * + length = end`, but this is not always true, for example if there are 7420 * only 2 records to show on the final page, with a length of 10. 7421 * * `recordsTotal` - Full data set length 7422 * * `recordsDisplay` - Data set length once the current filtering criterion 7423 * are applied. 7424 */ 7425 _api_register( 'page.info()', function ( action ) { 7426 if ( this.context.length === 0 ) { 7427 return undefined; 7428 } 7429 7430 var 7431 settings = this.context[0], 7432 start = settings._iDisplayStart, 7433 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1, 7434 visRecords = settings.fnRecordsDisplay(), 7435 all = len === -1; 7436 7437 return { 7438 "page": all ? 0 : Math.floor( start / len ), 7439 "pages": all ? 1 : Math.ceil( visRecords / len ), 7440 "start": start, 7441 "end": settings.fnDisplayEnd(), 7442 "length": len, 7443 "recordsTotal": settings.fnRecordsTotal(), 7444 "recordsDisplay": visRecords, 7445 "serverSide": _fnDataSource( settings ) === 'ssp' 7446 }; 7447 } ); 7448 7449 7450 /** 7451 * Get the current page length. 7452 * 7453 * @return {integer} Current page length. Note `-1` indicates that all records 7454 * are to be shown. 7455 *//** 7456 * Set the current page length. 7457 * 7458 * @param {integer} Page length to set. Use `-1` to show all records. 7459 * @returns {DataTables.Api} this 7460 */ 7461 _api_register( 'page.len()', function ( len ) { 7462 // Note that we can't call this function 'length()' because `length` 7463 // is a Javascript property of functions which defines how many arguments 7464 // the function expects. 7465 if ( len === undefined ) { 7466 return this.context.length !== 0 ? 7467 this.context[0]._iDisplayLength : 7468 undefined; 7469 } 7470 7471 // else, set the page length 7472 return this.iterator( 'table', function ( settings ) { 7473 _fnLengthChange( settings, len ); 7474 } ); 7475 } ); 7476 7477 7478 7479 var __reload = function ( settings, holdPosition, callback ) { 7480 // Use the draw event to trigger a callback 7481 if ( callback ) { 7482 var api = new _Api( settings ); 7483 7484 api.one( 'draw', function () { 7485 callback( api.ajax.json() ); 7486 } ); 7487 } 7488 7489 if ( _fnDataSource( settings ) == 'ssp' ) { 7490 _fnReDraw( settings, holdPosition ); 7491 } 7492 else { 7493 _fnProcessingDisplay( settings, true ); 7494 7495 // Cancel an existing request 7496 var xhr = settings.jqXHR; 7497 if ( xhr && xhr.readyState !== 4 ) { 7498 xhr.abort(); 7499 } 7500 7501 // Trigger xhr 7502 _fnBuildAjax( settings, [], function( json ) { 7503 _fnClearTable( settings ); 7504 7505 var data = _fnAjaxDataSrc( settings, json ); 7506 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 7507 _fnAddData( settings, data[i] ); 7508 } 7509 7510 _fnReDraw( settings, holdPosition ); 7511 _fnProcessingDisplay( settings, false ); 7512 } ); 7513 } 7514 }; 7515 7516 7517 /** 7518 * Get the JSON response from the last Ajax request that DataTables made to the 7519 * server. Note that this returns the JSON from the first table in the current 7520 * context. 7521 * 7522 * @return {object} JSON received from the server. 7523 */ 7524 _api_register( 'ajax.json()', function () { 7525 var ctx = this.context; 7526 7527 if ( ctx.length > 0 ) { 7528 return ctx[0].json; 7529 } 7530 7531 // else return undefined; 7532 } ); 7533 7534 7535 /** 7536 * Get the data submitted in the last Ajax request 7537 */ 7538 _api_register( 'ajax.params()', function () { 7539 var ctx = this.context; 7540 7541 if ( ctx.length > 0 ) { 7542 return ctx[0].oAjaxData; 7543 } 7544 7545 // else return undefined; 7546 } ); 7547 7548 7549 /** 7550 * Reload tables from the Ajax data source. Note that this function will 7551 * automatically re-draw the table when the remote data has been loaded. 7552 * 7553 * @param {boolean} [reset=true] Reset (default) or hold the current paging 7554 * position. A full re-sort and re-filter is performed when this method is 7555 * called, which is why the pagination reset is the default action. 7556 * @returns {DataTables.Api} this 7557 */ 7558 _api_register( 'ajax.reload()', function ( callback, resetPaging ) { 7559 return this.iterator( 'table', function (settings) { 7560 __reload( settings, resetPaging===false, callback ); 7561 } ); 7562 } ); 7563 7564 7565 /** 7566 * Get the current Ajax URL. Note that this returns the URL from the first 7567 * table in the current context. 7568 * 7569 * @return {string} Current Ajax source URL 7570 *//** 7571 * Set the Ajax URL. Note that this will set the URL for all tables in the 7572 * current context. 7573 * 7574 * @param {string} url URL to set. 7575 * @returns {DataTables.Api} this 7576 */ 7577 _api_register( 'ajax.url()', function ( url ) { 7578 var ctx = this.context; 7579 7580 if ( url === undefined ) { 7581 // get 7582 if ( ctx.length === 0 ) { 7583 return undefined; 7584 } 7585 ctx = ctx[0]; 7586
7587 return ctx.ajax ? 7588 $.isPlainObject( ctx.ajax ) ? 7589 ctx.ajax.url : 7590 ctx.ajax : 7591 ctx.sAjaxSource; 7592 } 7593 7594 // set 7595 return this.iterator( 'table', function ( settings ) { 7596 if ( $.isPlainObject( settings.ajax ) ) { 7597 settings.ajax.url = url; 7598 } 7599 else { 7600 settings.ajax = url; 7601 } 7602 // No need to consider sAjaxSource here since DataTables gives priority 7603 // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any 7604 // value of `sAjaxSource` redundant. 7605 } ); 7606 } ); 7607 7608 7609 /** 7610 * Load data from the newly set Ajax URL. Note that this method is only 7611 * available when `ajax.url()` is used to set a URL. Additionally, this method 7612 * has the same effect as calling `ajax.reload()` but is provided for 7613 * convenience when setting a new URL. Like `ajax.reload()` it will 7614 * automatically redraw the table once the remote data has been loaded. 7615 * 7616 * @returns {DataTables.Api} this 7617 */ 7618 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) { 7619 // Same as a reload, but makes sense to present it for easy access after a 7620 // url change 7621 return this.iterator( 'table', function ( ctx ) { 7622 __reload( ctx, resetPaging===false, callback ); 7623 } ); 7624 } ); 7625 7626 7627 7628 7629 var _selector_run = function ( type, selector, selectFn, settings, opts ) 7630 { 7631 var 7632 out = [], res, 7633 a, i, ien, j, jen, 7634 selectorType = typeof selector; 7635 7636 // Can't just check for isArray here, as an API or jQuery instance might be 7637 // given with their array like look 7638 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) { 7639 selector = [ selector ]; 7640 } 7641 7642 for ( i=0, ien=selector.length ; i<ien ; i++ ) { 7643 a = selector[i] && selector[i].split ? 7644 selector[i].split(',') : 7645 [ selector[i] ]; 7646 7647 for ( j=0, jen=a.length ; j<jen ; j++ ) { 7648 res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] ); 7649 7650 if ( res && res.length ) { 7651 out = out.concat( res ); 7652 } 7653 } 7654 } 7655 7656 // selector extensions 7657 var ext = _ext.selector[ type ]; 7658 if ( ext.length ) { 7659 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 7660 out = ext[i]( settings, opts, out ); 7661 } 7662 } 7663 7664 return _unique( out ); 7665 }; 7666 7667 7668 var _selector_opts = function ( opts ) 7669 { 7670 if ( ! opts ) { 7671 opts = {}; 7672 } 7673 7674 // Backwards compatibility for 1.9- which used the terminology filter rather 7675 // than search 7676 if ( opts.filter && opts.search === undefined ) { 7677 opts.search = opts.filter; 7678 } 7679 7680 return $.extend( { 7681 search: 'none', 7682 order: 'current', 7683 page: 'all' 7684 }, opts ); 7685 }; 7686 7687 7688 var _selector_first = function ( inst ) 7689 { 7690 // Reduce the API instance to the first item found 7691 for ( var i=0, ien=inst.length ; i<ien ; i++ ) { 7692 if ( inst[i].length > 0 ) { 7693 // Assign the first element to the first item in the instance 7694 // and truncate the instance and context 7695 inst[0] = inst[i]; 7696 inst[0].length = 1; 7697 inst.length = 1; 7698 inst.context = [ inst.context[i] ]; 7699 7700 return inst; 7701 } 7702 } 7703 7704 // Not found - return an empty instance 7705 inst.length = 0; 7706 return inst; 7707 }; 7708 7709 7710 var _selector_row_indexes = function ( settings, opts ) 7711 { 7712 var 7713 i, ien, tmp, a=[], 7714 displayFiltered = settings.aiDisplay, 7715 displayMaster = settings.aiDisplayMaster; 7716 7717 var 7718 search = opts.search, // none, applied, removed 7719 order = opts.order, // applied, current, index (original - compatibility with 1.9) 7720 page = opts.page; // all, current 7721 7722 if ( _fnDataSource( settings ) == 'ssp' ) { 7723 // In server-side processing mode, most options are irrelevant since 7724 // rows not shown don't exist and the index order is the applied order 7725 // Removed is a special case - for consistency just return an empty 7726 // array 7727 return search === 'removed' ? 7728 [] : 7729 _range( 0, displayMaster.length ); 7730 } 7731 else if ( page == 'current' ) { 7732 // Current page implies that order=current and fitler=applied, since it is 7733 // fairly senseless otherwise, regardless of what order and search actually 7734 // are 7735 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) { 7736 a.push( displayFiltered[i] ); 7737 } 7738 } 7739 else if ( order == 'current' || order == 'applied' ) { 7740 a = search == 'none' ? 7741 displayMaster.slice() : // no search 7742 search == 'applied' ? 7743 displayFiltered.slice() : // applied search 7744 $.map( displayMaster, function (el, i) { // removed search 7745 return $.inArray( el, displayFiltered ) === -1 ? el : null; 7746 } ); 7747 } 7748 else if ( order == 'index' || order == 'original' ) { 7749 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 7750 if ( search == 'none' ) { 7751 a.push( i ); 7752 } 7753 else { // applied | removed 7754 tmp = $.inArray( i, displayFiltered ); 7755 7756 if ((tmp === -1 && search == 'removed') || 7757 (tmp >= 0 && search == 'applied') ) 7758 { 7759 a.push( i ); 7760 } 7761 } 7762 } 7763 } 7764 7765 return a; 7766 }; 7767 7768 7769 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 7770 * Rows 7771 * 7772 * {} - no selector - use all available rows 7773 * {integer} - row aoData index 7774 * {node} - TR node 7775 * {string} - jQuery selector to apply to the TR elements 7776 * {array} - jQuery array of nodes, or simply an array of TR nodes 7777 * 7778 */ 7779 7780 7781 var __row_selector = function ( settings, selector, opts ) 7782 { 7783 var run = function ( sel ) { 7784 var selInt = _intVal( sel ); 7785 var i, ien; 7786 7787 // Short cut - selector is a number and no options provided (default is 7788 // all records, so no need to check if the index is in there, since it 7789 // must be - dev error if the index doesn't exist). 7790 if ( selInt !== null && ! opts ) { 7791 return [ selInt ]; 7792 } 7793 7794 var rows = _selector_row_indexes( settings, opts ); 7795 7796 if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) { 7797 // Selector - integer 7798 return [ selInt ]; 7799 } 7800 else if ( ! sel ) { 7801 // Selector - none 7802 return rows; 7803 } 7804 7805 // Selector - function 7806 if ( typeof sel === 'function' ) { 7807 return $.map( rows, function (idx) { 7808 var row = settings.aoData[ idx ]; 7809 return sel( idx, row._aData, row.nTr ) ? idx : null; 7810 } ); 7811 } 7812
7813 // Get nodes in the order from the `rows` array with null values removed 7814 var nodes = _removeEmpty( 7815 _pluck_order( settings.aoData, rows, 'nTr' ) 7816 ); 7817 7818 // Selector - node 7819 if ( sel.nodeName ) { 7820 if ( sel._DT_RowIndex !== undefined ) { 7821 return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup 7822 } 7823 else if ( sel._DT_CellIndex ) { 7824 return [ sel._DT_CellIndex.row ]; 7825 } 7826 else { 7827 var host = $(sel).closest('*[data-dt-row]'); 7828 return host.length ? 7829 [ host.data('dt-row') ] : 7830 []; 7831 } 7832 } 7833 7834 // ID selector. Want to always be able to select rows by id, regardless 7835 // of if the tr element has been created or not, so can't rely upon 7836 // jQuery here - hence a custom implementation. This does not match 7837 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything, 7838 // but to select it using a CSS selector engine (like Sizzle or 7839 // querySelect) it would need to need to be escaped for some characters. 7840 // DataTables simplifies this for row selectors since you can select 7841 // only a row. A # indicates an id any anything that follows is the id - 7842 // unescaped. 7843 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) { 7844 // get row index from id 7845 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ]; 7846 if ( rowObj !== undefined ) { 7847 return [ rowObj.idx ]; 7848 } 7849 7850 // need to fall through to jQuery in case there is DOM id that 7851 // matches 7852 } 7853 7854 // Selector - jQuery selector string, array of nodes or jQuery object/ 7855 // As jQuery's .filter() allows jQuery objects to be passed in filter, 7856 // it also allows arrays, so this will cope with all three options 7857 return $(nodes) 7858 .filter( sel ) 7859 .map( function () { 7860 return this._DT_RowIndex; 7861 } ) 7862 .toArray(); 7863 }; 7864 7865 return _selector_run( 'row', selector, run, settings, opts ); 7866 }; 7867 7868 7869 _api_register( 'rows()', function ( selector, opts ) { 7870 // argument shifting 7871 if ( selector === undefined ) { 7872 selector = ''; 7873 } 7874 else if ( $.isPlainObject( selector ) ) { 7875 opts = selector; 7876 selector = ''; 7877 } 7878 7879 opts = _selector_opts( opts ); 7880 7881 var inst = this.iterator( 'table', function ( settings ) { 7882 return __row_selector( settings, selector, opts ); 7883 }, 1 ); 7884 7885 // Want argument shifting here and in __row_selector? 7886 inst.selector.rows = selector; 7887 inst.selector.opts = opts; 7888 7889 return inst; 7890 } ); 7891 7892 _api_register( 'rows().nodes()', function () { 7893 return this.iterator( 'row', function ( settings, row ) { 7894 return settings.aoData[ row ].nTr || undefined; 7895 }, 1 ); 7896 } ); 7897 7898 _api_register( 'rows().data()', function () { 7899 return this.iterator( true, 'rows', function ( settings, rows ) { 7900 return _pluck_order( settings.aoData, rows, '_aData' ); 7901 }, 1 ); 7902 } ); 7903 7904 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) { 7905 return this.iterator( 'row', function ( settings, row ) { 7906 var r = settings.aoData[ row ]; 7907 return type === 'search' ? r._aFilterData : r._aSortData; 7908 }, 1 ); 7909 } ); 7910 7911 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) { 7912 return this.iterator( 'row', function ( settings, row ) { 7913 _fnInvalidate( settings, row, src ); 7914 } ); 7915 } ); 7916 7917 _api_registerPlural( 'rows().indexes()', 'row().index()', function () { 7918 return this.iterator( 'row', function ( settings, row ) { 7919 return row; 7920 }, 1 ); 7921 } ); 7922 7923 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) { 7924 var a = []; 7925 var context = this.context; 7926 7927 // `iterator` will drop undefined values, but in this case we want them 7928 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 7929 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) { 7930 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData ); 7931 a.push( (hash === true ? '#' : '' )+ id ); 7932 } 7933 } 7934 7935 return new _Api( context, a ); 7936 } ); 7937 7938 _api_registerPlural( 'rows().remove()', 'row().remove()', function () { 7939 var that = this; 7940 7941 this.iterator( 'row', function ( settings, row, thatIdx ) { 7942 var data = settings.aoData; 7943 var rowData = data[ row ]; 7944 var i, ien, j, jen; 7945 var loopRow, loopCells; 7946 7947 data.splice( row, 1 ); 7948 7949 // Update the cached indexes 7950 for ( i=0, ien=data.length ; i<ien ; i++ ) { 7951 loopRow = data[i]; 7952 loopCells = loopRow.anCells; 7953 7954 // Rows 7955 if ( loopRow.nTr !== null ) { 7956 loopRow.nTr._DT_RowIndex = i; 7957 } 7958 7959 // Cells 7960 if ( loopCells !== null ) { 7961 for ( j=0, jen=loopCells.length ; j<jen ; j++ ) { 7962 loopCells[j]._DT_CellIndex.row = i; 7963 } 7964 } 7965 } 7966 7967 // Delete from the display arrays 7968 _fnDeleteIndex( settings.aiDisplayMaster, row ); 7969 _fnDeleteIndex( settings.aiDisplay, row ); 7970 _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes 7971 7972 // Check for an 'overflow' they case for displaying the table 7973 _fnLengthOverflow( settings ); 7974 7975 // Remove the row's ID reference if there is one 7976 var id = settings.rowIdFn( rowData._aData ); 7977 if ( id !== undefined ) { 7978 delete settings.aIds[ id ]; 7979 } 7980 } ); 7981 7982 this.iterator( 'table', function ( settings ) { 7983 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 7984 settings.aoData[i].idx = i; 7985 } 7986 } ); 7987 7988 return this; 7989 } ); 7990 7991 7992 _api_register( 'rows.add()', function ( rows ) { 7993 var newRows = this.iterator( 'table', function ( settings ) { 7994 var row, i, ien; 7995 var out = []; 7996 7997 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
7998 row = rows[i]; 7999 8000 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 8001 out.push( _fnAddTr( settings, row )[0] ); 8002 } 8003 else { 8004 out.push( _fnAddData( settings, row ) ); 8005 } 8006 } 8007 8008 return out; 8009 }, 1 ); 8010 8011 // Return an Api.rows() extended instance, so rows().nodes() etc can be used 8012 var modRows = this.rows( -1 ); 8013 modRows.pop(); 8014 $.merge( modRows, newRows ); 8015 8016 return modRows; 8017 } ); 8018 8019 8020 8021 8022 8023 /** 8024 * 8025 */ 8026 _api_register( 'row()', function ( selector, opts ) { 8027 return _selector_first( this.rows( selector, opts ) ); 8028 } ); 8029 8030 8031 _api_register( 'row().data()', function ( data ) { 8032 var ctx = this.context; 8033 8034 if ( data === undefined ) { 8035 // Get 8036 return ctx.length && this.length ? 8037 ctx[0].aoData[ this[0] ]._aData : 8038 undefined; 8039 } 8040 8041 // Set 8042 ctx[0].aoData[ this[0] ]._aData = data; 8043 8044 // Automatically invalidate 8045 _fnInvalidate( ctx[0], this[0], 'data' ); 8046 8047 return this; 8048 } ); 8049 8050 8051 _api_register( 'row().node()', function () { 8052 var ctx = this.context; 8053 8054 return ctx.length && this.length ? 8055 ctx[0].aoData[ this[0] ].nTr || null : 8056 null; 8057 } ); 8058 8059 8060 _api_register( 'row.add()', function ( row ) { 8061 // Allow a jQuery object to be passed in - only a single row is added from 8062 // it though - the first element in the set 8063 if ( row instanceof $ && row.length ) { 8064 row = row[0]; 8065 } 8066 8067 var rows = this.iterator( 'table', function ( settings ) { 8068 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 8069 return _fnAddTr( settings, row )[0]; 8070 } 8071 return _fnAddData( settings, row ); 8072 } ); 8073 8074 // Return an Api.rows() extended instance, with the newly added row selected 8075 return this.row( rows[0] ); 8076 } ); 8077 8078 8079 8080 var __details_add = function ( ctx, row, data, klass ) 8081 { 8082 // Convert to array of TR elements 8083 var rows = []; 8084 var addRow = function ( r, k ) { 8085 // Recursion to allow for arrays of jQuery objects 8086 if ( $.isArray( r ) || r instanceof $ ) { 8087 for ( var i=0, ien=r.length ; i<ien ; i++ ) { 8088 addRow( r[i], k ); 8089 } 8090 return; 8091 } 8092 8093 // If we get a TR element, then just add it directly - up to the dev 8094 // to add the correct number of columns etc 8095 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) { 8096 rows.push( r ); 8097 } 8098 else { 8099 // Otherwise create a row with a wrapper 8100 var created = $('<tr><td/></tr>').addClass( k ); 8101 $('td', created) 8102 .addClass( k ) 8103 .html( r ) 8104 [0].colSpan = _fnVisbleColumns( ctx ); 8105 8106 rows.push( created[0] ); 8107 } 8108 }; 8109 8110 addRow( data, klass ); 8111 8112 if ( row._details ) { 8113 row._details.remove(); 8114 } 8115 8116 row._details = $(rows); 8117 8118 // If the children were already shown, that state should be retained 8119 if ( row._detailsShow ) { 8120 row._details.insertAfter( row.nTr ); 8121 } 8122 }; 8123 8124 8125 var __details_remove = function ( api, idx ) 8126 { 8127 var ctx = api.context; 8128 8129 if ( ctx.length ) { 8130 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ]; 8131 8132 if ( row && row._details ) { 8133 row._details.remove(); 8134 8135 row._detailsShow = undefined; 8136 row._details = undefined; 8137 } 8138 } 8139 }; 8140 8141 8142 var __details_display = function ( api, show ) { 8143 var ctx = api.context; 8144 8145 if ( ctx.length && api.length ) { 8146 var row = ctx[0].aoData[ api[0] ]; 8147 8148 if ( row._details ) { 8149 row._detailsShow = show; 8150 8151 if ( show ) { 8152 row._details.insertAfter( row.nTr ); 8153 } 8154 else { 8155 row._details.detach(); 8156 } 8157 8158 __details_events( ctx[0] ); 8159 } 8160 } 8161 }; 8162 8163 8164 var __details_events = function ( settings ) 8165 { 8166 var api = new _Api( settings ); 8167 var namespace = '.dt.DT_details'; 8168 var drawEvent = 'draw'+namespace; 8169 var colvisEvent = 'column-visibility'+namespace; 8170 var destroyEvent = 'destroy'+namespace; 8171 var data = settings.aoData; 8172 8173 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent ); 8174 8175 if ( _pluck( data, '_details' ).length > 0 ) { 8176 // On each draw, insert the required elements into the document 8177 api.on( drawEvent, function ( e, ctx ) { 8178 if ( settings !== ctx ) { 8179 return; 8180 } 8181 8182 api.rows( {page:'current'} ).eq(0).each( function (idx) { 8183 // Internal data grab 8184 var row = data[ idx ]; 8185 8186 if ( row._detailsShow ) { 8187 row._details.insertAfter( row.nTr ); 8188 } 8189 } ); 8190 } ); 8191 8192 // Column visibility change - update the colspan 8193 api.on( colvisEvent, function ( e, ctx, idx, vis ) { 8194 if ( settings !== ctx ) { 8195 return; 8196 } 8197 8198 // Update the colspan for the details rows (note, only if it already has 8199 // a colspan) 8200 var row, visible = _fnVisbleColumns( ctx ); 8201 8202 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8203 row = data[i]; 8204 8205 if ( row._details ) { 8206 row._details.children('td[colspan]').attr('colspan', visible ); 8207 } 8208 } 8209 } ); 8210 8211 // Table destroyed - nuke any child rows 8212 api.on( destroyEvent, function ( e, ctx ) { 8213 if ( settings !== ctx ) { 8214 return; 8215 } 8216 8217 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8218 if ( data[i]._details ) { 8219 __details_remove( api, i ); 8220 } 8221 } 8222 } ); 8223 } 8224 }; 8225 8226 // Strings for the method names to help minification 8227 var _emp = ''; 8228 var _child_obj = _emp+'row().child'; 8229 var _child_mth = _child_obj+'()'; 8230 8231 // data can be: 8232 // tr 8233 // string 8234 // jQuery or array of any of the above 8235 _api_register( _child_mth, function ( data, klass ) { 8236 var ctx = this.context; 8237 8238 if ( data === undefined ) { 8239 // get 8240 return ctx.length && this.length ? 8241 ctx[0].aoData[ this[0] ]._details : 8242 undefined; 8243 } 8244 else if ( data === true ) { 8245 // show 8246 this.child.show(); 8247 } 8248 else if ( data === false ) { 8249 // remove 8250 __details_remove( this ); 8251 } 8252 else if ( ctx.length && this.length ) { 8253 // set 8254 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass ); 8255 } 8256 8257 return this; 8258 } ); 8259 8260 8261 _api_register( [ 8262 _child_obj+'.show()', 8263 _child_mth+'.show()' // only when `child()` was called with parameters (without 8264 ], function ( show ) { // it returns an object and this method is not executed) 8265 __details_display( this, true ); 8266 return this; 8267 } ); 8268 8269 8270 _api_register( [ 8271 _child_obj+'.hide()', 8272 _child_mth+'.hide()' // only when `child()` was called with parameters (without 8273 ], function () { // it returns an object and this method is not executed) 8274 __details_display( this, false ); 8275 return this; 8276 } ); 8277 8278 8279 _api_register( [ 8280 _child_obj+'.remove()', 8281 _child_mth+'.remove()' // only when `child()` was called with parameters (without 8282 ], function () { // it returns an object and this method is not executed) 8283 __details_remove( this ); 8284 return this; 8285 } ); 8286 8287 8288 _api_register( _child_obj+'.isShown()', function () { 8289 var ctx = this.context; 8290 8291 if ( ctx.length && this.length ) { 8292 // _detailsShown as false or undefined will fall through to return false 8293 return ctx[0].aoData[ this[0] ]._detailsShow || false; 8294 } 8295 return false; 8296 } ); 8297 8298 8299 8300 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8301 * Columns 8302 * 8303 * {integer} - column index (>=0 count from left, <0 count from right) 8304 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) 8305 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) 8306 * "{string}:name" - column name 8307 * "{string}" - jQuery selector on column header nodes 8308 * 8309 */ 8310 8311 // can be an array of these items, comma separated list, or an array of comma 8312 // separated lists 8313 8314 var __re_column_selector = /^(.+):(name|visIdx|visible)$/; 8315 8316 8317 // r1 and r2 are redundant - but it means that the parameters match for the 8318 // iterator callback in columns().data() 8319 var __columnData = function ( settings, column, r1, r2, rows ) { 8320 var a = []; 8321 for ( var row=0, ien=rows.length ; row<ien ; row++ ) { 8322 a.push( _fnGetCellData( settings, rows[row], column ) ); 8323 } 8324 return a; 8325 }; 8326 8327 8328 var __column_selector = function ( settings, selector, opts ) 8329 { 8330 var 8331 columns = settings.aoColumns, 8332 names = _pluck( columns, 'sName' ), 8333 nodes = _pluck( columns, 'nTh' ); 8334 8335 var run = function ( s ) { 8336 var selInt = _intVal( s ); 8337 8338 // Selector - all 8339 if ( s === '' ) { 8340 return _range( columns.length ); 8341 } 8342 8343 // Selector - index 8344 if ( selInt !== null ) { 8345 return [ selInt >= 0 ? 8346 selInt : // Count from left 8347 columns.length + selInt // Count from right (+ because its a negative value) 8348 ]; 8349 } 8350 8351 // Selector = function 8352 if ( typeof s === 'function' ) { 8353 var rows = _selector_row_indexes( settings, opts ); 8354 8355 return $.map( columns, function (col, idx) { 8356 return s( 8357 idx, 8358 __columnData( settings, idx, 0, 0, rows ), 8359 nodes[ idx ] 8360 ) ? idx : null; 8361 } ); 8362 } 8363 8364 // jQuery or string selector 8365 var match = typeof s === 'string' ? 8366 s.match( __re_column_selector ) : 8367 ''; 8368 8369 if ( match ) { 8370 switch( match[2] ) { 8371 case 'visIdx': 8372 case 'visible': 8373 var idx = parseInt( match[1], 10 ); 8374 // Visible index given, convert to column index 8375 if ( idx < 0 ) { 8376 // Counting from the right 8377 var visColumns = $.map( columns, function (col,i) { 8378 return col.bVisible ? i : null; 8379 } ); 8380 return [ visColumns[ visColumns.length + idx ] ]; 8381 } 8382 // Counting from the left 8383 return [ _fnVisibleToColumnIndex( settings, idx ) ]; 8384 8385 case 'name': 8386 // match by name. `names` is column index complete and in order 8387 return $.map( names, function (name, i) { 8388 return name === match[1] ? i : null; 8389 } ); 8390 8391 default: 8392 return []; 8393 } 8394 } 8395 8396 // Cell in the table body 8397 if ( s.nodeName && s._DT_CellIndex ) { 8398 return [ s._DT_CellIndex.column ]; 8399 } 8400 8401 // jQuery selector on the TH elements for the columns 8402 var jqResult = $( nodes ) 8403 .filter( s ) 8404 .map( function () { 8405 return $.inArray( this, nodes ); // `nodes` is column index complete and in order 8406 } ) 8407 .toArray(); 8408 8409 if ( jqResult.length || ! s.nodeName ) {
8410 return jqResult; 8411 } 8412 8413 // Otherwise a node which might have a `dt-column` data attribute, or be 8414 // a child or such an element 8415 var host = $(s).closest('*[data-dt-column]'); 8416 return host.length ? 8417 [ host.data('dt-column') ] : 8418 []; 8419 }; 8420 8421 return _selector_run( 'column', selector, run, settings, opts ); 8422 }; 8423 8424 8425 var __setColumnVis = function ( settings, column, vis, recalc ) { 8426 var 8427 cols = settings.aoColumns, 8428 col = cols[ column ], 8429 data = settings.aoData, 8430 row, cells, i, ien, tr; 8431 8432 // Get 8433 if ( vis === undefined ) { 8434 return col.bVisible; 8435 } 8436 8437 // Set 8438 // No change 8439 if ( col.bVisible === vis ) { 8440 return; 8441 } 8442 8443 if ( vis ) { 8444 // Insert column 8445 // Need to decide if we should use appendChild or insertBefore 8446 var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 ); 8447 8448 for ( i=0, ien=data.length ; i<ien ; i++ ) { 8449 tr = data[i].nTr; 8450 cells = data[i].anCells; 8451 8452 if ( tr ) { 8453 // insertBefore can act like appendChild if 2nd arg is null 8454 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null ); 8455 } 8456 } 8457 } 8458 else { 8459 // Remove column 8460 $( _pluck( settings.aoData, 'anCells', column ) ).detach(); 8461 } 8462 8463 // Common actions 8464 col.bVisible = vis; 8465 _fnDrawHead( settings, settings.aoHeader ); 8466 _fnDrawHead( settings, settings.aoFooter ); 8467 8468 if ( recalc === undefined || recalc ) { 8469 // Automatically adjust column sizing 8470 _fnAdjustColumnSizing( settings ); 8471 } 8472 8473 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, recalc] ); 8474 8475 _fnSaveState( settings ); 8476 }; 8477 8478 8479 _api_register( 'columns()', function ( selector, opts ) { 8480 // argument shifting 8481 if ( selector === undefined ) { 8482 selector = ''; 8483 } 8484 else if ( $.isPlainObject( selector ) ) { 8485 opts = selector; 8486 selector = ''; 8487 } 8488 8489 opts = _selector_opts( opts ); 8490 8491 var inst = this.iterator( 'table', function ( settings ) { 8492 return __column_selector( settings, selector, opts ); 8493 }, 1 ); 8494 8495 // Want argument shifting here and in _row_selector? 8496 inst.selector.cols = selector; 8497 inst.selector.opts = opts; 8498 8499 return inst; 8500 } ); 8501 8502 _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) { 8503 return this.iterator( 'column', function ( settings, column ) { 8504 return settings.aoColumns[column].nTh; 8505 }, 1 ); 8506 } ); 8507 8508 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) { 8509 return this.iterator( 'column', function ( settings, column ) { 8510 return settings.aoColumns[column].nTf; 8511 }, 1 ); 8512 } ); 8513 8514 _api_registerPlural( 'columns().data()', 'column().data()', function () { 8515 return this.iterator( 'column-rows', __columnData, 1 ); 8516 } ); 8517 8518 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () { 8519 return this.iterator( 'column', function ( settings, column ) { 8520 return settings.aoColumns[column].mData; 8521 }, 1 ); 8522 } ); 8523 8524 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) { 8525 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8526 return _pluck_order( settings.aoData, rows, 8527 type === 'search' ? '_aFilterData' : '_aSortData', column 8528 ); 8529 }, 1 ); 8530 } ); 8531 8532 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () { 8533 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8534 return _pluck_order( settings.aoData, rows, 'anCells', column ) ; 8535 }, 1 ); 8536 } ); 8537 8538 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) { 8539 return this.iterator( 'column', function ( settings, column ) { 8540 if ( vis === undefined ) { 8541 return settings.aoColumns[ column ].bVisible; 8542 } // else 8543 __setColumnVis( settings, column, vis, calc ); 8544 } ); 8545 } ); 8546 8547 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) { 8548 return this.iterator( 'column', function ( settings, column ) { 8549 return type === 'visible' ? 8550 _fnColumnIndexToVisible( settings, column ) : 8551 column; 8552 }, 1 ); 8553 } ); 8554 8555 _api_register( 'columns.adjust()', function () { 8556 return this.iterator( 'table', function ( settings ) { 8557 _fnAdjustColumnSizing( settings ); 8558 }, 1 ); 8559 } ); 8560 8561 _api_register( 'column.index()', function ( type, idx ) { 8562 if ( this.context.length !== 0 ) { 8563 var ctx = this.context[0]; 8564 8565 if ( type === 'fromVisible' || type === 'toData' ) { 8566 return _fnVisibleToColumnIndex( ctx, idx ); 8567 } 8568 else if ( type === 'fromData' || type === 'toVisible' ) { 8569 return _fnColumnIndexToVisible( ctx, idx ); 8570 } 8571 } 8572 } ); 8573 8574 _api_register( 'column()', function ( selector, opts ) { 8575 return _selector_first( this.columns( selector, opts ) ); 8576 } ); 8577 8578 8579 8580 8581 var __cell_selector = function ( settings, selector, opts ) 8582 { 8583 var data = settings.aoData; 8584 var rows = _selector_row_indexes( settings, opts ); 8585 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) ); 8586 var allCells = $( [].concat.apply([], cells) ); 8587 var row; 8588 var columns = settings.aoColumns.length; 8589 var a, i, ien, j, o, host; 8590 8591 var run = function ( s ) { 8592 var fnSelector = typeof s === 'function'; 8593 8594 if ( s === null || s === undefined || fnSelector ) { 8595 // All cells and function selectors 8596 a = []; 8597 8598 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8599 row = rows[i]; 8600 8601 for ( j=0 ; j<columns ; j++ ) { 8602 o = { 8603 row: row, 8604 column: j 8605 }; 8606 8607 if ( fnSelector ) { 8608 // Selector - function 8609 host = data[ row ]; 8610 8611 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) { 8612 a.push( o ); 8613 } 8614 } 8615 else { 8616 // Selector - all 8617 a.push( o ); 8618 } 8619 } 8620 } 8621 8622 return a; 8623 } 8624 8625 // Selector - index 8626 if ( $.isPlainObject( s ) ) { 8627 return [s]; 8628 } 8629 8630 // Selector - jQuery filtered cells 8631 var jqResult = allCells 8632 .filter( s ) 8633 .map( function (i, el) { 8634 return { // use a new object, in case someone changes the values 8635 row: el._DT_CellIndex.row, 8636 column: el._DT_CellIndex.column 8637 }; 8638 } ) 8639 .toArray(); 8640 8641 if ( jqResult.length || ! s.nodeName ) { 8642 return jqResult; 8643 } 8644 8645 // Otherwise the selector is a node, and there is one last option - the 8646 // element might be a child of an element which has dt-row and dt-column 8647 // data attributes 8648 host = $(s).closest('*[data-dt-row]'); 8649 return host.length ? 8650 [ { 8651 row: host.data('dt-row'), 8652 column: host.data('dt-column') 8653 } ] : 8654 []; 8655 }; 8656 8657 return _selector_run( 'cell', selector, run, settings, opts ); 8658 }; 8659 8660 8661 8662 8663 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) { 8664 // Argument shifting 8665 if ( $.isPlainObject( rowSelector ) ) { 8666 // Indexes 8667 if ( rowSelector.row === undefined ) { 8668 // Selector options in first parameter 8669 opts = rowSelector; 8670 rowSelector = null; 8671 } 8672 else { 8673 // Cell index objects in first parameter 8674 opts = columnSelector; 8675 columnSelector = null; 8676 } 8677 } 8678 if ( $.isPlainObject( columnSelector ) ) { 8679 opts = columnSelector; 8680 columnSelector = null; 8681 } 8682 8683 // Cell selector 8684 if ( columnSelector === null || columnSelector === undefined ) { 8685 return this.iterator( 'table', function ( settings ) { 8686 return __cell_selector( settings, rowSelector, _selector_opts( opts ) ); 8687 } ); 8688 } 8689 8690 // Row + column selector 8691 var columns = this.columns( columnSelector, opts ); 8692 var rows = this.rows( rowSelector, opts ); 8693 var a, i, ien, j, jen; 8694 8695 var cells = this.iterator( 'table', function ( settings, idx ) { 8696 a = []; 8697 8698 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) { 8699 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) { 8700 a.push( { 8701 row: rows[idx][i], 8702 column: columns[idx][j] 8703 } ); 8704 } 8705 } 8706 8707 return a; 8708 }, 1 ); 8709 8710 $.extend( cells.selector, { 8711 cols: columnSelector, 8712 rows: rowSelector, 8713 opts: opts 8714 } ); 8715 8716 return cells; 8717 } ); 8718 8719 8720 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () { 8721 return this.iterator( 'cell', function ( settings, row, column ) { 8722 var data = settings.aoData[ row ]; 8723 8724 return data && data.anCells ? 8725 data.anCells[ column ] : 8726 undefined; 8727 }, 1 ); 8728 } ); 8729 8730 8731 _api_register( 'cells().data()', function () { 8732 return this.iterator( 'cell', function ( settings, row, column ) { 8733 return _fnGetCellData( settings, row, column ); 8734 }, 1 ); 8735 } ); 8736 8737 8738 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) { 8739 type = type === 'search' ? '_aFilterData' : '_aSortData'; 8740 8741 return this.iterator( 'cell', function ( settings, row, column ) { 8742 return settings.aoData[ row ][ type ][ column ]; 8743 }, 1 ); 8744 } ); 8745 8746 8747 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) { 8748 return this.iterator( 'cell', function ( settings, row, column ) { 8749 return _fnGetCellData( settings, row, column, type ); 8750 }, 1 ); 8751 } ); 8752 8753 8754 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () { 8755 return this.iterator( 'cell', function ( settings, row, column ) { 8756 return { 8757 row: row, 8758 column: column, 8759 columnVisible: _fnColumnIndexToVisible( settings, column ) 8760 }; 8761 }, 1 ); 8762 } ); 8763 8764 8765 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) { 8766 return this.iterator( 'cell', function ( settings, row, column ) { 8767 _fnInvalidate( settings, row, src, column ); 8768 } ); 8769 } ); 8770 8771 8772 8773 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) { 8774 return _selector_first( this.cells( rowSelector, columnSelector, opts ) ); 8775 } ); 8776 8777 8778 _api_register( 'cell().data()', function ( data ) { 8779 var ctx = this.context; 8780 var cell = this[0]; 8781 8782 if ( data === undefined ) { 8783 // Get 8784 return ctx.length && cell.length ? 8785 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) : 8786 undefined; 8787 } 8788 8789 // Set 8790 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data ); 8791 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column ); 8792 8793 return this; 8794 } ); 8795 8796 8797 8798 /** 8799 * Get current ordering (sorting) that has been applied to the table. 8800 * 8801 * @returns {array} 2D array containing the sorting information for the first 8802 * table in the current context. Each element in the parent array represents 8803 * a column being sorted upon (i.e. multi-sorting with two columns would have 8804 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is 8805 * the column index that the sorting condition applies to, the second is the 8806 * direction of the sort (`desc` or `asc`) and, optionally, the third is the 8807 * index of the sorting order from the `column.sorting` initialisation array.
8808 *//** 8809 * Set the ordering for the table. 8810 * 8811 * @param {integer} order Column index to sort upon. 8812 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`) 8813 * @returns {DataTables.Api} this 8814 *//** 8815 * Set the ordering for the table. 8816 * 8817 * @param {array} order 1D array of sorting information to be applied. 8818 * @param {array} [...] Optional additional sorting conditions 8819 * @returns {DataTables.Api} this 8820 *//** 8821 * Set the ordering for the table. 8822 * 8823 * @param {array} order 2D array of sorting information to be applied. 8824 * @returns {DataTables.Api} this 8825 */ 8826 _api_register( 'order()', function ( order, dir ) { 8827 var ctx = this.context; 8828 8829 if ( order === undefined ) { 8830 // get 8831 return ctx.length !== 0 ? 8832 ctx[0].aaSorting : 8833 undefined; 8834 } 8835 8836 // set 8837 if ( typeof order === 'number' ) { 8838 // Simple column / direction passed in 8839 order = [ [ order, dir ] ]; 8840 } 8841 else if ( ! $.isArray( order[0] ) ) { 8842 // Arguments passed in (list of 1D arrays) 8843 order = Array.prototype.slice.call( arguments ); 8844 } 8845 // otherwise a 2D array was passed in 8846 8847 return this.iterator( 'table', function ( settings ) { 8848 settings.aaSorting = order.slice(); 8849 } ); 8850 } ); 8851 8852 8853 /** 8854 * Attach a sort listener to an element for a given column 8855 * 8856 * @param {node|jQuery|string} node Identifier for the element(s) to attach the 8857 * listener to. This can take the form of a single DOM node, a jQuery 8858 * collection of nodes or a jQuery selector which will identify the node(s). 8859 * @param {integer} column the column that a click on this node will sort on 8860 * @param {function} [callback] callback function when sort is run 8861 * @returns {DataTables.Api} this 8862 */ 8863 _api_register( 'order.listener()', function ( node, column, callback ) { 8864 return this.iterator( 'table', function ( settings ) { 8865 _fnSortAttachListener( settings, node, column, callback ); 8866 } ); 8867 } ); 8868 8869 8870 _api_register( 'order.fixed()', function ( set ) { 8871 if ( ! set ) { 8872 var ctx = this.context; 8873 var fixed = ctx.length ? 8874 ctx[0].aaSortingFixed : 8875 undefined; 8876 8877 return $.isArray( fixed ) ? 8878 { pre: fixed } : 8879 fixed; 8880 } 8881 8882 return this.iterator( 'table', function ( settings ) { 8883 settings.aaSortingFixed = $.extend( true, {}, set ); 8884 } ); 8885 } ); 8886 8887 8888 // Order by the selected column(s) 8889 _api_register( [ 8890 'columns().order()', 8891 'column().order()' 8892 ], function ( dir ) { 8893 var that = this; 8894 8895 return this.iterator( 'table', function ( settings, i ) { 8896 var sort = []; 8897 8898 $.each( that[i], function (j, col) { 8899 sort.push( [ col, dir ] ); 8900 } ); 8901 8902 settings.aaSorting = sort; 8903 } ); 8904 } ); 8905 8906 8907 8908 _api_register( 'search()', function ( input, regex, smart, caseInsen ) { 8909 var ctx = this.context; 8910 8911 if ( input === undefined ) { 8912 // get 8913 return ctx.length !== 0 ? 8914 ctx[0].oPreviousSearch.sSearch : 8915 undefined; 8916 } 8917 8918 // set 8919 return this.iterator( 'table', function ( settings ) { 8920 if ( ! settings.oFeatures.bFilter ) { 8921 return; 8922 } 8923 8924 _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, { 8925 "sSearch": input+"", 8926 "bRegex": regex === null ? false : regex, 8927 "bSmart": smart === null ? true : smart, 8928 "bCaseInsensitive": caseInsen === null ? true : caseInsen 8929 } ), 1 ); 8930 } ); 8931 } ); 8932 8933 8934 _api_registerPlural( 8935 'columns().search()', 8936 'column().search()', 8937 function ( input, regex, smart, caseInsen ) { 8938 return this.iterator( 'column', function ( settings, column ) { 8939 var preSearch = settings.aoPreSearchCols; 8940 8941 if ( input === undefined ) { 8942 // get 8943 return preSearch[ column ].sSearch; 8944 } 8945 8946 // set 8947 if ( ! settings.oFeatures.bFilter ) { 8948 return; 8949 } 8950 8951 $.extend( preSearch[ column ], { 8952 "sSearch": input+"", 8953 "bRegex": regex === null ? false : regex, 8954 "bSmart": smart === null ? true : smart, 8955 "bCaseInsensitive": caseInsen === null ? true : caseInsen 8956 } ); 8957 8958 _fnFilterComplete( settings, settings.oPreviousSearch, 1 ); 8959 } ); 8960 } 8961 ); 8962 8963 /* 8964 * State API methods 8965 */ 8966 8967 _api_register( 'state()', function () { 8968 return this.context.length ? 8969 this.context[0].oSavedState : 8970 null; 8971 } ); 8972 8973 8974 _api_register( 'state.clear()', function () { 8975 return this.iterator( 'table', function ( settings ) { 8976 // Save an empty object 8977 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} ); 8978 } ); 8979 } ); 8980 8981 8982 _api_register( 'state.loaded()', function () { 8983 return this.context.length ? 8984 this.context[0].oLoadedState : 8985 null; 8986 } ); 8987 8988 8989 _api_register( 'state.save()', function () { 8990 return this.iterator( 'table', function ( settings ) { 8991 _fnSaveState( settings ); 8992 } ); 8993 } ); 8994 8995 8996 8997 /**
8998 * Provide a common method for plug-ins to check the version of DataTables being 8999 * used, in order to ensure compatibility. 9000 * 9001 * @param {string} version Version string to check for, in the format "X.Y.Z". 9002 * Note that the formats "X" and "X.Y" are also acceptable. 9003 * @returns {boolean} true if this version of DataTables is greater or equal to 9004 * the required version, or false if this version of DataTales is not 9005 * suitable 9006 * @static 9007 * @dtopt API-Static 9008 * 9009 * @example 9010 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) ); 9011 */ 9012 DataTable.versionCheck = DataTable.fnVersionCheck = function( version ) 9013 { 9014 var aThis = DataTable.version.split('.'); 9015 var aThat = version.split('.'); 9016 var iThis, iThat; 9017 9018 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) { 9019 iThis = parseInt( aThis[i], 10 ) || 0; 9020 iThat = parseInt( aThat[i], 10 ) || 0; 9021 9022 // Parts are the same, keep comparing 9023 if (iThis === iThat) { 9024 continue; 9025 } 9026 9027 // Parts are different, return immediately 9028 return iThis > iThat; 9029 } 9030 9031 return true; 9032 }; 9033 9034 9035 /** 9036 * Check if a `<table>` node is a DataTable table already or not. 9037 * 9038 * @param {node|jquery|string} table Table node, jQuery object or jQuery 9039 * selector for the table to test. Note that if more than more than one 9040 * table is passed on, only the first will be checked 9041 * @returns {boolean} true the table given is a DataTable, or false otherwise 9042 * @static 9043 * @dtopt API-Static 9044 * 9045 * @example 9046 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { 9047 * $('#example').dataTable(); 9048 * } 9049 */ 9050 DataTable.isDataTable = DataTable.fnIsDataTable = function ( table ) 9051 { 9052 var t = $(table).get(0); 9053 var is = false; 9054 9055 $.each( DataTable.settings, function (i, o) { 9056 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null; 9057 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null; 9058 9059 if ( o.nTable === t || head === t || foot === t ) { 9060 is = true; 9061 } 9062 } ); 9063 9064 return is; 9065 }; 9066 9067 9068 /** 9069 * Get all DataTable tables that have been initialised - optionally you can 9070 * select to get only currently visible tables. 9071 * 9072 * @param {boolean} [visible=false] Flag to indicate if you want all (default) 9073 * or visible tables only. 9074 * @returns {array} Array of `table` nodes (not DataTable instances) which are 9075 * DataTables 9076 * @static 9077 * @dtopt API-Static 9078 * 9079 * @example 9080 * $.each( $.fn.dataTable.tables(true), function () { 9081 * $(table).DataTable().columns.adjust(); 9082 * } ); 9083 */ 9084 DataTable.tables = DataTable.fnTables = function ( visible ) 9085 { 9086 var api = false; 9087 9088 if ( $.isPlainObject( visible ) ) { 9089 api = visible.api; 9090 visible = visible.visible; 9091 } 9092 9093 var a = $.map( DataTable.settings, function (o) { 9094 if ( !visible || (visible && $(o.nTable).is(':visible')) ) { 9095 return o.nTable; 9096 } 9097 } ); 9098 9099 return api ? 9100 new _Api( a ) : 9101 a; 9102 }; 9103 9104 9105 /** 9106 * DataTables utility methods 9107 * 9108 * This namespace provides helper methods that DataTables uses internally to 9109 * create a DataTable, but which are not exclusively used only for DataTables. 9110 * These methods can be used by extension authors to save the duplication of 9111 * code. 9112 * 9113 * @namespace 9114 */ 9115 DataTable.util = { 9116 /** 9117 * Throttle the calls to a function. Arguments and context are maintained 9118 * for the throttled function. 9119 * 9120 * @param {function} fn Function to be called 9121 * @param {integer} freq Call frequency in mS 9122 * @return {function} Wrapped function 9123 */ 9124 throttle: _fnThrottle, 9125 9126 9127 /** 9128 * Escape a string such that it can be used in a regular expression 9129 * 9130 * @param {string} sVal string to escape 9131 * @returns {string} escaped string 9132 */ 9133 escapeRegex: _fnEscapeRegex 9134 }; 9135 9136 9137 /** 9138 * Convert from camel case parameters to Hungarian notation. This is made public 9139 * for the extensions to provide the same ability as DataTables core to accept 9140 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase 9141 * parameters. 9142 * 9143 * @param {object} src The model object which holds all parameters that can be 9144 * mapped. 9145 * @param {object} user The object to convert from camel case to Hungarian. 9146 * @param {boolean} force When set to `true`, properties which already have a 9147 * Hungarian value in the `user` object will be overwritten. Otherwise they 9148 * won't be. 9149 */ 9150 DataTable.camelToHungarian = _fnCamelToHungarian; 9151 9152 9153 9154 /** 9155 * 9156 */ 9157 _api_register( '$()', function ( selector, opts ) { 9158 var 9159 rows = this.rows( opts ).nodes(), // Get all rows 9160 jqRows = $(rows); 9161 9162 return $( [].concat( 9163 jqRows.filter( selector ).toArray(), 9164 jqRows.find( selector ).toArray() 9165 ) ); 9166 } ); 9167 9168 9169 // jQuery functions to operate on the tables 9170 $.each( [ 'on', 'one', 'off' ], function (i, key) { 9171 _api_register( key+'()', function ( /* event, handler */ ) { 9172 var args = Array.prototype.slice.call(arguments); 9173 9174 // Add the `dt` namespace automatically if it isn't already present 9175 if ( ! args[0].match(/\.dt\b/) ) { 9176 args[0] += '.dt'; 9177 } 9178 9179 var inst = $( this.tables().nodes() ); 9180 inst[key].apply( inst, args ); 9181 return this; 9182 } ); 9183 } ); 9184 9185 9186 _api_register( 'clear()', function () { 9187 return this.iterator( 'table', function ( settings ) { 9188 _fnClearTable( settings ); 9189 } ); 9190 } ); 9191 9192 9193 _api_register( 'settings()', function () { 9194 return new _Api( this.context, this.context ); 9195 } ); 9196 9197 9198 _api_register( 'init()', function () { 9199 var ctx = this.context; 9200 return ctx.length ? ctx[0].oInit : null; 9201 } ); 9202 9203 9204 _api_register( 'data()', function () { 9205 return this.iterator( 'table', function ( settings ) { 9206 return _pluck( settings.aoData, '_aData' ); 9207 } ).flatten(); 9208 } ); 9209 9210 9211 _api_register( 'destroy()', function ( remove ) { 9212 remove = remove || false; 9213 9214 return this.iterator( 'table', function ( settings ) { 9215 var orig = settings.nTableWrapper.parentNode; 9216 var classes = settings.oClasses; 9217 var table = settings.nTable; 9218 var tbody = settings.nTBody; 9219 var thead = settings.nTHead; 9220 var tfoot = settings.nTFoot; 9221 var jqTable = $(table); 9222 var jqTbody = $(tbody); 9223 var jqWrapper = $(settings.nTableWrapper); 9224 var rows = $.map( settings.aoData, function (r) { return r.nTr; } ); 9225 var i, ien; 9226 9227 // Flag to note that the table is currently being destroyed - no action 9228 // should be taken 9229 settings.bDestroying = true; 9230
9231 // Fire off the destroy callbacks for plug-ins etc 9232 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] ); 9233 9234 // If not being removed from the document, make all columns visible 9235 if ( ! remove ) { 9236 new _Api( settings ).columns().visible( true ); 9237 } 9238 9239 // Blitz all `DT` namespaced events (these are internal events, the 9240 // lowercase, `dt` events are user subscribed and they are responsible 9241 // for removing them 9242 jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT'); 9243 $(window).unbind('.DT-'+settings.sInstance); 9244 9245 // When scrolling we had to break the table up - restore it 9246 if ( table != thead.parentNode ) { 9247 jqTable.children('thead').detach(); 9248 jqTable.append( thead ); 9249 } 9250 9251 if ( tfoot && table != tfoot.parentNode ) { 9252 jqTable.children('tfoot').detach(); 9253 jqTable.append( tfoot ); 9254 } 9255 9256 settings.aaSorting = []; 9257 settings.aaSortingFixed = []; 9258 _fnSortingClasses( settings ); 9259 9260 $( rows ).removeClass( settings.asStripeClasses.join(' ') ); 9261 9262 $('th, td', thead).removeClass( classes.sSortable+' '+ 9263 classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone 9264 ); 9265 9266 if ( settings.bJUI ) { 9267 $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach(); 9268 $('th, td', thead).each( function () { 9269 var wrapper = $('div.'+classes.sSortJUIWrapper, this); 9270 $(this).append( wrapper.contents() ); 9271 wrapper.detach(); 9272 } ); 9273 } 9274 9275 // Add the TR elements back into the table in their original order 9276 jqTbody.children().detach(); 9277 jqTbody.append( rows ); 9278 9279 // Remove the DataTables generated nodes, events and classes 9280 var removedMethod = remove ? 'remove' : 'detach'; 9281 jqTable[ removedMethod ](); 9282 jqWrapper[ removedMethod ](); 9283 9284 // If we need to reattach the table to the document 9285 if ( ! remove && orig ) { 9286 // insertBefore acts like appendChild if !arg[1] 9287 orig.insertBefore( table, settings.nTableReinsertBefore ); 9288 9289 // Restore the width of the original table - was read from the style property, 9290 // so we can restore directly to that 9291 jqTable 9292 .css( 'width', settings.sDestroyWidth ) 9293 .removeClass( classes.sTable ); 9294 9295 // If the were originally stripe classes - then we add them back here. 9296 // Note this is not fool proof (for example if not all rows had stripe 9297 // classes - but it's a good effort without getting carried away 9298 ien = settings.asDestroyStripes.length; 9299 9300 if ( ien ) { 9301 jqTbody.children().each( function (i) { 9302 $(this).addClass( settings.asDestroyStripes[i % ien] ); 9303 } ); 9304 } 9305 } 9306 9307 /* Remove the settings object from the settings array */ 9308 var idx = $.inArray( settings, DataTable.settings ); 9309 if ( idx !== -1 ) { 9310 DataTable.settings.splice( idx, 1 ); 9311 } 9312 } ); 9313 } ); 9314 9315 9316 // Add the `every()` method for rows, columns and cells in a compact form 9317 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) { 9318 _api_register( type+'s().every()', function ( fn ) { 9319 var opts = this.selector.opts; 9320 var api = this; 9321 9322 return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) { 9323 // Rows and columns: 9324 // arg1 - index 9325 // arg2 - table counter 9326 // arg3 - loop counter 9327 // arg4 - undefined 9328 // Cells: 9329 // arg1 - row index 9330 // arg2 - column index 9331 // arg3 - table counter 9332 // arg4 - loop counter 9333 fn.call( 9334 api[ type ]( 9335 arg1, 9336 type==='cell' ? arg2 : opts, 9337 type==='cell' ? opts : undefined 9338 ), 9339 arg1, arg2, arg3, arg4 9340 ); 9341 } ); 9342 } ); 9343 } ); 9344 9345 9346 // i18n method for extensions to be able to use the language object from the 9347 // DataTable 9348 _api_register( 'i18n()', function ( token, def, plural ) { 9349 var ctx = this.context[0]; 9350 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage ); 9351 9352 if ( resolved === undefined ) { 9353 resolved = def; 9354 } 9355 9356 if ( plural !== undefined && $.isPlainObject( resolved ) ) { 9357 resolved = resolved[ plural ] !== undefined ? 9358 resolved[ plural ] : 9359 resolved._; 9360 } 9361 9362 return resolved.replace( '%d', plural ); // nb: plural might be undefined, 9363 } ); 9364 9365 /**
9366 * Version string for plug-ins to check compatibility. Allowed format is 9367 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used 9368 * only for non-release builds. See http://semver.org/ for more information. 9369 * @member 9370 * @type string 9371 * @default Version number 9372 */ 9373 DataTable.version = "1.10.11"; 9374 9375 /** 9376 * Private data store, containing all of the settings objects that are 9377 * created for the tables on a given page. 9378 * 9379 * Note that the `DataTable.settings` object is aliased to 9380 * `jQuery.fn.dataTableExt` through which it may be accessed and 9381 * manipulated, or `jQuery.fn.dataTable.settings`. 9382 * @member 9383 * @type array 9384 * @default [] 9385 * @private 9386 */ 9387 DataTable.settings = []; 9388 9389 /** 9390 * Object models container, for the various models that DataTables has 9391 * available to it. These models define the objects that are used to hold 9392 * the active state and configuration of the table. 9393 * @namespace 9394 */ 9395 DataTable.models = {}; 9396 9397 9398 9399 /** 9400 * Template object for the way in which DataTables holds information about 9401 * search information for the global filter and individual column filters. 9402 * @namespace 9403 */ 9404 DataTable.models.oSearch = { 9405 /** 9406 * Flag to indicate if the filtering should be case insensitive or not 9407 * @type boolean 9408 * @default true 9409 */ 9410 "bCaseInsensitive": true, 9411 9412 /** 9413 * Applied search term 9414 * @type string 9415 * @default <i>Empty string</i> 9416 */ 9417 "sSearch": "", 9418 9419 /** 9420 * Flag to indicate if the search term should be interpreted as a 9421 * regular expression (true) or not (false) and therefore and special 9422 * regex characters escaped. 9423 * @type boolean 9424 * @default false 9425 */ 9426 "bRegex": false, 9427 9428 /** 9429 * Flag to indicate if DataTables is to use its smart filtering or not. 9430 * @type boolean 9431 * @default true 9432 */ 9433 "bSmart": true 9434 }; 9435 9436 9437 9438 9439 /** 9440 * Template object for the way in which DataTables holds information about 9441 * each individual row. This is the object format used for the settings 9442 * aoData array. 9443 * @namespace 9444 */ 9445 DataTable.models.oRow = { 9446 /** 9447 * TR element for the row 9448 * @type node 9449 * @default null 9450 */ 9451 "nTr": null, 9452 9453 /** 9454 * Array of TD elements for each row. This is null until the row has been 9455 * created. 9456 * @type array nodes 9457 * @default [] 9458 */ 9459 "anCells": null, 9460 9461 /** 9462 * Data object from the original data source for the row. This is either 9463 * an array if using the traditional form of DataTables, or an object if 9464 * using mData options. The exact type will depend on the passed in 9465 * data from the data source, or will be an array if using DOM a data 9466 * source. 9467 * @type array|object 9468 * @default [] 9469 */ 9470 "_aData": [], 9471 9472 /** 9473 * Sorting data cache - this array is ostensibly the same length as the 9474 * number of columns (although each index is generated only as it is 9475 * needed), and holds the data that is used for sorting each column in the 9476 * row. We do this cache generation at the start of the sort in order that 9477 * the formatting of the sort data need be done only once for each cell 9478 * per sort. This array should not be read from or written to by anything 9479 * other than the master sorting methods. 9480 * @type array 9481 * @default null 9482 * @private 9483 */ 9484 "_aSortData": null, 9485 9486 /** 9487 * Per cell filtering data cache. As per the sort data cache, used to 9488 * increase the performance of the filtering in DataTables 9489 * @type array 9490 * @default null 9491 * @private 9492 */ 9493 "_aFilterData": null, 9494 9495 /** 9496 * Filtering data cache. This is the same as the cell filtering cache, but 9497 * in this case a string rather than an array. This is easily computed with 9498 * a join on `_aFilterData`, but is provided as a cache so the join isn't 9499 * needed on every search (memory traded for performance) 9500 * @type array 9501 * @default null 9502 * @private 9503 */ 9504 "_sFilterRow": null, 9505 9506 /** 9507 * Cache of the class name that DataTables has applied to the row, so we 9508 * can quickly look at this variable rather than needing to do a DOM check 9509 * on className for the nTr property. 9510 * @type string 9511 * @default <i>Empty string</i> 9512 * @private 9513 */ 9514 "_sRowStripe": "", 9515 9516 /** 9517 * Denote if the original data source was from the DOM, or the data source 9518 * object. This is used for invalidating data, so DataTables can 9519 * automatically read data from the original source, unless uninstructed 9520 * otherwise. 9521 * @type string 9522 * @default null 9523 * @private 9524 */ 9525 "src": null, 9526 9527 /** 9528 * Index in the aoData array. This saves an indexOf lookup when we have the 9529 * object, but want to know the index 9530 * @type integer 9531 * @default -1 9532 * @private 9533 */ 9534 "idx": -1 9535 }; 9536 9537 9538 /** 9539 * Template object for the column information object in DataTables. This object 9540 * is held in the settings aoColumns array and contains all the information that 9541 * DataTables needs about each individual column. 9542 * 9543 * Note that this object is related to {@link DataTable.defaults.column} 9544 * but this one is the internal data store for DataTables's cache of columns. 9545 * It should NOT be manipulated outside of DataTables. Any configuration should 9546 * be done through the initialisation options. 9547 * @namespace 9548 */ 9549 DataTable.models.oColumn = { 9550 /** 9551 * Column index. This could be worked out on-the-fly with $.inArray, but it 9552 * is faster to just hold it as a variable 9553 * @type integer 9554 * @default null 9555 */ 9556 "idx": null, 9557 9558 /** 9559 * A list of the columns that sorting should occur on when this column 9560 * is sorted. That this property is an array allows multi-column s
9560orting 9561 * to be defined for a column (for example first name / last name columns 9562 * would benefit from this). The values are integers pointing to the 9563 * columns to be sorted on (typically it will be a single integer pointing 9564 * at itself, but that doesn't need to be the case). 9565 * @type array 9566 */ 9567 "aDataSort": null, 9568 9569 /** 9570 * Define the sorting directions that are applied to the column, in sequence 9571 * as the column is repeatedly sorted upon - i.e. the first value is used 9572 * as the sorting direction when the column if first sorted (clicked on). 9573 * Sort it again (click again) and it will move on to the next index. 9574 * Repeat until loop. 9575 * @type array 9576 */ 9577 "asSorting": null, 9578 9579 /** 9580 * Flag to indicate if the column is searchable, and thus should be included 9581 * in the filtering or not. 9582 * @type boolean 9583 */ 9584 "bSearchable": null, 9585 9586 /** 9587 * Flag to indicate if the column is sortable or not. 9588 * @type boolean 9589 */ 9590 "bSortable": null, 9591 9592 /** 9593 * Flag to indicate if the column is currently visible in the table or not 9594 * @type boolean 9595 */ 9596 "bVisible": null, 9597 9598 /** 9599 * Store for manual type assignment using the `column.type` option. This 9600 * is held in store so we can manipulate the column's `sType` property. 9601 * @type string 9602 * @default null 9603 * @private 9604 */ 9605 "_sManualType": null, 9606 9607 /** 9608 * Flag to indicate if HTML5 data attributes should be used as the data 9609 * source for filtering or sorting. True is either are. 9610 * @type boolean 9611 * @default false 9612 * @private 9613 */ 9614 "_bAttrSrc": false, 9615 9616 /** 9617 * Developer definable function that is called whenever a cell is created (Ajax source, 9618 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 9619 * allowing you to modify the DOM element (add background colour for example) when the 9620 * element is available. 9621 * @type function 9622 * @param {element} nTd The TD node that has been created 9623 * @param {*} sData The Data for the cell 9624 * @param {array|object} oData The data for the whole row 9625 * @param {int} iRow The row index for the aoData data store 9626 * @default null 9627 */ 9628 "fnCreatedCell": null, 9629 9630 /** 9631 * Function to get data from a cell in a column. You should <b>never</b> 9632 * access data directly through _aData internally in DataTables - always use 9633 * the method attached to this property. It allows mData to function as 9634 * required. This function is automatically assigned by the column 9635 * initialisation method 9636 * @type function 9637 * @param {array|object} oData The data array/object for the array 9638 * (i.e. aoData[]._aData) 9639 * @param {string} sSpecific The specific data type you want to get - 9640 * 'display', 'type' 'filter' 'sort' 9641 * @returns {*} The data for the cell from the given row's data 9642 * @default null 9643 */ 9644 "fnGetData": null, 9645 9646 /** 9647 * Function to set data for a cell in the column. You should <b>never</b> 9648 * set the data directly to _aData internally in DataTables - always use 9649 * this method. It allows mData to function as required. This function 9650 * is automatically assigned by the column initialisation method 9651 * @type function 9652 * @param {array|object} oData The data array/object for the array 9653 * (i.e. aoData[]._aData) 9654 * @param {*} sValue Value to set 9655 * @default null 9656 */ 9657 "fnSetData": null, 9658 9659 /** 9660 * Property to read the value for the cells in the column from the data 9661 * source array / object. If null, then the default content is used, if a 9662 * function is given then the return from the function is used. 9663 * @type function|int|string|null 9664 * @default null 9665 */ 9666 "mData": null, 9667 9668 /** 9669 * Partner property to mData which is used (only when defined) to get 9670 * the data - i.e. it is basically the same as mData, but without the 9671 * 'set' option, and also the data fed to it is the result from mData. 9672 * This is the rendering method to match the data method of mData. 9673 * @type function|int|string|null 9674 * @default null 9675 */ 9676 "mRender": null, 9677 9678 /** 9679 * Unique header TH/TD element for this column - this is what the sorting 9680 * listener is attached to (if sorting is enabled.) 9681 * @type node 9682 * @default null 9683 */ 9684 "nTh": null, 9685 9686 /** 9687 * Unique footer TH/TD element for this column (if there is one). Not used
9688 * in DataTables as such, but can be used for plug-ins to reference the 9689 * footer for each column. 9690 * @type node 9691 * @default null 9692 */ 9693 "nTf": null, 9694 9695 /** 9696 * The class to apply to all TD elements in the table's TBODY for the column 9697 * @type string 9698 * @default null 9699 */ 9700 "sClass": null, 9701 9702 /** 9703 * When DataTables calculates the column widths to assign to each column, 9704 * it finds the longest string in each column and then constructs a 9705 * temporary table and reads the widths from that. The problem with this 9706 * is that "mmm" is much wider then "iiii", but the latter is a longer 9707 * string - thus the calculation can go wrong (doing it properly and putting 9708 * it into an DOM object and measuring that is horribly(!) slow). Thus as 9709 * a "work around" we provide this option. It will append its value to the 9710 * text that is found to be the longest string for the column - i.e. padding. 9711 * @type string 9712 */ 9713 "sContentPadding": null, 9714 9715 /** 9716 * Allows a default value to be given for a column's data, and will be used 9717 * whenever a null data source is encountered (this can be because mData 9718 * is set to null, or because the data source itself is null). 9719 * @type string 9720 * @default null 9721 */ 9722 "sDefaultContent": null, 9723 9724 /** 9725 * Name for the column, allowing reference to the column by name as well as 9726 * by index (needs a lookup to work by name). 9727 * @type string 9728 */ 9729 "sName": null, 9730 9731 /** 9732 * Custom sorting data type - defines which of the available plug-ins in 9733 * afnSortData the custom sorting will use - if any is defined. 9734 * @type string 9735 * @default std 9736 */ 9737 "sSortDataType": 'std', 9738 9739 /** 9740 * Class to be applied to the header element when sorting on this column 9741 * @type string 9742 * @default null 9743 */ 9744 "sSortingClass": null, 9745 9746 /** 9747 * Class to be applied to the header element when sorting on this column - 9748 * when jQuery UI theming is used. 9749 * @type string 9750 * @default null 9751 */ 9752 "sSortingClassJUI": null, 9753 9754 /** 9755 * Title of the column - what is seen in the TH element (nTh). 9756 * @type string 9757 */ 9758 "sTitle": null, 9759 9760 /** 9761 * Column sorting and filtering type 9762 * @type string 9763 * @default null 9764 */ 9765 "sType": null, 9766 9767 /** 9768 * Width of the column 9769 * @type string 9770 * @default null 9771 */ 9772 "sWidth": null, 9773 9774 /** 9775 * Width of the column when it was first "encountered" 9776 * @type string 9777 * @default null 9778 */ 9779 "sWidthOrig": null 9780 }; 9781 9782 9783 /* 9784 * Developer note: The properties of the object below are given in Hungarian 9785 * notation, that was used as the interface for DataTables prior to v1.10, however 9786 * from v1.10 onwards the primary interface is camel case. In order to avoid 9787 * breaking backwards compatibility utterly with this change, the Hungarian 9788 * version is still, internally the primary interface, but is is not documented 9789 * - hence the @name tags in each doc comment. This allows a Javascript function 9790 * to create a map from Hungarian notation to camel case (going the other direction 9791 * would require each property to be listed, which would at around 3K to the size 9792 * of DataTables, while this method is about a 0.5K hit. 9793 * 9794 * Ultimately this does pave the way for Hungarian notation to be dropped 9795 * completely, but that is a massive amount of work and will break current 9796 * installs (therefore is on-hold until v2). 9797 */ 9798 9799 /** 9800 * Initialisation options that can be given to DataTables at initialisation 9801 * time. 9802 * @namespace 9803 */ 9804 DataTable.defaults = { 9805 /** 9806 * An array of data to use for the table, passed in at initialisation which 9807 * will be used in preference to any data which is already in the DOM. This is 9808 * particularly useful for constructing tables purely in Javascript, for 9809 * example with a custom Ajax call. 9810 * @type array 9811 * @default null 9812 * 9813 * @dtopt Option 9814 * @name DataTable.defaults.data 9815 * 9816 * @example 9817 * // Using a 2D array data source 9818 * $(document).ready( function () { 9819 * $('#example').dataTable( { 9820 * "data": [ 9821 * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'], 9822 * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'], 9823 * ], 9824 * "columns": [ 9825 * { "title": "Engine" }, 9826 * { "title": "Browser" }, 9827 * { "title": "Platform" }, 9828 * { "title": "Version" }, 9829 * { "title": "Grade" } 9830 * ] 9831 * } ); 9832 * } ); 9833 * 9834 * @example 9835 * // Using an array of objects as a data source (`data`) 9836 * $(document).ready( function () { 9837 * $('#example').dataTable( { 9838 * "data": [ 9839 * {
9840 * "engine": "Trident", 9841 * "browser": "Internet Explorer 4.0", 9842 * "platform": "Win 95+", 9843 * "version": 4, 9844 * "grade": "X" 9845 * }, 9846 * { 9847 * "engine": "Trident", 9848 * "browser": "Internet Explorer 5.0", 9849 * "platform": "Win 95+", 9850 * "version": 5, 9851 * "grade": "C" 9852 * } 9853 * ], 9854 * "columns": [ 9855 * { "title": "Engine", "data": "engine" }, 9856 * { "title": "Browser", "data": "browser" }, 9857 * { "title": "Platform", "data": "platform" }, 9858 * { "title": "Version", "data": "version" }, 9859 * { "title": "Grade", "data": "grade" } 9860 * ] 9861 * } ); 9862 * } ); 9863 */ 9864 "aaData": null, 9865 9866 9867 /** 9868 * If ordering is enabled, then DataTables will perform a first pass sort on 9869 * initialisation. You can define which column(s) the sort is performed 9870 * upon, and the sorting direction, with this variable. The `sorting` array 9871 * should contain an array for each column to be sorted initially containing 9872 * the column's index and a direction string ('asc' or 'desc'). 9873 * @type array 9874 * @default [[0,'asc']] 9875 * 9876 * @dtopt Option 9877 * @name DataTable.defaults.order 9878 * 9879 * @example 9880 * // Sort by 3rd column first, and then 4th column 9881 * $(document).ready( function() { 9882 * $('#example').dataTable( { 9883 * "order": [[2,'asc'], [3,'desc']] 9884 * } ); 9885 * } ); 9886 * 9887 * // No initial sorting 9888 * $(document).ready( function() { 9889 * $('#example').dataTable( { 9890 * "order": [] 9891 * } ); 9892 * } ); 9893 */ 9894 "aaSorting": [[0,'asc']], 9895 9896 9897 /** 9898 * This parameter is basically identical to the `sorting` parameter, but 9899 * cannot be overridden by user interaction with the table. What this means 9900 * is that you could have a column (visible or hidden) which the sorting 9901 * will always be forced on first - any sorting after that (from the user) 9902 * will then be performed as required. This can be useful for grouping rows 9903 * together. 9904 * @type array 9905 * @default null 9906 * 9907 * @dtopt Option 9908 * @name DataTable.defaults.orderFixed 9909 * 9910 * @example 9911 * $(document).ready( function() { 9912 * $('#example').dataTable( { 9913 * "orderFixed": [[0,'asc']] 9914 * } ); 9915 * } ) 9916 */ 9917 "aaSortingFixed": [], 9918 9919 9920 /** 9921 * DataTables can be instructed to load data to display in the table from a 9922 * Ajax source. This option defines how that Ajax call is made and where to. 9923 * 9924 * The `ajax` property has three different modes of operation, depending on 9925 * how it is defined. These are: 9926 * 9927 * * `string` - Set the URL from where the data should be loaded from. 9928 * * `object` - Define properties for `jQuery.ajax`. 9929 * * `function` - Custom data get function 9930 * 9931 * `string` 9932 * -------- 9933 * 9934 * As a string, the `ajax` property simply defines the URL from which 9935 * DataTables will load data. 9936 * 9937 * `object` 9938 * -------- 9939 * 9940 * As an object, the parameters in the object are passed to 9941 * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control 9942 * of the Ajax request. DataTables has a number of default parameters which 9943 * you can override using this option. Please refer to the jQuery 9944 * documentation for a full description of the options available, although 9945 * the following parameters provide additional options in DataTables or 9946 * require special consideration: 9947 * 9948 * * `data` - As with jQuery, `data` can be provided as an object, but it 9949 * can also be used as a function to manipulate the data DataTables sends 9950 * to the server. The function takes a single parameter, an object of 9951 * parameters with the values that DataTables has readied for sending. An 9952 * object may be returned which will be merged into the DataTables 9953 * defaults, or you can add the items to the object that was passed in and 9954 * not return anything from the function. This supersedes `fnServerParams` 9955 * from DataTables 1.9-. 9956 * 9957 * * `dataSrc` - By default DataTables will look for the property `data` (or 9958 * `aaData` for compatibility with DataTables 1.9-) when obtaining data 9959 * from an Ajax source or for server-side processing - this parameter 9960 * allows that property to be changed. You can use Javascript dotted 9961 * object notation to get a data source for multiple levels of nesting, or 9962 * it my be used as a function. As a function it takes a single parameter, 9963 * the JSON returned from the server, which can be manipulated as 9964 * required, with the returned value being that used by DataTables as the 9965 * data source for the table. This supersedes `sAjaxDataProp` from 9966 * DataTables 1.9-. 9967 * 9968 * * `success` - Should not be overridden it is used internally in 9969 * DataTables. To manipulate / transform the data returned by the server 9970 * use `ajax.dataSrc`, or use `ajax` as a function (see below). 9971 * 9972 * `function` 9973 * ---------- 9974 * 9975 * As a function, making the Ajax call is left up to yourself allowing 9976 * complete control of the Ajax request. Indeed, if desired, a method other 9977 * than Ajax could be used to obtain the required data, such as Web storage 9978 * or an AIR database. 9979 * 9980 * The function is given four parameters and no return is required. The 9981 * parameters are: 9982 * 9983 * 1. _object_ - Data to send to the server 9984 * 2. _function_ - Callback function that must be executed when the required 9985 * data has been obtained. That data should be passed into the callback 9986 * as the only parameter 9987 * 3. _object_ - DataTables settings object for the table 9988 * 9989 * Note that this supersedes `fnServerData` from DataTables 1.9-. 9990 * 9991 * @type string|object|function 9992 * @default null 9993 * 9994 * @dtopt Option 9995 * @name DataTable.defaults.ajax
9996 * @since 1.10.0 9997 * 9998 * @example 9999 * // Get JSON data from a file via Ajax. 10000 * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default). 10001 * $('#example').dataTable( { 10002 * "ajax": "data.json" 10003 * } ); 10004 * 10005 * @example 10006 * // Get JSON data from a file via Ajax, using `dataSrc` to change 10007 * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`) 10008 * $('#example').dataTable( { 10009 * "ajax": { 10010 * "url": "data.json", 10011 * "dataSrc": "tableData" 10012 * } 10013 * } ); 10014 * 10015 * @example 10016 * // Get JSON data from a file via Ajax, using `dataSrc` to read data 10017 * // from a plain array rather than an array in an object 10018 * $('#example').dataTable( { 10019 * "ajax": { 10020 * "url": "data.json", 10021 * "dataSrc": "" 10022 * } 10023 * } ); 10024 * 10025 * @example 10026 * // Manipulate the data returned from the server - add a link to data 10027 * // (note this can, should, be done using `render` for the column - this 10028 * // is just a simple example of how the data can be manipulated). 10029 * $('#example').dataTable( { 10030 * "ajax": { 10031 * "url": "data.json", 10032 * "dataSrc": function ( json ) { 10033 * for ( var i=0, ien=json.length ; i<ien ; i++ ) { 10034 * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>'; 10035 * } 10036 * return json; 10037 * } 10038 * } 10039 * } ); 10040 * 10041 * @example 10042 * // Add data to the request 10043 * $('#example').dataTable( { 10044 * "ajax": { 10045 * "url": "data.json", 10046 * "data": function ( d ) { 10047 * return { 10048 * "extra_search": $('#extra').val() 10049 * }; 10050 * } 10051 * } 10052 * } ); 10053 * 10054 * @example 10055 * // Send request as POST 10056 * $('#example').dataTable( { 10057 * "ajax": { 10058 * "url": "data.json", 10059 * "type": "POST" 10060 * } 10061 * } ); 10062 * 10063 * @example 10064 * // Get the data from localStorage (could interface with a form for 10065 * // adding, editing and removing rows). 10066 * $('#example').dataTable( { 10067 * "ajax": function (data, callback, settings) { 10068 * callback( 10069 * JSON.parse( localStorage.getItem('dataTablesData') ) 10070 * ); 10071 * } 10072 * } ); 10073 */ 10074 "ajax": null, 10075 10076 10077 /** 10078 * This parameter allows you to readily specify the entries in the length drop 10079 * down menu that DataTables shows when pagination is enabled. It can be 10080 * either a 1D array of options which will be used for both the displayed 10081 * option and the value, or a 2D array which will use the array in the first 10082 * position as the value, and the array in the second position as the 10083 * displayed options (useful for language strings such as 'All'). 10084 * 10085 * Note that the `pageLength` property will be automatically set to the 10086 * first value given in this array, unless `pageLength` is also provided. 10087 * @type array 10088 * @default [ 10, 25, 50, 100 ] 10089 * 10090 * @dtopt Option 10091 * @name DataTable.defaults.lengthMenu 10092 * 10093 * @example 10094 * $(document).ready( function() { 10095 * $('#example').dataTable( { 10096 * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]] 10097 * } ); 10098 * } ); 10099 */ 10100 "aLengthMenu": [ 10, 25, 50, 100 ], 10101 10102 10103 /** 10104 * The `columns` option in the initialisation parameter allows you to define 10105 * details about the way individual columns behave. For a full list of 10106 * column options that can be set, please see 10107 * {@link DataTable.defaults.column}. Note that if you use `columns` to 10108 * define your columns, you must have an entry in the array for every single 10109 * column that you have in your table (these can be null if you don't which 10110 * to specify any options). 10111 * @member 10112 * 10113 * @name DataTable.defaults.column 10114 */ 10115 "aoColumns": null, 10116 10117 /** 10118 * Very similar to `columns`, `columnDefs` allows you to target a specific 10119 * column, multiple columns, or all columns, using the `targets` property of 10120 * each object in the array. This allows great flexibility when creating 10121 * tables, as the `columnDefs` arrays can be of any length, targeting the 10122 * columns you specifically want. `columnDefs` may use any of the column 10123 * options available: {@link DataTable.defaults.column}, but it _must_ 10124 * have `targets` defined in each object in the array. Values in the `targets` 10125 * array may be: 10126 * <ul> 10127 * <li>
10127a string - class name will be matched on the TH for the column</li> 10128 * <li>0 or a positive integer - column index counting from the left</li> 10129 * <li>a negative integer - column index counting from the right</li> 10130 * <li>the string "_all" - all columns (i.e. assign a default)</li> 10131 * </ul> 10132 * @member 10133 * 10134 * @name DataTable.defaults.columnDefs 10135 */ 10136 "aoColumnDefs": null, 10137 10138 10139 /** 10140 * Basically the same as `search`, this parameter defines the individual column 10141 * filtering state at initialisation time. The array must be of the same size 10142 * as the number of columns, and each element be an object with the parameters 10143 * `search` and `escapeRegex` (the latter is optional). 'null' is also 10144 * accepted and the default will be used. 10145 * @type array 10146 * @default [] 10147 * 10148 * @dtopt Option 10149 * @name DataTable.defaults.searchCols 10150 * 10151 * @example 10152 * $(document).ready( function() { 10153 * $('#example').dataTable( { 10154 * "searchCols": [ 10155 * null, 10156 * { "search": "My filter" }, 10157 * null, 10158 * { "search": "^[0-9]", "escapeRegex": false } 10159 * ] 10160 * } ); 10161 * } ) 10162 */ 10163 "aoSearchCols": [], 10164 10165 10166 /** 10167 * An array of CSS classes that should be applied to displayed rows. This 10168 * array may be of any length, and DataTables will apply each class 10169 * sequentially, looping when required. 10170 * @type array 10171 * @default null <i>
10171Will take the values determined by the `oClasses.stripe*` 10172 * options</i> 10173 * 10174 * @dtopt Option 10175 * @name DataTable.defaults.stripeClasses 10176 * 10177 * @example 10178 * $(document).ready( function() { 10179 * $('#example').dataTable( { 10180 * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ] 10181 * } ); 10182 * } ) 10183 */ 10184 "asStripeClasses": null, 10185 10186 10187 /** 10188 * Enable or disable automatic column width calculation. This can be disabled 10189 * as an optimisation (it takes some time to calculate the widths) if the 10190 * tables widths are passed in using `columns`. 10191 * @type boolean 10192 * @default true 10193 * 10194 * @dtopt Features 10195 * @name DataTable.defaults.autoWidth 10196 * 10197 * @example 10198 * $(document).ready( function () { 10199 * $('#example').dataTable( { 10200 * "autoWidth": false 10201 * } ); 10202 * } ); 10203 */ 10204 "bAutoWidth": true, 10205 10206 10207 /** 10208 * Deferred rendering can provide DataTables with a huge speed boost when you 10209 * are using an Ajax or JS data source for the table. This option, when set to 10210 * true, will cause DataTables to defer the creation of the table elements for 10211 * each row until they are needed for a draw - saving a significant amount of 10212 * time. 10213 * @type boolean 10214 * @default false 10215 * 10216 * @dtopt Features 10217 * @name DataTable.defaults.deferRender 10218 * 10219 * @example 10220 * $(document).ready( function() { 10221 * $('#example').dataTable( { 10222 * "ajax": "sources/arrays.txt", 10223 * "deferRender": true 10224 * } ); 10225 * } ); 10226 */ 10227 "bDeferRender": false, 10228 10229 10230 /** 10231 * Replace a DataTable which matches the given selector and replace it with 10232 * one which has the properties of the new initialisation object passed. If no 10233 * table matches the selector, then the new DataTable will be constructed as 10234 * per normal. 10235 * @type boolean 10236 * @default false 10237 * 10238 * @dtopt Options 10239 * @name DataTable.defaults.destroy 10240 * 10241 * @example 10242 * $(document).ready( function() { 10243 * $('#example').dataTable( { 10244 * "srollY": "200px", 10245 * "paginate": false 10246 * } ); 10247 * 10248 * // Some time later.... 10249 * $('#example').dataTable( { 10250 * "filter": false, 10251 * "destroy": true 10252 * } ); 10253 * } ); 10254 */ 10255 "bDestroy": false, 10256 10257 10258 /** 10259 * Enable or disable filtering of data. Filtering in DataTables is "smart" in 10260 * that it allows the end user to input multiple words (space separated) and 10261 * will match a row containing those words, even if not in the order that was 10262 * specified (this allow matching across multiple columns). Note that if you 10263 * wish to use filtering in DataTables this must remain 'true' - to remove the 10264 * default filtering input box and retain filtering abilities, please use 10265 * {@link DataTable.defaults.dom}. 10266 * @type boolean 10267 * @default true 10268 * 10269 * @dtopt Features 10270 * @name DataTable.defaults.searching 10271 * 10272 * @example 10273 * $(document).ready( function () { 10274 * $('#example').dataTable( { 10275 * "searching": false 10276 * } ); 10277 * } ); 10278 */ 10279 "bFilter": true, 10280 10281 10282 /** 10283 * Enable or disable the table information display. This shows information 10284 * about the data that is currently visible on the page, including information 10285 * about filtered data if that action is being performed. 10286 * @type boolean 10287 * @default true 10288 * 10289 * @dtopt Features 10290 * @name DataTable.defaults.info 10291 * 10292 * @example 10293 * $(document).ready( function () { 10294 * $('#example').dataTable( { 10295 * "info": false 10296 * } ); 10297 * } ); 10298 */ 10299 "bInfo": true, 10300 10301 10302 /** 10303 * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some 10304 * slightly different and additional mark-up from what DataTables has 10305 * traditionally used). 10306 * @type boolean 10307 * @default false 10308 * 10309 * @dtopt Features 10310 * @name DataTable.defaults.jQueryUI 10311 * 10312 * @example 10313 * $(document).ready( function() { 10314 * $('#example').dataTable( { 10315 * "jQueryUI": true 10316 * } ); 10317 * } ); 10318 */ 10319 "bJQueryUI": false, 10320 10321 10322 /** 10323 * Allows the end user to select the size of a formatted page from a select 10324 * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`). 10325 * @type boolean 10326 * @default true 10327 * 10328 * @dtopt Features 10329 * @name DataTable.defaults.lengthChange 10330 * 10331 * @example 10332 * $(document).ready( function () { 10333 * $('#example').dataTable( { 10334 * "lengthChange": false 10335 * } ); 10336 * } ); 10337 */ 10338 "bLengthChange": true, 10339 10340 10341 /** 10342 * Enable or disable pagination. 10343 * @type boolean 10344 * @default true 10345 * 10346 * @dtopt Features 10347 * @name DataTable.defaults.paging 10348 * 10349 * @example 10350 * $(document).ready( function () { 10351 * $('#example').dataTable( { 10352 * "paging": false 10353 * } ); 10354 * } ); 10355 */ 10356 "bPaginate": true, 10357 10358 10359 /** 10360 * Enable or disable the display of a 'processing' indicator when the table is 10361 * being processed (e.g. a sort). This is particularly useful for tables with 10362 * large amounts of data where it can take a noticeable amount of time to sort 10363 * the entries. 10364 * @type boolean 10365 * @default false 10366 * 10367 * @dtopt Features 10368 * @name DataTable.defaults.processing 10369 * 10370 * @example 10371 * $(document).ready( function () { 10372 * $('#example').dataTable( { 10373 * "processing": true 10374 * } ); 10375 * } ); 10376 */ 10377 "bProcessing": false, 10378 10379 10380 /** 10381 * Retrieve the DataTables object for the given selector. Note that if the 10382 * table has already been initialised, this parameter will cause DataTables 10383 * to simply return the object that has already been set up - it will not take 10384 * account of any changes you might have made to the initialisation object 10385 * passed to DataTables (setting this parameter to true is an acknowledgement 10386 * that you understand this). `destroy` can be used to reinitialise a table if 10387 * you need. 10388 * @type boolean 10389 * @default false 10390 * 10391 * @dtopt Options 10392 * @name DataTable.defaults.retrieve 10393 * 10394 * @example 10395 * $(document).ready( function() { 10396 * initTable(); 10397 * tableActions(); 10398 * } ); 10399 * 10400 * function initTable () 10401 * { 10402 * return $('#example').dataTable( { 10403 * "scrollY": "200px", 10404 * "paginate": false, 10405 * "retrieve": true 10406 * } ); 10407 * } 10408 * 10409 * function tableActions () 10410 * { 10411 * var table = initTable(); 10412 * // perform API operations with oTable 10413 * } 10414 */ 10415 "bRetrieve": false, 10416 10417 10418 /** 10419 * When vertical (y) scrolling is enabled, DataTables will force the height of
10420 * the table's viewport to the given height at all times (useful for layout). 10421 * However, this can look odd when filtering data down to a small data set, 10422 * and the footer is left "floating" further down. This parameter (when 10423 * enabled) will cause DataTables to collapse the table's viewport down when 10424 * the result set will fit within the given Y height. 10425 * @type boolean 10426 * @default false 10427 * 10428 * @dtopt Options 10429 * @name DataTable.defaults.scrollCollapse 10430 * 10431 * @example 10432 * $(document).ready( function() { 10433 * $('#example').dataTable( { 10434 * "scrollY": "200", 10435 * "scrollCollapse": true 10436 * } ); 10437 * } ); 10438 */ 10439 "bScrollCollapse": false, 10440 10441 10442 /** 10443 * Configure DataTables to use server-side processing. Note that the 10444 * `ajax` parameter must also be given in order to give DataTables a 10445 * source to obtain the required data for each draw. 10446 * @type boolean 10447 * @default false 10448 * 10449 * @dtopt Features 10450 * @dtopt Server-side 10451 * @name DataTable.defaults.serverSide 10452 * 10453 * @example 10454 * $(document).ready( function () { 10455 * $('#example').dataTable( { 10456 * "serverSide": true, 10457 * "ajax": "xhr.php" 10458 * } ); 10459 * } ); 10460 */ 10461 "bServerSide": false, 10462 10463 10464 /** 10465 * Enable or disable sorting of columns. Sorting of individual columns can be 10466 * disabled by the `sortable` option for each column. 10467 * @type boolean 10468 * @default true 10469 * 10470 * @dtopt Features 10471 * @name DataTable.defaults.ordering 10472 * 10473 * @example 10474 * $(document).ready( function () { 10475 * $('#example').dataTable( { 10476 * "ordering": false 10477 * } ); 10478 * } ); 10479 */ 10480 "bSort": true, 10481 10482 10483 /** 10484 * Enable or display DataTables' ability to sort multiple columns at the 10485 * same time (activated by shift-click by the user). 10486 * @type boolean 10487 * @default true 10488 * 10489 * @dtopt Options 10490 * @name DataTable.defaults.orderMulti 10491 * 10492 * @example 10493 * // Disable multiple column sorting ability 10494 * $(document).ready( function () { 10495 * $('#example').dataTable( { 10496 * "orderMulti": false 10497 * } ); 10498 * } ); 10499 */ 10500 "bSortMulti": true, 10501 10502 10503 /** 10504 * Allows control over whether DataTables should use the top (true) unique 10505 * cell that is found for a single column, or the bottom (false - default). 10506 * This is useful when using complex headers. 10507 * @type boolean 10508 * @default false 10509 * 10510 * @dtopt Options 10511 * @name DataTable.defaults.orderCellsTop 10512 * 10513 * @example 10514 * $(document).ready( function() { 10515 * $('#example').dataTable( { 10516 * "orderCellsTop": true 10517 * } ); 10518 * } ); 10519 */ 10520 "bSortCellsTop": false, 10521 10522 10523 /** 10524 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and 10525 * `sorting\_3` to the columns which are currently being sorted on. This is 10526 * presented as a feature switch as it can increase processing time (while 10527 * classes are removed and added) so for large data sets you might want to 10528 * turn this off. 10529 * @type boolean 10530 * @default true 10531 * 10532 * @dtopt Features 10533 * @name DataTable.defaults.orderClasses 10534 * 10535 * @example 10536 * $(document).ready( function () { 10537 * $('#example').dataTable( { 10538 * "orderClasses": false 10539 * } ); 10540 * } ); 10541 */ 10542 "bSortClasses": true, 10543 10544 10545 /** 10546 * Enable or disable state saving. When enabled HTML5 `localStorage` will be 10547 * used to save table display information such as pagination information, 10548 * display length, filtering and sorting. As such when the end user reloads 10549 * the page the display display will match what thy had previously set up. 10550 * 10551 * Due to the use of `localStorage` the default state saving is not supported 10552 * in IE6 or 7. If state saving is required in those browsers, use 10553 * `stateSaveCallback` to provide a storage solution such as cookies. 10554 * @type boolean 10555 * @default false 10556 * 10557 * @dtopt Features 10558 * @name DataTable.defaults.stateSave 10559 * 10560 * @example 10561 * $(document).ready( function () { 10562 * $('#example').dataTable( { 10563 * "stateSave": true 10564 * } ); 10565 * } ); 10566 */ 10567 "bStateSave": false, 10568 10569 10570 /** 10571 * This function is called when a TR element is created (and all TD child 10572 * elements have been inserted), or registered if using a DOM source, allowing 10573 * manipulation of the TR element (adding classes etc). 10574 * @type function 10575 * @param {node} row "TR" element for the current row 10576 * @param {array} data Raw data array for this row 10577 * @param {int} dataIndex The index of this row in the internal aoData array 10578 * 10579 * @dtopt Callbacks 10580 * @name DataTable.defaults.createdRow 10581 * 10582 * @example 10583 * $(document).ready( function() { 10584 * $('#example').dataTable( { 10585 * "createdRow": function( row, data, dataIndex ) { 10586 * // Bold the grade for all 'A' grade browsers 10587 * if ( data[4] == "A" ) 10588 * { 10589 * $('td:eq(4)', row).html( '<b>A</b>' ); 10590 * } 10591 * } 10592 * } ); 10593 * } ); 10594 */ 10595 "fnCreatedRow": null, 10596 10597 10598 /** 10599 * This function is called on every 'draw' event, and allows you to 10600 * dynamically modify any aspect you want about the created DOM. 10601 * @type function 10602 * @param {object} settings DataTables settings object 10603 * 10604 * @dtopt Callbacks 10605 * @name DataTable.defaults.drawCallback 10606 * 10607 * @example 10608 * $(document).ready( function() { 10609 * $('#example').dataTable( { 10610 * "drawCallback": function( settings ) { 10611 * alert( 'DataTables has redrawn the table' ); 10612 * } 10613 * } ); 10614 * } ); 10615 */ 10616 "fnDrawCallback": null, 10617 10618 10619 /** 10620 * Identical to fnHeaderCallback() but for the table footer this function 10621 * allows you to modify the table footer on every 'draw' event. 10622 * @type function 10623 * @param {node} foot "TR" element for the footer 10624 * @param {array} data Full table data (as derived from the original HTML) 10625 * @param {int} start Index for the current display starting point in the 10626 * display array 10627 * @param {int} end Index for the current display ending point in the 10628 * display array 10629 * @param {array int} display Index array to translate the visual position 10630 * to the full data array 10631 * 10632 * @dtopt Callbacks 10633 * @name DataTable.defaults.footerCallback 10634 * 10635 * @example 10636 * $(document).ready( function() { 10637 * $('#example').dataTable( { 10638 * "footerCallback": function( tfoot, data, start, end, display ) { 10639 * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start; 10640 * } 10641 * } ); 10642 * } ) 10643 */ 10644 "fnFooterCallback": null, 10645 10646 10647 /** 10648 * When rendering large numbers in the information element for the table 10649 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers 10650 * to have a comma separator for the 'thousands' units (e.g. 1 million is 10651 * rendered as "1,000,000") to help readability for the end user. This 10652 * function will override the default method DataTables uses. 10653 * @type function 10654 * @member 10655 * @param {int} toFormat number to be formatted 10656 * @returns {string} formatted string for DataTables to show the number 10657 * 10658 * @dtopt Callbacks 10659 * @name DataTable.defaults.formatNumber 10660 * 10661 * @example 10662 * // Format a number using a single quote for the separator (note that
10663 * // this can also be done with the language.thousands option) 10664 * $(document).ready( function() { 10665 * $('#example').dataTable( { 10666 * "formatNumber": function ( toFormat ) { 10667 * return toFormat.toString().replace( 10668 * /\B(?=(\d{3})+(?!\d))/g, "'" 10669 * ); 10670 * }; 10671 * } ); 10672 * } ); 10673 */ 10674 "fnFormatNumber": function ( toFormat ) { 10675 return toFormat.toString().replace( 10676 /\B(?=(\d{3})+(?!\d))/g, 10677 this.oLanguage.sThousands 10678 ); 10679 }, 10680 10681 10682 /** 10683 * This function is called on every 'draw' event, and allows you to 10684 * dynamically modify the header row. This can be used to calculate and 10685 * display useful information about the table. 10686 * @type function 10687 * @param {node} head "TR" element for the header 10688 * @param {array} data Full table data (as derived from the original HTML) 10689 * @param {int} start Index for the current display starting point in the 10690 * display array 10691 * @param {int} end Index for the current display ending point in the 10692 * display array 10693 * @param {array int} display Index array to translate the visual position 10694 * to the full data array 10695 * 10696 * @dtopt Callbacks 10697 * @name DataTable.defaults.headerCallback 10698 * 10699 * @example 10700 * $(document).ready( function() { 10701 * $('#example').dataTable( { 10702 * "fheaderCallback": function( head, data, start, end, display ) { 10703 * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records"; 10704 * } 10705 * } ); 10706 * } ) 10707 */ 10708 "fnHeaderCallback": null, 10709 10710 10711 /** 10712 * The information element can be used to convey information about the current 10713 * state of the table. Although the internationalisation options presented by 10714 * DataTables are quite capable of dealing with most customisations, there may 10715 * be times where you wish to customise the string further. This callback 10716 * allows you to do exactly that. 10717 * @type function 10718 * @param {object} oSettings DataTables settings object 10719 * @param {int} start Starting position in data for the draw 10720 * @param {int} end End position in data for the draw 10721 * @param {int} max Total number of rows in the table (regardless of 10722 * filtering) 10723 * @param {int} total Total number of rows in the data set, after filtering 10724 * @param {string} pre The string that DataTables has formatted using it's 10725 * own rules 10726 * @returns {string} The string to be displayed in the information element. 10727 * 10728 * @dtopt Callbacks 10729 * @name DataTable.defaults.infoCallback 10730 * 10731 * @example 10732 * $('#example').dataTable( { 10733 * "infoCallback": function( settings, start, end, max, total, pre ) { 10734 * return start +" to "+ end; 10735 * } 10736 * } ); 10737 */ 10738 "fnInfoCallback": null, 10739 10740 10741 /** 10742 * Called when the table has been initialised. Normally DataTables will 10743 * initialise sequentially and there will be no need for this function, 10744 * however, this does not hold true when using external language information 10745 * since that is obtained using an async XHR call. 10746 * @type function 10747 * @param {object} settings DataTables settings object 10748 * @param {object} json The JSON object request from the server - only 10749 * present if client-side Ajax sourced data is used 10750 * 10751 * @dtopt Callbacks 10752 * @name DataTable.defaults.initComplete 10753 * 10754 * @example 10755 * $(document).ready( function() { 10756 * $('#example').dataTable( { 10757 * "initComplete": function(settings, json) { 10758 * alert( 'DataTables has finished its initialisation.' ); 10759 * } 10760 * } ); 10761 * } ) 10762 */ 10763 "fnInitComplete": null, 10764 10765 10766 /** 10767 * Called at the very start of each table draw and can be used to cancel the 10768 * draw by returning false, any other return (including undefined) results in 10769 * the full draw occurring). 10770 * @type function 10771 * @param {object} settings DataTables settings object 10772 * @returns {boolean} False will cancel the draw, anything else (including no 10773 * return) will allow it to complete. 10774 * 10775 * @dtopt Callbacks 10776 * @name DataTable.defaults.preDrawCallback 10777 * 10778 * @example 10779 * $(document).ready( function() { 10780 * $('#example').dataTable( { 10781 * "preDrawCallback": function( settings ) { 10782 * if ( $('#test').val() == 1 ) { 10783 * return false;
10784 * } 10785 * } 10786 * } ); 10787 * } ); 10788 */ 10789 "fnPreDrawCallback": null, 10790 10791 10792 /** 10793 * This function allows you to 'post process' each row after it have been 10794 * generated for each table draw, but before it is rendered on screen. This 10795 * function might be used for setting the row class name etc. 10796 * @type function 10797 * @param {node} row "TR" element for the current row 10798 * @param {array} data Raw data array for this row 10799 * @param {int} displayIndex The display index for the current table draw 10800 * @param {int} displayIndexFull The index of the data in the full list of 10801 * rows (after filtering) 10802 * 10803 * @dtopt Callbacks 10804 * @name DataTable.defaults.rowCallback 10805 * 10806 * @example 10807 * $(document).ready( function() { 10808 * $('#example').dataTable( { 10809 * "rowCallback": function( row, data, displayIndex, displayIndexFull ) { 10810 * // Bold the grade for all 'A' grade browsers 10811 * if ( data[4] == "A" ) { 10812 * $('td:eq(4)', row).html( '<b>A</b>' ); 10813 * } 10814 * } 10815 * } ); 10816 * } ); 10817 */ 10818 "fnRowCallback": null, 10819 10820 10821 /** 10822 * __Deprecated__ The functionality provided by this parameter has now been 10823 * superseded by that provided through `ajax`, which should be used instead. 10824 * 10825 * This parameter allows you to override the default function which obtains 10826 * the data from the server so something more suitable for your application. 10827 * For example you could use POST data, or pull information from a Gears or 10828 * AIR database. 10829 * @type function 10830 * @member 10831 * @param {string} source HTTP source to obtain the data from (`ajax`) 10832 * @param {array} data A key/value pair object containing the data to send 10833 * to the server 10834 * @param {function} callback to be called on completion of the data get 10835 * process that will draw the data on the page. 10836 * @param {object} settings DataTables settings object 10837 * 10838 * @dtopt Callbacks 10839 * @dtopt Server-side 10840 * @name DataTable.defaults.serverData 10841 * 10842 * @deprecated 1.10. Please use `ajax` for this functionality now. 10843 */ 10844 "fnServerData": null, 10845 10846 10847 /** 10848 * __Deprecated__ The functionality provided by this parameter has now been 10849 * superseded by that provided through `ajax`, which should be used instead. 10850 * 10851 * It is often useful to send extra data to the server when making an Ajax 10852 * request - for example custom filtering information, and this callback 10853 * function makes it trivial to send extra information to the server. The 10854 * passed in parameter is the data set that has been constructed by 10855 * DataTables, and you can add to this or modify it as you require. 10856 * @type function 10857 * @param {array} data Data array (array of objects which are name/value 10858 * pairs) that has been constructed by DataTables and will be sent to the 10859 * server. In the case of Ajax sourced data with server-side processing 10860 * this will be an empty array, for server-side processing there will be a 10861 * significant number of parameters! 10862 * @returns {undefined} Ensure that you modify the data array passed in, 10863 * as this is passed by reference. 10864 * 10865 * @dtopt Callbacks 10866 * @dtopt Server-side 10867 * @name DataTable.defaults.serverParams 10868 * 10869 * @deprecated 1.10. Please use `ajax` for this functionality now. 10870 */ 10871 "fnServerParams": null, 10872 10873 10874 /** 10875 * Load the table state. With this function you can define from where, and how, the 10876 * state of a table is loaded. By default DataTables will load from `localStorage` 10877 * but you might wish to use a server-side database or cookies. 10878 * @type function 10879 * @member 10880 * @param {object} settings DataTables settings object 10881 * @return {object} The DataTables state object to be loaded 10882 * 10883 * @dtopt Callbacks 10884 * @name DataTable.defaults.stateLoadCallback 10885 * 10886 * @example 10887 * $(document).ready( function() { 10888 * $('#example').dataTable( { 10889 * "stateSave": true, 10890 * "stateLoadCallback": function (settings) { 10891 * var o; 10892 * 10893 * // Send an Ajax request to the server to get the data. Note that 10894 * // this is a synchronous request. 10895 * $.ajax( { 10896 * "url": "/state_load", 10897 * "async": false, 10898 * "dataType": "json", 10899 * "success": function (json) { 10900 * o = json; 10901 * } 10902 * } ); 10903 * 10904 * return o; 10905 * } 10906 * } ); 10907 * } ); 10908 */ 10909 "fnStateLoadCallback": function ( settings ) { 10910 try { 10911 return JSON.parse(
10912 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem( 10913 'DataTables_'+settings.sInstance+'_'+location.pathname 10914 ) 10915 ); 10916 } catch (e) {} 10917 }, 10918 10919 10920 /** 10921 * Callback which allows modification of the saved state prior to loading that state. 10922 * This callback is called when the table is loading state from the stored data, but 10923 * prior to the settings object being modified by the saved state. Note that for 10924 * plug-in authors, you should use the `stateLoadParams` event to load parameters for 10925 * a plug-in. 10926 * @type function 10927 * @param {object} settings DataTables settings object 10928 * @param {object} data The state object that is to be loaded 10929 * 10930 * @dtopt Callbacks 10931 * @name DataTable.defaults.stateLoadParams 10932 * 10933 * @example 10934 * // Remove a saved filter, so filtering is never loaded 10935 * $(document).ready( function() { 10936 * $('#example').dataTable( { 10937 * "stateSave": true, 10938 * "stateLoadParams": function (settings, data) { 10939 * data.oSearch.sSearch = ""; 10940 * } 10941 * } ); 10942 * } ); 10943 * 10944 * @example 10945 * // Disallow state loading by returning false 10946 * $(document).ready( function() { 10947 * $('#example').dataTable( { 10948 * "stateSave": true, 10949 * "stateLoadParams": function (settings, data) { 10950 * return false; 10951 * } 10952 * } ); 10953 * } ); 10954 */ 10955 "fnStateLoadParams": null, 10956 10957 10958 /** 10959 * Callback that is called when the state has been loaded from the state saving method 10960 * and the DataTables settings object has been modified as a result of the loaded state. 10961 * @type function 10962 * @param {object} settings DataTables settings object 10963 * @param {object} data The state object that was loaded 10964 * 10965 * @dtopt Callbacks 10966 * @name DataTable.defaults.stateLoaded 10967 * 10968 * @example 10969 * // Show an alert with the filtering value that was saved 10970 * $(document).ready( function() { 10971 * $('#example').dataTable( { 10972 * "stateSave": true, 10973 * "stateLoaded": function (settings, data) { 10974 * alert( 'Saved filter was: '+data.oSearch.sSearch ); 10975 * } 10976 * } ); 10977 * } ); 10978 */ 10979 "fnStateLoaded": null, 10980 10981 10982 /** 10983 * Save the table state. This function allows you to define where and how the state 10984 * information for the table is stored By default DataTables will use `localStorage` 10985 * but you might wish to use a server-side database or cookies. 10986 * @type function 10987 * @member 10988 * @param {object} settings DataTables settings object 10989 * @param {object} data The state object to be saved 10990 * 10991 * @dtopt Callbacks 10992 * @name DataTable.defaults.stateSaveCallback 10993 * 10994 * @example 10995 * $(document).ready( function() { 10996 * $('#example').dataTable( { 10997 * "stateSave": true, 10998 * "stateSaveCallback": function (settings, data) { 10999 * // Send an Ajax request to the server with the state object 11000 * $.ajax( { 11001 * "url": "/state_save", 11002 * "data": data, 11003 * "dataType": "json", 11004 * "method": "POST" 11005 * "success": function () {} 11006 * } ); 11007 * } 11008 * } ); 11009 * } ); 11010 */ 11011 "fnStateSaveCallback": function ( settings, data ) { 11012 try { 11013 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem( 11014 'DataTables_'+settings.sInstance+'_'+location.pathname, 11015 JSON.stringify( data ) 11016 ); 11017 } catch (e) {} 11018 }, 11019 11020 11021 /** 11022 * Callback which allows modification of the state to be saved. Called when the table 11023 * has changed state a new state save is required. This method allows modification of 11024 * the state saving object prior to actually doing the save, including addition or 11025 * other state properties or modification. Note that for plug-in authors, you should 11026 * use the `stateSaveParams` event to save parameters for a plug-in. 11027 * @type function 11028 * @param {object} settings DataTables settings object 11029 * @param {object} data The state object to be saved 11030 * 11031 * @dtopt Callbacks 11032 * @name DataTable.defaults.stateSaveParams 11033 * 11034 * @example 11035 * // Remove a saved filter, so filtering is never saved 11036 * $(document).ready( function() { 11037 * $('#example').dataTable( { 11038 * "stateSave": true, 11039 * "stateSaveParams": function (settings, data) { 11040 * data.oSearch.sSearch = ""; 11041 * } 11042 * } ); 11043 * } ); 11044 */ 11045 "fnStateSaveParams": null, 11046 11047 11048 /** 11049 * Duration for which the saved state information is considered valid. After this period 11050 * has elapsed the state will be returned to the default. 11051 * Value is given in seconds. 11052 * @type int 11053 * @default 7200 <i>(2 hours)</i> 11054 * 11055 * @dtopt Options 11056 * @name DataTable.defaults.stateDuration 11057 * 11058 * @example 11059 * $(document).ready( function() { 11060 * $('#example').dataTable( { 11061 * "stateDuration": 60*60*24; // 1 day 11062 * } ); 11063 * } ) 11064 */ 11065 "iStateDuration": 7200, 11066 11067 11068 /** 11069 * When enabled DataTables will not make a request to the server for the first 11070 * page draw - rather it will use the data already on the page (no sorting etc 11071 * will be applied to it), thus saving on an XHR at load time. `deferLoading` 11072 * is used to indicate that deferred loading is required, but it is also used
11073 * to tell DataTables how many records there are in the full table (allowing 11074 * the information element and pagination to be displayed correctly). In the case 11075 * where a filtering is applied to the table on initial load, this can be 11076 * indicated by giving the parameter as an array, where the first element is 11077 * the number of records available after filtering and the second element is the 11078 * number of records without filtering (allowing the table information element 11079 * to be shown correctly). 11080 * @type int | array 11081 * @default null 11082 * 11083 * @dtopt Options 11084 * @name DataTable.defaults.deferLoading 11085 * 11086 * @example 11087 * // 57 records available in the table, no filtering applied 11088 * $(document).ready( function() { 11089 * $('#example').dataTable( { 11090 * "serverSide": true, 11091 * "ajax": "scripts/server_processing.php", 11092 * "deferLoading": 57 11093 * } ); 11094 * } ); 11095 * 11096 * @example 11097 * // 57 records after filtering, 100 without filtering (an initial filter applied) 11098 * $(document).ready( function() { 11099 * $('#example').dataTable( { 11100 * "serverSide": true, 11101 * "ajax": "scripts/server_processing.php", 11102 * "deferLoading": [ 57, 100 ], 11103 * "search": { 11104 * "search": "my_filter" 11105 * } 11106 * } ); 11107 * } ); 11108 */ 11109 "iDeferLoading": null, 11110 11111 11112 /** 11113 * Number of rows to display on a single page when using pagination. If 11114 * feature enabled (`lengthChange`) then the end user will be able to override 11115 * this to a custom setting using a pop-up menu. 11116 * @type int 11117 * @default 10 11118 * 11119 * @dtopt Options 11120 * @name DataTable.defaults.pageLength 11121 * 11122 * @example 11123 * $(document).ready( function() { 11124 * $('#example').dataTable( { 11125 * "pageLength": 50 11126 * } ); 11127 * } ) 11128 */ 11129 "iDisplayLength": 10, 11130 11131 11132 /** 11133 * Define the starting point for data display when using DataTables with 11134 * pagination. Note that this parameter is the number of records, rather than 11135 * the page number, so if you have 10 records per page and want to start on 11136 * the third page, it should be "20". 11137 * @type int 11138 * @default 0 11139 * 11140 * @dtopt Options 11141 * @name DataTable.defaults.displayStart 11142 * 11143 * @example 11144 * $(document).ready( function() { 11145 * $('#example').dataTable( { 11146 * "displayStart": 20 11147 * } ); 11148 * } ) 11149 */ 11150 "iDisplayStart": 0, 11151 11152 11153 /** 11154 * By default DataTables allows keyboard navigation of the table (sorting, paging, 11155 * and filtering) by adding a `tabindex` attribute to the required elements. This 11156 * allows you to tab through the controls and press the enter key to activate them. 11157 * The tabindex is default 0, meaning that the tab follows the flow of the document. 11158 * You can overrule this using this parameter if you wish. Use a value of -1 to 11159 * disable built-in keyboard navigation. 11160 * @type int 11161 * @default 0 11162 * 11163 * @dtopt Options 11164 * @name DataTable.defaults.tabIndex 11165 * 11166 * @example 11167 * $(document).ready( function() { 11168 * $('#example').dataTable( { 11169 * "tabIndex": 1 11170 * } ); 11171 * } ); 11172 */ 11173 "iTabIndex": 0, 11174 11175 11176 /** 11177 * Classes that DataTables assigns to the various components and features 11178 * that it adds to the HTML table. This allows classes to be configured 11179 * during initialisation in addition to through the static 11180 * {@link DataTable.ext.oStdClasses} object). 11181 * @namespace 11182 * @name DataTable.defaults.classes 11183 */ 11184 "oClasses": {}, 11185 11186 11187 /** 11188 * All strings that DataTables uses in the user interface that it creates 11189 * are defined in this object, allowing you to modified them individually or 11190 * completely replace them all as required. 11191 * @namespace 11192 * @name DataTable.defaults.language 11193 */ 11194 "oLanguage": { 11195 /** 11196 * Strings that are used for WAI-ARIA labels and controls only (these are not 11197 * actually visible on the page, but will be read by screenreaders, and thus 11198 * must be internationalised as well). 11199 * @namespace 11200 * @name DataTable.defaults.language.aria 11201 */ 11202 "oAria": { 11203 /** 11204 * ARIA label that is added to the table headers when the column may be 11205 * sorted ascending by activing the column (click or return when focu
11205sed). 11206 * Note that the column header is prefixed to this string. 11207 * @type string 11208 * @default : activate to sort column ascending 11209 * 11210 * @dtopt Language 11211 * @name DataTable.defaults.language.aria.sortAscending 11212 * 11213 * @example 11214 * $(document).ready( function() { 11215 * $('#example').dataTable( { 11216 * "language": { 11217 * "aria": { 11218 * "sortAscending": " - click/return to sort ascending" 11219 * } 11220 * } 11221 * } ); 11222 * } ); 11223 */ 11224 "sSortAscending": ": activate to sort column ascending", 11225 11226 /** 11227 * ARIA label that is added to the table headers when the column may be 11228 * sorted descending by activing the column (click or return when focused). 11229 * Note that the column header is prefixed to this string. 11230 * @type string 11231 * @default : activate to sort column ascending 11232 * 11233 * @dtopt Language 11234 * @name DataTable.defaults.language.aria.sortDescending 11235 * 11236 * @example 11237 * $(document).ready( function() { 11238 * $('#example').dataTable( { 11239 * "language": { 11240 * "aria": { 11241 * "sortDescending": " - click/return to sort descending" 11242 * } 11243 * } 11244 * } ); 11245 * } ); 11246 */ 11247 "sSortDescending": ": activate to sort column descending" 11248 }, 11249 11250 /** 11251 * Pagination string used by DataTables for the built-in pagination 11252 * control types. 11253 * @namespace 11254 * @name DataTable.defaults.language.paginate 11255 */ 11256 "oPaginate": { 11257 /** 11258 * Text to use when using the 'full_numbers' type of pagination for the 11259 * button to take the user to the first page. 11260 * @type string 11261 * @default First 11262 * 11263 * @dtopt Language 11264 * @name DataTable.defaults.language.paginate.first 11265 * 11266 * @example 11267 * $(document).ready( function() { 11268 * $('#example').dataTable( { 11269 * "language": { 11270 * "paginate": { 11271 * "first": "First page" 11272 * } 11273 * } 11274 * } ); 11275 * } ); 11276 */ 11277 "sFirst": "First", 11278 11279 11280 /** 11281 * Text to use when using the 'full_numbers' type of pagination for the 11282 * button to take the user to the last page. 11283 * @type string 11284 * @default Last 11285 * 11286 * @dtopt Language 11287 * @name DataTable.defaults.language.paginate.last 11288 * 11289 * @example 11290 * $(document).ready( function() { 11291 * $('#example').dataTable( { 11292 * "language": { 11293 * "paginate": { 11294 * "last": "Last page" 11295 * } 11296 * } 11297 * } ); 11298 * } ); 11299 */ 11300 "sLast": "Last", 11301 11302 11303 /** 11304 * Text to use for the 'next' pagination button (to take the user to the 11305 * next page). 11306 * @type string 11307 * @default Next 11308 * 11309 * @dtopt Language 11310 * @name DataTable.defaults.language.paginate.next 11311 * 11312 * @example 11313 * $(document).ready( function() { 11314 * $('#example').dataTable( { 11315 * "language": { 11316 * "paginate": { 11317 * "next": "Next page" 11318 * } 11319 * } 11320 * } ); 11321 * } ); 11322 */ 11323 "sNext": "Next", 11324 11325 11326 /** 11327 * Text to use for the 'previous' pagination button (to take the user to 11328 * the previous page). 11329 * @type string 11330 * @default Previous 11331 * 11332 * @dtopt Language 11333 * @name DataTable.defaults.language.paginate.previous 11334 * 11335 * @example 11336 * $(document).ready( function() { 11337 * $('#example').dataTable( { 11338 * "language": { 11339 * "paginate": { 11340 * "previous": "Previous page" 11341 * } 11342 * } 11343 * } ); 11344 * } ); 11345 */ 11346 "sPrevious": "Previous" 11347 }, 11348 11349 /** 11350 * This string is shown in preference to `zeroRecords` when the table is 11351 * empty of data (regardless of filtering). Note that this is an optional 11352 * parameter - if it is not given, the value of `zeroRecords` will be used 11353 * instead (either the default or given value). 11354 * @type string 11355 * @default No data available in table 11356 * 11357 * @dtopt Language 11358 * @name DataTable.defaults.language.emptyTable 11359 * 11360 * @example 11361 * $(document).ready( function() { 11362 * $('#example').dataTable( { 11363 * "language": { 11364 * "emptyTable": "No data available in table" 11365 * } 11366 * } ); 11367 * } ); 11368 */ 11369 "sEmptyTable": "No data available in table", 11370 11371 11372 /** 11373 * This string gives information to the end user about the information 11374 * that is current on display on the page. The following tokens can be 11375 * used in the string and will be dynamically replaced as the table 11376 * display updates. This tokens can be placed anywhere in the string, or 11377 * removed as needed by the language requires: 11378 * 11379 * * `\_START\_` - Display index of the first record on the current page 11380 * * `\_END\_` - Display index of the last record on the current page 11381 * * `\_TOTAL\_` - Number of records in the table after filtering 11382 * * `\_MAX\_` - Number of records in the table without filtering 11383 * * `\_PAGE\_` - Current page number 11384 * * `\_PAGES\_` - Total number of pages of data in the table 11385 * 11386 * @type string 11387 * @default Showing _START_ to _END_ of _TOTAL_ entries 11388 * 11389 * @dtopt Language 11390 * @name DataTable.defaults.language.info 11391 * 11392 * @example 11393 * $(document).ready( function() { 11394 * $('#example').dataTable( { 11395 * "language": { 11396 * "info": "Showing page _PAGE_ of _PAGES_" 11397 * } 11398 * } ); 11399 * } ); 11400 */ 11401 "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries", 11402 11403 11404 /**
11405 * Display information string for when the table is empty. Typically the 11406 * format of this string should match `info`. 11407 * @type string 11408 * @default Showing 0 to 0 of 0 entries 11409 * 11410 * @dtopt Language 11411 * @name DataTable.defaults.language.infoEmpty 11412 * 11413 * @example 11414 * $(document).ready( function() { 11415 * $('#example').dataTable( { 11416 * "language": { 11417 * "infoEmpty": "No entries to show" 11418 * } 11419 * } ); 11420 * } ); 11421 */ 11422 "sInfoEmpty": "Showing 0 to 0 of 0 entries", 11423 11424 11425 /** 11426 * When a user filters the information in a table, this string is appended 11427 * to the information (`info`) to give an idea of how strong the filtering 11428 * is. The variable _MAX_ is dynamically updated. 11429 * @type string 11430 * @default (filtered from _MAX_ total entries) 11431 * 11432 * @dtopt Language 11433 * @name DataTable.defaults.language.infoFiltered 11434 * 11435 * @example 11436 * $(document).ready( function() { 11437 * $('#example').dataTable( { 11438 * "language": { 11439 * "infoFiltered": " - filtering from _MAX_ records" 11440 * } 11441 * } ); 11442 * } ); 11443 */ 11444 "sInfoFiltered": "(filtered from _MAX_ total entries)", 11445 11446 11447 /** 11448 * If can be useful to append extra information to the info string at times, 11449 * and this variable does exactly that. This information will be appended to 11450 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are 11451 * being used) at all times. 11452 * @type string 11453 * @default <i>Empty string</i> 11454 * 11455 * @dtopt Language 11456 * @name DataTable.defaults.language.infoPostFix 11457 * 11458 * @example 11459 * $(document).ready( function() { 11460 * $('#example').dataTable( { 11461 * "language": { 11462 * "infoPostFix": "All records shown are derived from real information." 11463 * } 11464 * } ); 11465 * } ); 11466 */ 11467 "sInfoPostFix": "", 11468 11469 11470 /** 11471 * This decimal place operator is a little different from the other 11472 * language options since DataTables doesn't output floating point 11473 * numbers, so it won't ever use this for display of a number. Rather, 11474 * what this parameter does is modify the sort methods of the table so 11475 * that numbers which are in a format which has a character other than 11476 * a period (`.`) as a decimal place will be sorted numerically. 11477 * 11478 * Note that numbers with different decimal places cannot be shown in 11479 * the same table and still be sortable, the table must be consistent. 11480 * However, multiple different tables on the page can use different 11481 * decimal place characters. 11482 * @type string 11483 * @default 11484 * 11485 * @dtopt Language 11486 * @name DataTable.defaults.language.decimal 11487 * 11488 * @example 11489 * $(document).ready( function() { 11490 * $('#example').dataTable( { 11491 * "language": { 11492 * "decimal": "," 11493 * "thousands": "." 11494 * } 11495 * } ); 11496 * } ); 11497 */ 11498 "sDecimal": "", 11499 11500 11501 /** 11502 * DataTables has a build in number formatter (`formatNumber`) which is 11503 * used to format large numbers that are used in the table information. 11504 * By default a comma is used, but this can be trivially changed to any 11505 * character you wish with this parameter. 11506 * @type string 11507 * @default , 11508 * 11509 * @dtopt Language 11510 * @name DataTable.defaults.language.thousands 11511 * 11512 * @example 11513 * $(document).ready( function() { 11514 * $('#example').dataTable( { 11515 * "language": { 11516 * "thousands": "'" 11517 * } 11518 * } ); 11519 * } ); 11520 */ 11521 "sThousands": ",", 11522 11523 11524 /** 11525 * Detail the action that will be taken when the drop down menu for the 11526 * pagination length option is changed. The '_MENU_' variable is replaced 11527 * with a default select list of 10, 25, 50 and 100, and can be replaced 11528 * with a custom select box if required. 11529 * @type string 11530 * @default Show _MENU_ entries 11531 * 11532 * @dtopt Language 11533 * @name DataTable.defaults.language.lengthMenu 11534 * 11535 * @example 11536 * // Language change only 11537 * $(document).ready( function() { 11538 * $('#example').dataTable( { 11539 * "language": {
11540 * "lengthMenu": "Display _MENU_ records" 11541 * } 11542 * } ); 11543 * } ); 11544 * 11545 * @example 11546 * // Language and options change 11547 * $(document).ready( function() { 11548 * $('#example').dataTable( { 11549 * "language": { 11550 * "lengthMenu": 'Display <select>'+ 11551 * '<option value="10">10</option>'+ 11552 * '<option value="20">20</option>'+ 11553 * '<option value="30">30</option>'+ 11554 * '<option value="40">40</option>'+ 11555 * '<option value="50">50</option>'+ 11556 * '<option value="-1">All</option>'+ 11557 * '</select> records' 11558 * } 11559 * } ); 11560 * } ); 11561 */ 11562 "sLengthMenu": "Show _MENU_ entries", 11563 11564 11565 /** 11566 * When using Ajax sourced data and during the first draw when DataTables is 11567 * gathering the data, this message is shown in an empty row in the table to 11568 * indicate to the end user the the data is being loaded. Note that this 11569 * parameter is not used when loading data by server-side processing, just 11570 * Ajax sourced data with client-side processing. 11571 * @type string 11572 * @default Loading... 11573 * 11574 * @dtopt Language 11575 * @name DataTable.defaults.language.loadingRecords 11576 * 11577 * @example 11578 * $(document).ready( function() { 11579 * $('#example').dataTable( { 11580 * "language": { 11581 * "loadingRecords": "Please wait - loading..." 11582 * } 11583 * } ); 11584 * } ); 11585 */ 11586 "sLoadingRecords": "Loading...", 11587 11588 11589 /** 11590 * Text which is displayed when the table is processing a user action 11591 * (usually a sort command or similar). 11592 * @type string 11593 * @default Processing... 11594 * 11595 * @dtopt Language 11596 * @name DataTable.defaults.language.processing 11597 * 11598 * @example 11599 * $(document).ready( function() { 11600 * $('#example').dataTable( { 11601 * "language": { 11602 * "processing": "DataTables is currently busy" 11603 * } 11604 * } ); 11605 * } ); 11606 */ 11607 "sProcessing": "Processing...", 11608 11609 11610 /** 11611 * Details the actions that will be taken when the user types into the 11612 * filtering input text box. The variable "_INPUT_", if used in the string, 11613 * is replaced with the HTML text box for the filtering input allowing 11614 * control over where it appears in the string. If "_INPUT_" is not given 11615 * then the input box is appended to the string automatically. 11616 * @type string 11617 * @default Search: 11618 * 11619 * @dtopt Language 11620 * @name DataTable.defaults.language.search 11621 * 11622 * @example 11623 * // Input text box will be appended at the end automatically 11624 * $(document).ready( function() { 11625 * $('#example').dataTable( { 11626 * "language": { 11627 * "search": "Filter records:" 11628 * } 11629 * } ); 11630 * } ); 11631 * 11632 * @example 11633 * // Specify where the filter should appear 11634 * $(document).ready( function() { 11635 * $('#example').dataTable( { 11636 * "language": { 11637 * "search": "Apply filter _INPUT_ to table" 11638 * } 11639 * } ); 11640 * } ); 11641 */ 11642 "sSearch": "Search:", 11643 11644 11645 /** 11646 * Assign a `placeholder` attribute to the search `input` element 11647 * @type string 11648 * @default 11649 * 11650 * @dtopt Language 11651 * @name DataTable.defaults.language.searchPlaceholder 11652 */ 11653 "sSearchPlaceholder": "", 11654 11655 11656 /** 11657 * All of the language information can be stored in a file on the 11658 * server-side, which DataTables will look up if this parameter is passed. 11659 * It must store the URL of the language file, which is in a JSON format, 11660 * and the object has the same properties as the oLanguage object in the 11661 * initialiser object (i.e. the above parameters). Please refer to one of 11662 * the example language files to see how this works in action. 11663 * @type string 11664 * @default <i>Empty string - i.e. disabled</i> 11665 * 11666 * @dtopt Language 11667 * @name DataTable.defaults.language.url 11668 * 11669 * @example 11670 * $(document).ready( function() { 11671 * $('#example').dataTable( { 11672 * "language": { 11673 * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt" 11674 * } 11675 * } ); 11676 * } ); 11677 */ 11678 "sUrl": "", 11679 11680 11681 /** 11682 * Text shown inside the table records when the is no information to be 11683 * displayed after filtering. `emptyTable` is shown when there is simply no 11684 * information in the table at all (regardless of filtering). 11685 * @type string
11686 * @default No matching records found 11687 * 11688 * @dtopt Language 11689 * @name DataTable.defaults.language.zeroRecords 11690 * 11691 * @example 11692 * $(document).ready( function() { 11693 * $('#example').dataTable( { 11694 * "language": { 11695 * "zeroRecords": "No records to display" 11696 * } 11697 * } ); 11698 * } ); 11699 */ 11700 "sZeroRecords": "No matching records found" 11701 }, 11702 11703 11704 /** 11705 * This parameter allows you to have define the global filtering state at 11706 * initialisation time. As an object the `search` parameter must be 11707 * defined, but all other parameters are optional. When `regex` is true, 11708 * the search string will be treated as a regular expression, when false 11709 * (default) it will be treated as a straight string. When `smart` 11710 * DataTables will use it's smart filtering methods (to word match at 11711 * any point in the data), when false this will not be done. 11712 * @namespace 11713 * @extends DataTable.models.oSearch 11714 * 11715 * @dtopt Options 11716 * @name DataTable.defaults.search 11717 * 11718 * @example 11719 * $(document).ready( function() { 11720 * $('#example').dataTable( { 11721 * "search": {"search": "Initial search"} 11722 * } ); 11723 * } ) 11724 */ 11725 "oSearch": $.extend( {}, DataTable.models.oSearch ), 11726 11727 11728 /** 11729 * __Deprecated__ The functionality provided by this parameter has now been 11730 * superseded by that provided through `ajax`, which should be used instead. 11731 * 11732 * By default DataTables will look for the property `data` (or `aaData` for 11733 * compatibility with DataTables 1.9-) when obtaining data from an Ajax 11734 * source or for server-side processing - this parameter allows that 11735 * property to be changed. You can use Javascript dotted object notation to 11736 * get a data source for multiple levels of nesting. 11737 * @type string 11738 * @default data 11739 * 11740 * @dtopt Options 11741 * @dtopt Server-side 11742 * @name DataTable.defaults.ajaxDataProp 11743 * 11744 * @deprecated 1.10. Please use `ajax` for this functionality now. 11745 */ 11746 "sAjaxDataProp": "data", 11747 11748 11749 /** 11750 * __Deprecated__ The functionality provided by this parameter has now been 11751 * superseded by that provided through `ajax`, which should be used instead. 11752 * 11753 * You can instruct DataTables to load data from an external 11754 * source using this parameter (use aData if you want to pass data in you 11755 * already have). Simply provide a url a JSON object can be obtained from. 11756 * @type string 11757 * @default null 11758 * 11759 * @dtopt Options 11760 * @dtopt Server-side 11761 * @name DataTable.defaults.ajaxSource 11762 * 11763 * @deprecated 1.10. Please use `ajax` for this functionality now. 11764 */ 11765 "sAjaxSource": null, 11766 11767 11768 /** 11769 * This initialisation variable allows you to specify exactly where in the 11770 * DOM you want DataTables to inject the various controls it adds to the page 11771 * (for example you might want the pagination controls at the top of the 11772 * table). DIV elements (with or without a custom class) can also be added to 11773 * aid styling. The follow syntax is used: 11774 * <ul> 11775 * <li>The following options are allowed: 11776 * <ul> 11777 * <li>'l' - Length changing</li> 11778 * <li>'f' - Filtering input</li> 11779 * <li>'t' - The table!</li> 11780 * <li>'i' - Information</li> 11781 * <li>'p' - Pagination</li> 11782 * <li>'r' - pRocessing</li> 11783 * </ul> 11784 * </li> 11785 * <li>The following constants are allowed: 11786 * <ul> 11787 * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li> 11788 * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li> 11789 * </ul> 11790 * </li> 11791 * <li>The following syntax is expected: 11792 * <ul> 11793 * <li>'<' and '>' - div elements</li> 11794 * <li>'<"class" and '>' - div with a class</li> 11795 * <li>'<"#id" and '>' - div with an ID</li> 11796 * </ul> 11797 * </li> 11798 * <li>Examples: 11799 * <ul> 11800 * <li>'<"wrapper"flipt>'</li> 11801 * <li>'<lf<t>ip>'</li> 11802 * </ul> 11803 * </li> 11804 * </ul> 11805 * @type string 11806 * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b> 11807 * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i> 11808 * 11809 * @dtopt Options 11810 * @name DataTable.defaults.dom 11811 * 11812 * @example 11813 * $(document).ready( function() { 11814 * $('#example').dataTable( { 11815 * "dom": '<"top"i>rt<"bottom"flp><"clear">' 11816 * } ); 11817 * } ); 11818 */ 11819 "sDom": "lfrtip", 11820 11821 11822 /** 11823 * Search delay option. This will throttle full table searches that use the 11824 * DataTables provided search input element (it does not effect calls to 11825 * `dt-api search()`, providing a delay before the search is made. 11826 * @type integer 11827 * @default 0 11828 * 11829 * @dtopt Options 11830 * @name DataTable.defaults.searchDelay 11831 * 11832 * @example 11833 * $(document).ready( function() { 11834 * $('#example').dataTable( { 11835 * "searchDelay": 200 11836 * } ); 11837 * } ) 11838 */ 11839 "searchDelay": null, 11840 11841 11842 /** 11843 * DataTables features four different built-in options for the buttons to 11844 * display for pagination control: 11845 * 11846 * * `simple` - 'Previous' and 'Next' buttons only 11847 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers 11848 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons 11849 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus 11850 * page numbers 11851 * 11852 * Further methods can be added using {@link DataTable.ext.oPagination}. 11853 * @type string 11854 * @default simple_numbers 11855 * 11856 * @dtopt Options 11857 * @name DataTable.defaults.pagingType 11858 * 11859 * @example 11860 * $(document).ready( function() { 11861 * $('#example').dataTable( { 11862 * "pagingType": "full_numbers" 11863 * } ); 11864 * } ) 11865 */ 11866 "sPaginationType": "simple_numbers", 11867 11868 11869 /** 11870 * Enable horizontal scrolling. When a table is too wide to fit into a 11871 * certain layout, or you have a large number of columns in the table, you 11872 * can enable x-scrolling to show the table in a viewport, which can be 11873 * scrolled. This property can be `true` which will allow the table to 11874 * scroll horizontally when needed, or any CSS unit, or a number (in which 11875 * case it will be treated as a pixel measurement). Setting as simply `true` 11876 * is recommended.
11877 * @type boolean|string 11878 * @default <i>blank string - i.e. disabled</i> 11879 * 11880 * @dtopt Features 11881 * @name DataTable.defaults.scrollX 11882 * 11883 * @example 11884 * $(document).ready( function() { 11885 * $('#example').dataTable( { 11886 * "scrollX": true, 11887 * "scrollCollapse": true 11888 * } ); 11889 * } ); 11890 */ 11891 "sScrollX": "", 11892 11893 11894 /** 11895 * This property can be used to force a DataTable to use more width than it 11896 * might otherwise do when x-scrolling is enabled. For example if you have a 11897 * table which requires to be well spaced, this parameter is useful for 11898 * "over-sizing" the table, and thus forcing scrolling. This property can by 11899 * any CSS unit, or a number (in which case it will be treated as a pixel 11900 * measurement). 11901 * @type string 11902 * @default <i>blank string - i.e. disabled</i> 11903 * 11904 * @dtopt Options 11905 * @name DataTable.defaults.scrollXInner 11906 * 11907 * @example 11908 * $(document).ready( function() { 11909 * $('#example').dataTable( { 11910 * "scrollX": "100%", 11911 * "scrollXInner": "110%" 11912 * } ); 11913 * } ); 11914 */ 11915 "sScrollXInner": "", 11916 11917 11918 /** 11919 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable 11920 * to the given height, and enable scrolling for any data which overflows the 11921 * current viewport. This can be used as an alternative to paging to display 11922 * a lot of data in a small area (although paging and scrolling can both be 11923 * enabled at the same time). This property can be any CSS unit, or a number 11924 * (in which case it will be treated as a pixel measurement). 11925 * @type string 11926 * @default <i>blank string - i.e. disabled</i> 11927 * 11928 * @dtopt Features 11929 * @name DataTable.defaults.scrollY 11930 * 11931 * @example 11932 * $(document).ready( function() { 11933 * $('#example').dataTable( { 11934 * "scrollY": "200px", 11935 * "paginate": false 11936 * } ); 11937 * } ); 11938 */ 11939 "sScrollY": "", 11940 11941 11942 /** 11943 * __Deprecated__ The functionality provided by this parameter has now been 11944 * superseded by that provided through `ajax`, which should be used instead. 11945 * 11946 * Set the HTTP method that is used to make the Ajax call for server-side 11947 * processing or Ajax sourced data. 11948 * @type string 11949 * @default GET 11950 * 11951 * @dtopt Options 11952 * @dtopt Server-side 11953 * @name DataTable.defaults.serverMethod 11954 * 11955 * @deprecated 1.10. Please use `ajax` for this functionality now. 11956 */ 11957 "sServerMethod": "GET", 11958 11959 11960 /** 11961 * DataTables makes use of renderers when displaying HTML elements for 11962 * a table. These renderers can be added or modified by plug-ins to 11963 * generate suitable mark-up for a site. For example the Bootstrap 11964 * integration plug-in for DataTables uses a paging button renderer to 11965 * display pagination buttons in the mark-up required by Bootstrap. 11966 * 11967 * For further information about the renderers available see 11968 * DataTable.ext.renderer 11969 * @type string|object 11970 * @default null 11971 * 11972 * @name DataTable.defaults.renderer 11973 * 11974 */ 11975 "renderer": null, 11976 11977 11978 /** 11979 * Set the data property name that DataTables should use to get a row's id 11980 * to set as the `id` property in the node. 11981 * @type string 11982 * @default DT_RowId 11983 * 11984 * @name DataTable.defaults.rowId 11985 */ 11986 "rowId": "DT_RowId" 11987 }; 11988 11989 _fnHungarianMap( DataTable.defaults ); 11990 11991 11992 11993 /* 11994 * Developer note - See note in model.defaults.js about the use of Hungarian 11995 * notation and camel case. 11996 */ 11997 11998 /** 11999 * Column options that can be given to DataTables at initialisation time. 12000 * @namespace 12001 */ 12002 DataTable.defaults.column = { 12003 /** 12004 * Define which column(s) an order will occur on for this column. This 12005 * allows a column's ordering to take multiple columns into account when 12006 * doing a sort or use the data from a different column. For example first 12007 * name / last name columns make sense to do a multi-column sort over the 12008 * two columns. 12009 * @type array|int 12010 * @default null <i>Takes the value of the column index automatically</i> 12011 * 12012 * @name DataTable.defaults.column.orderData 12013 * @dtopt Columns 12014 * 12015 * @example 12016 * // Using `columnDefs` 12017 * $(document).ready( function() { 12018 * $('#example').dataTable( { 12019 * "columnDefs": [ 12020 * { "orderData": [ 0, 1 ], "targets": [ 0 ] }, 12021 * { "orderData": [ 1, 0 ], "targets": [ 1 ] }, 12022 * { "orderData": 2, "targets": [ 2 ] } 12023 * ] 12024 * } ); 12025 * } ); 12026 * 12027 * @example 12028 * // Using `columns` 12029 * $(document).ready( function() { 12030 * $('#example').dataTable( { 12031 * "columns": [ 12032 * { "orderData": [ 0, 1 ] }, 12033 * { "orderData": [ 1, 0 ] }, 12034 * { "orderData": 2 }, 12035 * null, 12036 * null 12037 * ] 12038 * } ); 12039 * } ); 12040 */ 12041 "aDataSort": null, 12042 "iDataSort": -1, 12043 12044 12045 /** 12046 * You can control the default ordering direction, and even alter the 12047 * behaviour of the sort handler (i.e. only allow ascending ordering etc) 12048 * using this parameter. 12049 * @type array 12050 * @default [ 'asc', 'desc' ] 12051 * 12052 * @name DataTable.defaults.column.orderSequence 12053 * @dtopt Columns 12054 * 12055 * @example 12056 * // Using `columnDefs` 12057 * $(document).ready( function() { 12058 * $('#example').dataTable( { 12059 * "columnDefs": [ 12060 * { "orderSequence": [ "asc" ], "targets": [ 1 ] }, 12061 * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] }, 12062 * { "orderSequence": [ "desc" ], "targets": [ 3 ] } 12063 * ] 12064 * } ); 12065 * } ); 12066 * 12067 * @example 12068 * // Using `columns` 12069 * $(document).ready( function() { 12070 * $('#example').dataTable( { 12071 * "columns": [ 12072 * null, 12073 * { "orderSequence": [ "asc" ] }, 12074 * { "orderSequence": [ "desc", "asc", "asc" ] }, 12075 * { "orderSequence": [ "desc" ] }, 12076 * null 12077 * ] 12078 * } ); 12079 * } ); 12080 */ 12081 "asSorting": [ 'asc', 'desc' ], 12082 12083 12084 /** 12085 * Enable or disable filtering on the data in this column. 12086 * @type boolean 12087 * @default true 12088 * 12089 * @name DataTable.defaults.column.searchable 12090 * @dtopt Columns 12091 * 12092 * @example 12093 * // Using `columnDefs` 12094 * $(document).ready( function() { 12095 * $('#example').dataTable( { 12096 * "columnDefs": [ 12097 * { "searchable": false, "targets": [ 0 ] } 12098 * ] } ); 12099 * } ); 12100 * 12101 * @example 12102 * // Using `columns` 12103 * $(document).ready( function() { 12104 * $('#example').dataTable( { 12105 * "columns": [ 12106 * { "searchable": false }, 12107 * null, 12108 * null, 12109 * null, 12110 * null 12111 * ] } ); 12112 * } ); 12113 */ 12114 "bSearchable": true, 12115 12116 12117 /** 12118 * Enable or disable ordering on this column. 12119 * @type boolean 12120 * @default true 12121 * 12122 * @name DataTable.defaults.column.orderable 12123 * @dtopt Columns 12124 * 12125 * @example 12126 * // Using `columnDefs` 12127 * $(document).ready( function() { 12128 * $('#example').dataTable( { 12129 * "columnDefs": [ 12130 * { "orderable": false, "targets": [ 0 ] } 12131 * ] } ); 12132 * } ); 12133 * 12134 * @example 12135 * // Using `columns` 12136 * $(document).ready( function() { 12137 * $('#example').dataTable( { 12138 * "columns": [ 12139 * { "orderable": false }, 12140 * null, 12141 * null, 12142 * null, 12143 * null 12144 * ] } ); 12145 * } ); 12146 */ 12147 "bSortable": true, 12148 12149 12150 /** 12151 * Enable or disable the display of this column. 12152 * @type boolean 12153 * @default true 12154 * 12155 * @name DataTable.defaults.column.visible 12156 * @dtopt Columns 12157 * 12158 * @example 12159 * // Using `columnDefs` 12160 * $(document).ready( function() { 12161 * $('#example').dataTable( { 12162 * "columnDefs": [ 12163 * { "visible": false, "targets": [ 0 ] } 12164 * ] } ); 12165 * } ); 12166 * 12167 * @example 12168 * // Using `columns` 12169 * $(document).ready( function() { 12170 * $('#example').dataTable( { 12171 * "columns": [ 12172 * { "visible": false }, 12173 * null, 12174 * null, 12175 * null, 12176 * null 12177 * ] } ); 12178 * } ); 12179 */ 12180 "bVisible": true, 12181 12182 12183 /** 12184 * Developer definable function that is called whenever a cell is created (Ajax source, 12185 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 12186 * allowing you to modify the DOM element (add background colour for example) when the 12187 * element is available. 12188 * @type function 12189 * @param {element} td The TD node that has been created 12190 * @param {*} cellData The Data for the cell 12191 * @param {array|object} rowData The data for the whole row 12192 * @param {int} row The row index for the aoData data store 12193 * @param {int} col The column index for aoColumns 12194 * 12195 * @name DataTable.defaults.column.createdCell 12196 * @dtopt Columns 12197 * 12198 * @example 12199 * $(document).ready( function() { 12200 * $('#example').dataTable( { 12201 * "columnDefs": [ { 12202 * "targets": [3], 12203 * "createdCell": function (td, cellData, rowData, row, c
12203ol) { 12204 * if ( cellData == "1.7" ) { 12205 * $(td).css('color', 'blue') 12206 * } 12207 * } 12208 * } ] 12209 * }); 12210 * } ); 12211 */ 12212 "fnCreatedCell": null, 12213 12214 12215 /** 12216 * This parameter has been replaced by `data` in DataTables to ensure naming 12217 * consistency. `dataProp` can still be used, as there is backwards 12218 * compatibility in DataTables for this option, but it is strongly 12219 * recommended that you use `data` in preference to `dataProp`. 12220 * @name DataTable.defaults.column.dataProp 12221 */ 12222 12223 12224 /** 12225 * This property can be used to read data from any data source property, 12226 * including deeply nested objects / properties. `data` can be given in a 12227 * number of different ways which effect its behaviour: 12228 * 12229 * * `integer` - treated as an array index for the data source. This is the 12230 * default that DataTables uses (incrementally increased for each column). 12231 * * `string` - read an object property from the data source. There are 12232 * three 'special' options that can be used in the string to alter how 12233 * DataTables reads the data from the source object: 12234 * * `.` - Dotted Javascript notation. Just as you use a `.` in 12235 * Javascript to read from nested objects, so to can the options 12236 * specified in `data`. For example: `browser.version` or 12237 * `browser.name`. If your object parameter name contains a period, use 12238 * `\\` to escape it - i.e. `first\\.name`. 12239 * * `[]` - Array notation. DataTables can automatically combine data 12240 * from and array source, joining the data with the characters provided 12241 * between the two brackets. For example: `name[, ]` would provide a 12242 * comma-space separated list from the source array. If no characters 12243 * are provided between the brackets, the original array source is 12244 * returned. 12245 * * `()` - Function notation. Adding `()` to the end of a parameter will 12246 * execute a function of the name given. For example: `browser()` for a 12247 * simple function on the data source, `browser.version()` for a 12248 * function in a nested property or even `browser().version` to get an 12249 * object property if the function called returns an object. Note that 12250 * function notation is recommended for use in `render` rather than 12251 * `data` as it is much simpler to use as a renderer. 12252 * * `null` - use the original data source for the row rather than plucking 12253 * data directly from it. This action has effects on two other 12254 * initialisation options: 12255 * * `defaultContent` - When null is given as the `data` option and 12256 * `defaultContent` is specified for the column, the value defined by 12257 * `defaultContent` will be used for the cell. 12258 * * `render` - When null is used for the `data` option and the `render` 12259 * option is specified for the column, the whole data source for the 12260 * row is used for the renderer. 12261 * * `function` - the function given will be executed whenever DataTables 12262 * needs to set or get the data for a cell in the column. The function 12263 * takes three parameters: 12264 * * Parameters: 12265 * * `{array|object}` The data source for the row 12266 * * `{string}` The type call data requested - this will be 'set' when 12267 * setting data or 'filter', 'display', 'type', 'sort' or undefined 12268 * when gathering data. Note that when `undefined` is given for the 12269 * type DataTables expects to get the raw data for the object back< 12270 * * `{*}` Data to set when the second parameter is 'set'. 12271 * * Return: 12272 * * The return value from the function is not required when 'set' is 12273 * the type of call, but otherwise the return is what will be used 12274 * for the data requested. 12275 * 12276 * Note that `data` is a getter and setter option. If you just require 12277 * formatting of data for output, you will likely want to use `render` which 12278 * is simply a getter and thus simpler to use. 12279 * 12280 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The 12281 * name change reflects the flexibility of this property and is consistent 12282 * with the naming of mRender. If 'mDataProp' is given, then it will still 12283 * be used by DataTables, as it automatically maps the old name to the new 12284 * if required. 12285 * 12286 * @type string|int|function|null 12287 * @default null <i>Use automatically calculated column index</i> 12288 * 12289 * @name DataTable.defaults.column.data 12290 * @dtopt Columns 12291 * 12292 * @example 12293 * // Read table data from objects 12294 * // JSON structure for each row: 12295 * // { 12296 * // "engine": {value}, 12297 * // "browser": {value}, 12298 * // "platform": {value}, 12299 * // "version": {value}, 12300 * // "grade": {value} 12301 * // } 12302 * $(document).ready( function() { 12303 * $('#example').dataTable( { 12304 * "ajaxSource": "sources/objects.txt", 12305 * "columns": [ 12306 * { "data": "engine" }, 12307 * { "data": "browser" }, 12308 * { "data": "platform" }, 12309 * { "data": "version" }, 12310 * { "data": "grade" } 12311 * ] 12312 * } ); 12313 * } ); 12314 * 12315 * @example 12316 * // Read information from deeply nested objects 12317 * // JSON structure for each row: 12318 * // { 12319 * // "engine": {value}, 12320 * // "browser": {value}, 12321 * // "platform": { 12322 * // "inner": {value} 12323 * // }, 12324 * // "details": [ 12325 * // {value}, {value} 12326 * // ] 12327 * // } 12328 * $(document).ready( function() { 12329 * $('#example').dataTable( { 12330 * "ajaxSource": "sources/deep.txt", 12331 * "columns": [ 12332 * { "data": "engine" }, 12333 * { "data": "browser" }, 12334 * { "data": "platform.inner" }, 12335 * { "data": "platform.details.0" }, 12336 * { "data": "platform.details.1" } 12337 * ] 12338 * } ); 12339 * } ); 12340 * 12341 * @example 12342 * // Using `data` as a function to provide different information for 12343 * // sorting, filtering and display. In this case, currency (price) 12344 * $(document).ready( function() { 12345 * $('#example').dataTable( { 12346 * "columnDefs": [ { 12347 * "targets": [ 0 ], 12348 * "data": function ( source, type, val ) { 12349 * if (type === 'set') { 12350 * source.price = val; 12351 * // Store the computed dislay and filter values for efficiency 12352 * source.price_display = val=="" ? "" : "$"+numberFormat(val); 12353 * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val; 12354 * return; 12355 * } 12356 * else if (type === 'display') { 12357 * return source.price_display; 12358 * } 12359 * else if (type === 'filter') { 12360 * return source.price_filter; 12361 * } 12362 * // 'sort', 'type' and undefined all just use the integer 12363 * return source.price; 12364 * } 12365 * } ] 12366 * } ); 12367 * } ); 12368 * 12369 * @example 12370 * // Using default content 12371 * $(document).ready( function() { 12372 * $('#example').dataTable( { 12373 * "columnDefs": [ { 12374 * "targets": [ 0 ], 12375 * "data": null, 12376 * "defaultContent": "Click to edit" 12377 * } ] 12378 * } ); 12379 * } ); 12380 * 12381 * @example 12382 * // Using array notation - outputting a list from an array 12383 * $(document).ready( function() { 12384 * $('#example').dataTable( { 12385 * "columnDefs": [ { 12386 * "targets": [ 0 ], 12387 * "data": "name[, ]" 12388 * } ] 12389 * } ); 12390 * } ); 12391 * 12392 */ 12393 "mData": null, 12394 12395 12396 /** 12397 * This property is the rendering partner to `data` and it is suggested that 12398 * when you want to manipulate data for display (including filtering, 12399 * sorting etc) without altering the underlying data for the table, use this 12400 * property. `render` can be considered to be the the read only companion to 12401 * `data` which is read / write (then as such more complex). Like `data` 12402 * this option can be given in a number of different ways to effect its 12403 * behaviour: 12404 * 12405 * * `integer` - treated as an array index for the data source. This is the 12406 * default that DataTables uses (incrementally increased for each column). 12407 * * `string` - read an object property from the data source. There are 12408 * three 'special' options that can be used in the string to alter how 12409 * DataTables reads the data from the source object: 12410 * * `.` - Dotted Javascript notation. Just as you use a `.` in 12411 * Javascript to read from nested objects, so to can the options 12412 * specified in `data`. For example: `browser.version` or 12413 * `browser.name`. If your object parameter name contains a period, use 12414 * `\\` to escape it - i.e. `first\\.name`. 12415 * * `[]` - Array notation. DataTables can automatically combine data 12416 * from and array source, joining the data with the characters provided 12417 * between the two brackets. For example: `name[, ]` would provide a 12418 * comma-space separated list from the source array. If no characters 12419 * are provided between the brackets, the original array source is 12420 * returned. 12421 * * `()` - Function notation. Adding `()` to the end of a parameter will 12422 * execute a function of the name given. For example: `browser()` for a 12423 * simple function on the data source, `browser.version()` for a 12424 * function in a nested property or even `browser().version` to get an 12425 * object property if the function called returns an object. 12426 * * `object` - use different data for the different data types requested by 12427 * DataTables ('filter', 'display', 'type' or 'sort'). The property names 12428 * of the object is the data type the property refers to and the value can 12429 * defined using an integer, string or function using the same rules as 12430 * `render` normally does. Note that an `_` option _must_ be specified. 12431 * This is the default value to use if you haven't specified a value for 12432 * the data type requested by DataTables. 12433 * * `function` - the function given will be executed whenever DataTables 12434 * needs to set or get the data for a cell in the column. The function 12435 * takes three parameters: 12436 * * Parameters: 12437 * * {array|object} The data source for the row (based on `data`) 12438 * * {string} The type call data requested - this will be 'filter', 12439 * 'display', 'type' or 'sort'. 12440 * * {array|object} The full data source for the row (not based on 12441 * `data`) 12442 * * Return: 12443 * * The return value from the function is what will be used for the 12444 * data requested. 12445 * 12446 * @type string|int|function|object|null 12447 * @default null Use the data source value. 12448 * 12449 * @name DataTable.defaults.column.render 12450 * @dtopt Columns 12451 * 12452 * @example 12453 * // Create a comma separated list from an array of objects 12454 * $(document).ready( function() { 12455 * $('#example').dataTable( { 12456 * "ajaxSource": "sources/deep.txt", 12457 * "columns": [ 12458 * { "data": "engine" }, 12459 * { "data": "browser" }, 12460 * { 12461 * "data": "platform", 12462 * "render": "[, ].name" 12463 * } 12464 * ] 12465 * } ); 12466 * } ); 12467 * 12468 * @example 12469 * // Execute a function to obtain data 12470 * $(document).ready( function() { 12471 * $('#example').dataTable( { 12472 * "columnDefs": [ { 12473 * "targets": [ 0 ], 12474 * "data": null, // Use the full data source object for the renderer's source 12475 * "render": "browserName()" 12476 * } ] 12477 * } ); 12478 * } ); 12479 * 12480 * @example 12481 * // As an object, extracting different data for the different types 12482 * // This would be used with a data source such as: 12483 * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" } 12484 * // Here the `phone` integer is used for sorting and type detection, while `phone_filter` 12485 * // (which has both forms) is used for filtering for if a user inputs either format, while 12486 * // the formatted phone number is the one that is shown in the table. 12487 * $(document).ready( function() { 12488 * $('#example').dataTable( { 12489 * "columnDefs": [ { 12490 * "targets": [ 0 ], 12491 * "data": null, // Use the full data source object for the renderer's source 12492 * "render": { 12493 * "_": "phone", 12494 * "filter": "phone_filter", 12495 * "display": "phone_display" 12496 * } 12497 * } ] 12498 * } ); 12499 * } ); 12500 * 12501 * @example 12502 * // Use as a function to create a link from the data source 12503 * $(document).ready( function() { 12504 * $('#example').dataTable( { 12505 * "columnDefs": [ { 12506 * "targets": [ 0 ], 12507 * "data": "download_link", 12508 * "render": function ( data, type, full ) { 12509 * return '<a href="'+data+'">Download</a>'; 12510 * } 12511 * } ] 12512 * } ); 12513 * } ); 12514 */
12515 "mRender": null, 12516 12517 12518 /** 12519 * Change the cell type created for the column - either TD cells or TH cells. This 12520 * can be useful as TH cells have semantic meaning in the table body, allowing them 12521 * to act as a header for a row (you may wish to add scope='row' to the TH elements). 12522 * @type string 12523 * @default td 12524 * 12525 * @name DataTable.defaults.column.cellType 12526 * @dtopt Columns 12527 * 12528 * @example 12529 * // Make the first column use TH cells 12530 * $(document).ready( function() { 12531 * $('#example').dataTable( { 12532 * "columnDefs": [ { 12533 * "targets": [ 0 ], 12534 * "cellType": "th" 12535 * } ] 12536 * } ); 12537 * } ); 12538 */ 12539 "sCellType": "td", 12540 12541 12542 /** 12543 * Class to give to each cell in this column. 12544 * @type string 12545 * @default <i>Empty string</i> 12546 * 12547 * @name DataTable.defaults.column.class 12548 * @dtopt Columns 12549 * 12550 * @example 12551 * // Using `columnDefs` 12552 * $(document).ready( function() { 12553 * $('#example').dataTable( { 12554 * "columnDefs": [ 12555 * { "class": "my_class", "targets": [ 0 ] } 12556 * ] 12557 * } ); 12558 * } ); 12559 * 12560 * @example 12561 * // Using `columns` 12562 * $(document).ready( function() { 12563 * $('#example').dataTable( { 12564 * "columns": [ 12565 * { "class": "my_class" }, 12566 * null, 12567 * null, 12568 * null, 12569 * null 12570 * ] 12571 * } ); 12572 * } ); 12573 */ 12574 "sClass": "", 12575 12576 /** 12577 * When DataTables calculates the column widths to assign to each column, 12578 * it finds the longest string in each column and then constructs a 12579 * temporary table and reads the widths from that. The problem with this 12580 * is that "mmm" is much wider then "iiii", but the latter is a longer 12581 * string - thus the calculation can go wrong (doing it properly and putting 12582 * it into an DOM object and measuring that is horribly(!) slow). Thus as 12583 * a "work around" we provide this option. It will append its value to the 12584 * text that is found to be the longest string for the column - i.e. padding. 12585 * Generally you shouldn't need this! 12586 * @type string 12587 * @default <i>Empty string<i> 12588 * 12589 * @name DataTable.defaults.column.contentPadding 12590 * @dtopt Columns 12591 * 12592 * @example 12593 * // Using `columns` 12594 * $(document).ready( function() { 12595 * $('#example').dataTable( { 12596 * "columns": [ 12597 * null, 12598 * null, 12599 * null, 12600 * { 12601 * "contentPadding": "mmm" 12602 * } 12603 * ] 12604 * } ); 12605 * } ); 12606 */ 12607 "sContentPadding": "", 12608 12609 12610 /** 12611 * Allows a default value to be given for a column's data, and will be used 12612 * whenever a null data source is encountered (this can be because `data` 12613 * is set to null, or because the data source itself is null). 12614 * @type string 12615 * @default null 12616 * 12617 * @name DataTable.defaults.column.defaultContent 12618 * @dtopt Columns 12619 * 12620 * @example 12621 * // Using `columnDefs` 12622 * $(document).ready( function() { 12623 * $('#example').dataTable( { 12624 * "columnDefs": [ 12625 * { 12626 * "data": null, 12627 * "defaultContent": "Edit", 12628 * "targets": [ -1 ] 12629 * } 12630 * ] 12631 * } ); 12632 * } ); 12633 * 12634 * @example 12635 * // Using `columns` 12636 * $(document).ready( function() { 12637 * $('#example').dataTable( { 12638 * "columns": [ 12639 * null, 12640 * null, 12641 * null, 12642 * { 12643 * "data": null, 12644 * "defaultContent": "Edit" 12645 * } 12646 * ] 12647 * } ); 12648 * } ); 12649 */ 12650 "sDefaultContent": null, 12651 12652 12653 /** 12654 * This parameter is only used in DataTables' server-side processing. It can 12655 * be exceptionally useful to know what columns are being displayed on the 12656 * client side, and to map these to database fields. When defined, the names 12657 * also allow DataTables to reorder information from the server if it comes 12658 * back in an unexpected order (i.e. if you switch your columns around on the 12659 * client-side, your server-side code does not also need updating). 12660 * @type string 12661 * @default <i>Empty string</i> 12662 * 12663 * @name DataTable.defaults.column.name 12664 * @dtopt Columns 12665 * 12666 * @example 12667 * // Using `columnDefs` 12668 * $(document).ready( function() { 12669 * $('#example').dataTable( { 12670 * "columnDefs": [ 12671 * { "name": "engine", "targets": [ 0 ] }, 12672 * { "name": "browser", "targets": [ 1 ] }, 12673 * { "name": "platform", "targets": [ 2 ] }, 12674 * { "name": "version", "targets": [ 3 ] }, 12675 * { "name": "grade", "targets": [ 4 ] } 12676 * ] 12677 * } ); 12678 * } ); 12679 * 12680 * @example 12681 * // Using `columns` 12682 * $(document).ready( function() { 12683 * $('#example').dataTable( { 12684 * "columns": [ 12685 * { "name": "engine" }, 12686 * { "name": "browser" }, 12687 * { "name": "platform" }, 12688 * { "name": "version" }, 12689 * { "name": "grade" } 12690 * ] 12691 * } ); 12692 * } ); 12693 */ 12694 "sName": "", 12695 12696 12697 /** 12698 * Defines a data source type for the ordering which can be used to read 12699 * real-time information from the table (updating the internally cached 12700 * version) prior to ordering. This allows ordering to occur on user 12701 * editable elements such as form inputs. 12702 * @type string 12703 * @default std 12704 * 12705 * @name DataTable.defaults.column.orderDataType 12706 * @dtopt Columns 12707 * 12708 * @example 12709 * // Using `columnDefs` 12710 * $(document).ready( function() { 12711 * $('#example').dataTable( { 12712 * "columnDefs": [ 12713 * { "orderDataType": "dom-text", "targets": [ 2, 3 ] }, 12714 * { "type": "numeric", "targets": [ 3 ] }, 12715 * { "orderDataType": "dom-select", "targets": [ 4 ] }, 12716 * { "orderDataType": "dom-checkbox", "targets": [ 5 ] } 12717 * ] 12718 * } ); 12719 * } ); 12720 * 12721 * @example 12722 * // Using `columns` 12723 * $(document).ready( function() { 12724 * $('#example').dataTable( { 12725 * "columns": [ 12726 * null, 12727 * null, 12728 * { "orderDataType": "dom-text" }, 12729 * { "orderDataType": "dom-text", "type": "numeric" }, 12730 * { "orderDataType": "dom-select" }, 12731 * { "orderDataType": "dom-checkbox" } 12732 * ] 12733 * } ); 12734 * } ); 12735 */ 12736 "sSortDataType": "std", 12737 12738 12739 /** 12740 * The title of this column. 12741 * @type string 12742 * @default null <i>Derived from the 'TH' value for this column in the 12743 * original HTML table.</i> 12744 * 12745 * @name DataTable.defaults.column.title 12746 * @dtopt Columns 12747 * 12748 * @example 12749 * // Using `columnDefs` 12750 * $(document).ready( function() { 12751 * $('#example').dataTable( { 12752 * "columnDefs": [ 12753 * { "title": "My column title", "targets": [ 0 ] } 12754 * ] 12755 * } ); 12756 * } ); 12757 * 12758 * @example 12759 * // Using `columns` 12760 * $(document).ready( function() { 12761 * $('#example').dataTable( { 12762 * "columns": [ 12763 * { "title": "My column title" }, 12764 * null, 12765 * null, 12766 * null, 12767 * null 12768 * ] 12769 * } ); 12770 * } ); 12771 */ 12772 "sTitle": null, 12773 12774 12775 /** 12776 * The type allows you to specify how the data for this column will be
12777 * ordered. Four types (string, numeric, date and html (which will strip 12778 * HTML tags before ordering)) are currently available. Note that only date 12779 * formats understood by Javascript's Date() object will be accepted as type 12780 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 12781 * 'numeric', 'date' or 'html' (by default). Further types can be adding 12782 * through plug-ins. 12783 * @type string 12784 * @default null <i>Auto-detected from raw data</i> 12785 * 12786 * @name DataTable.defaults.column.type 12787 * @dtopt Columns 12788 * 12789 * @example 12790 * // Using `columnDefs` 12791 * $(document).ready( function() { 12792 * $('#example').dataTable( { 12793 * "columnDefs": [ 12794 * { "type": "html", "targets": [ 0 ] } 12795 * ] 12796 * } ); 12797 * } ); 12798 * 12799 * @example 12800 * // Using `columns` 12801 * $(document).ready( function() { 12802 * $('#example').dataTable( { 12803 * "columns": [ 12804 * { "type": "html" }, 12805 * null, 12806 * null, 12807 * null, 12808 * null 12809 * ] 12810 * } ); 12811 * } ); 12812 */ 12813 "sType": null, 12814 12815 12816 /** 12817 * Defining the width of the column, this parameter may take any CSS value 12818 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not 12819 * been given a specific width through this interface ensuring that the table 12820 * remains readable. 12821 * @type string 12822 * @default null <i>Automatic</i> 12823 * 12824 * @name DataTable.defaults.column.width 12825 * @dtopt Columns 12826 * 12827 * @example 12828 * // Using `columnDefs` 12829 * $(document).ready( function() { 12830 * $('#example').dataTable( { 12831 * "columnDefs": [ 12832 * { "width": "20%", "targets": [ 0 ] } 12833 * ] 12834 * } ); 12835 * } ); 12836 * 12837 * @example 12838 * // Using `columns` 12839 * $(document).ready( function() { 12840 * $('#example').dataTable( { 12841 * "columns": [ 12842 * { "width": "20%" }, 12843 * null, 12844 * null, 12845 * null, 12846 * null 12847 * ] 12848 * } ); 12849 * } ); 12850 */ 12851 "sWidth": null 12852 }; 12853 12854 _fnHungarianMap( DataTable.defaults.column ); 12855 12856 12857 12858 /** 12859 * DataTables settings object - this holds all the information needed for a 12860 * given table, including configuration, data and current application of the 12861 * table options. DataTables does not have a single instance for each DataTable 12862 * with the settings attached to that instance, but rather instances of the 12863 * DataTable "class" are created on-the-fly as needed (typically by a 12864 * $().dataTable() call) and the settings object is then applied to that 12865 * instance. 12866 * 12867 * Note that this object is related to {@link DataTable.defaults} but this 12868 * one is the internal data store for DataTables's cache of columns. It should 12869 * NOT be manipulated outside of DataTables. Any configuration should be done 12870 * through the initialisation options. 12871 * @namespace 12872 * @todo Really should attach the settings object to individual instances so we 12873 * don't need to create new instances on each $().dataTable() call (if the 12874 * table already exists). It would also save passing oSettings around and 12875 * into every single function. However, this is a very significant 12876 * architecture change for DataTables and will almost certainly break 12877 * backwards compatibility with older installations. This is something that 12878 * will be done in 2.0. 12879 */ 12880 DataTable.models.oSettings = { 12881 /** 12882 * Primary features of DataTables and their enablement state. 12883 * @namespace 12884 */ 12885 "oFeatures": { 12886 12887 /** 12888 * Flag to say if DataTables should automatically try to calculate the 12889 * optimum table and columns widths (true) or not (false). 12890 * Note that this parameter will be set by the initialisation routine. To 12891 * set a default use {@link DataTable.defaults}. 12892 * @type boolean 12893 */ 12894 "bAutoWidth": null, 12895 12896 /** 12897 * Delay the creation of TR and TD elements until they are actually 12898 * needed by a driven page draw. This can give a significant speed 12899 * increase for Ajax source and Javascript source data, but makes no 12900 * difference at all fro DOM and server-side processing tables. 12901 * Note that this parameter will be set by the initialisation routine. To 12902 * set a default use {@link DataTable.defaults}. 12903 * @type boolean 12904 */ 12905 "bDeferRender": null, 12906 12907 /** 12908 * Enable filtering on the table or not. Note that if this is disabled 12909 * then there is no filtering at all on the table, including fnFilter. 12910 * To just remove the filtering input use sDom and remove the 'f' option. 12911 * Note that this parameter will be set by the initialisation routine. To 12912 * set a default use {@link DataTable.defaults}. 12913 * @type boolean 12914 */ 12915 "bFilter": null, 12916 12917 /** 12918 * Table information element (the 'Showing x of y records' div) enable 12919 * flag. 12920 * Note that this parameter will be set by the initialisation routine. To 12921 * set a default use {@link DataTable.defaults}. 12922 * @type boolean 12923 */ 12924 "bInfo": null, 12925 12926 /** 12927 * Present a user control allowing the end user to change the page size 12928 * when pagination is enabled. 12929 * Note that this parameter will be set by the initialisation routine. To 12930 * set a default use {@link DataTable.defaults}. 12931 * @type boolean 12932 */ 12933 "bLengthChange": null, 12934 12935 /** 12936 * Pagination enabled or not. Note that if this is disabled then length 12937 * changing must also be disabled. 12938 * Note that this parameter will be set by the initialisation routine. To 12939 * set a default use {@link DataTable.defaults}. 12940 * @type boolean 12941 */ 12942 "bPaginate": null, 12943 12944 /** 12945 * Processing indicator enable flag whenever DataTables is enacting a 12946 * user request - typically an Ajax request for server-side processing. 12947 * Note that this parameter will be set by the initialisation routine. To 12948 * set a default use {@link DataTable.defaults}. 12949 * @type boolean 12950 */ 12951 "bProcessing": null, 12952 12953 /** 12954 * Server-side processing enabled flag - when enabled DataTables will 12955 * get all data from the server for every draw - there is no filtering, 12956 * sorting or paging done on the client-side. 12957 * Note that this parameter will be set by the initialisation routine. To 12958 * set a default use {@link DataTable.defaults}. 12959 * @type boolean 12960 */ 12961 "bServerSide": null, 12962 12963 /** 12964 * Sorting enablement flag. 12965 * Note that this parameter will be set by the initialisation routine. To 12966 * set a default use {@link DataTable.defaults}. 12967 * @type boolean 12968 */ 12969 "bSort": null, 12970 12971 /**
12972 * Multi-column sorting 12973 * Note that this parameter will be set by the initialisation routine. To 12974 * set a default use {@link DataTable.defaults}. 12975 * @type boolean 12976 */ 12977 "bSortMulti": null, 12978 12979 /** 12980 * Apply a class to the columns which are being sorted to provide a 12981 * visual highlight or not. This can slow things down when enabled since 12982 * there is a lot of DOM interaction. 12983 * Note that this parameter will be set by the initialisation routine. To 12984 * set a default use {@link DataTable.defaults}. 12985 * @type boolean 12986 */ 12987 "bSortClasses": null, 12988 12989 /** 12990 * State saving enablement flag. 12991 * Note that this parameter will be set by the initialisation routine. To 12992 * set a default use {@link DataTable.defaults}. 12993 * @type boolean 12994 */ 12995 "bStateSave": null 12996 }, 12997 12998 12999 /** 13000 * Scrolling settings for a table. 13001 * @namespace 13002 */ 13003 "oScroll": { 13004 /** 13005 * When the table is shorter in height than sScrollY, collapse the 13006 * table container down to the height of the table (when true). 13007 * Note that this parameter will be set by the initialisation routine. To 13008 * set a default use {@link DataTable.defaults}. 13009 * @type boolean 13010 */ 13011 "bCollapse": null, 13012 13013 /** 13014 * Width of the scrollbar for the web-browser's platform. Calculated 13015 * during table initialisation. 13016 * @type int 13017 * @default 0 13018 */ 13019 "iBarWidth": 0, 13020 13021 /** 13022 * Viewport width for horizontal scrolling. Horizontal scrolling is 13023 * disabled if an empty string. 13024 * Note that this parameter will be set by the initialisation routine. To 13025 * set a default use {@link DataTable.defaults}. 13026 * @type string 13027 */ 13028 "sX": null, 13029 13030 /** 13031 * Width to expand the table to when using x-scrolling. Typically you 13032 * should not need to use this. 13033 * Note that this parameter will be set by the initialisation routine. To 13034 * set a default use {@link DataTable.defaults}. 13035 * @type string 13036 * @deprecated 13037 */ 13038 "sXInner": null, 13039 13040 /** 13041 * Viewport height for vertical scrolling. Vertical scrolling is disabled 13042 * if an empty string. 13043 * Note that this parameter will be set by the initialisation routine. To 13044 * set a default use {@link DataTable.defaults}. 13045 * @type string 13046 */ 13047 "sY": null 13048 }, 13049 13050 /** 13051 * Language information for the table. 13052 * @namespace 13053 * @extends DataTable.defaults.oLanguage 13054 */ 13055 "oLanguage": { 13056 /** 13057 * Information callback function. See 13058 * {@link DataTable.defaults.fnInfoCallback} 13059 * @type function 13060 * @default null 13061 */ 13062 "fnInfoCallback": null 13063 }, 13064 13065 /** 13066 * Browser support parameters 13067 * @namespace 13068 */ 13069 "oBrowser": { 13070 /** 13071 * Indicate if the browser incorrectly calculates width:100% inside a 13072 * scrolling element (IE6/7) 13073 * @type boolean 13074 * @default false 13075 */ 13076 "bScrollOversize": false, 13077 13078 /** 13079 * Determine if the vertical scrollbar is on the right or left of the 13080 * scrolling container - needed for rtl language layout, although not 13081 * all browsers move the scrollbar (Safari). 13082 * @type boolean 13083 * @default false 13084 */ 13085 "bScrollbarLeft": false, 13086 13087 /** 13088 * Flag for if `getBoundingClientRect` is fully supported or not 13089 * @type boolean 13090 * @default false 13091 */ 13092 "bBounding": false, 13093 13094 /** 13095 * Browser scrollbar width 13096 * @type integer 13097 * @default 0 13098 */ 13099 "barWidth": 0 13100 }, 13101 13102 13103 "ajax": null, 13104 13105 13106 /** 13107 * Array referencing the nodes which are used for the features. The 13108 * parameters of this object match what is allowed by sDom - i.e. 13109 * <ul> 13110 * <li>'l' - Length changing</li> 13111 * <li>'f' - Filtering input</li> 13112 * <li>'t' - The table!</li> 13113 * <li>'i' - Information</li> 13114 * <li>'p' - Pagination</li> 13115 * <li>'r' - pRocessing</li> 13116 * </ul> 13117 * @type array 13118 * @default [] 13119 */ 13120 "aanFeatures": [], 13121 13122 /** 13123 * Store data information - see {@link DataTable.models.oRow} for detailed 13124 * information. 13125 * @type array 13126 * @default [] 13127 */ 13128 "aoData": [], 13129 13130 /** 13131 * Array of indexes which are in the current display (after filtering etc) 13132 * @type array 13133 * @default [] 13134 */ 13135 "aiDisplay": [], 13136 13137 /** 13138 * Array of indexes for display - no filtering 13139 * @type array 13140 * @default [] 13141 */ 13142 "aiDisplayMaster": [], 13143 13144 /** 13145 * Map of row ids to data indexes 13146 * @type object 13147 * @default {} 13148 */ 13149 "aIds": {}, 13150 13151 /** 13152 * Store information about each column that is in use 13153 * @type array 13154 * @default [] 13155 */ 13156 "aoColumns": [], 13157 13158 /** 13159 * Store information about the table's header 13160 * @type array 13161 * @default [] 13162 */ 13163 "aoHeader": [], 13164 13165 /** 13166 * Store information about the table's footer 13167 * @type array 13168 * @default [] 13169 */ 13170 "aoFooter": [], 13171 13172 /** 13173 * Store the applied global search information in case we want to force a 13174 * research or compare the old search to a new one. 13175 * Note that this parameter will be set by the initialisation routine. To 13176 * set a default use {@link DataTable.defaults}. 13177 * @namespace 13178 * @extends DataTable.models.oSearch 13179 */ 13180 "oPreviousSearch": {}, 13181 13182 /** 13183 * Store the applied search for each column - see 13184 * {@link DataTable.models.oSearch} for the format that is used for the 13185 * filtering information for each column. 13186 * @type array 13187 * @default [] 13188 */ 13189 "aoPreSearchCols": [], 13190 13191 /** 13192 * Sorting that is applied to the table. Note that the inner arrays are 13193 * used in the following manner: 13194 * <ul> 13195 * <li>Index 0 - column number</li> 13196 * <li>Index 1 - current sorting direction</li> 13197 * </ul> 13198 * Note that this parameter will be set by the initialisation routine. To 13199 * set a default use {@link DataTable.defaults}. 13200 * @type array 13201 * @todo These inner arrays should really be objects 13202 */ 13203 "aaSorting": null, 13204 13205 /** 13206 * Sorting that is always applied to the table (i.e. prefixed in front of 13207 * aaSorting). 13208 * Note that this parameter will be set by the initialisation routine. To 13209 * set a default use {@link DataTable.defaults}. 13210 * @type array 13211 * @default [] 13212 */ 13213 "aaSortingFixed": [], 13214 13215 /** 13216 * Classes to use for the striping of a table. 13217 * Note that this parameter will be set by the initialisation routine. To 13218 * set a default use {@link DataTable.defaults}. 13219 * @type array 13220 * @default [] 13221 */ 13222 "asStripeClasses": null, 13223 13224 /** 13225 * If restoring a table - we should restore its striping classes as well 13226 * @type array 13227 * @default [] 13228 */ 13229 "asDestroyStripes": [], 13230 13231 /** 13232 * If restoring a table - we should restore its width 13233 * @type int 13234 * @default 0 13235 */ 13236 "sDestroyWidth": 0, 13237 13238 /** 13239 * Callback functions array for every time a row is inserted (i.e. on a draw). 13240 * @type array 13241 * @default [] 13242 */ 13243 "aoRowCallback": [], 13244 13245 /** 13246 * Callback functions for the header on each draw. 13247 * @type array 13248 * @default [] 13249 */ 13250 "aoHeaderCallback": [], 13251 13252 /** 13253 * Callback function for the footer on each draw. 13254 * @type array 13255 * @default [] 13256 */ 13257 "aoFooterCallback": [], 13258 13259 /** 13260 * Array of callback functions for draw callback functions 13261 * @type array 13262 * @default [] 13263 */ 13264 "aoDrawCallback": [], 13265 13266 /** 13267 * Array of callback functions for row created function 13268 * @type array 13269 * @default [] 13270 */ 13271 "aoRowCreatedCallback": [], 13272 13273 /** 13274 * Callback functions for just before the table is redrawn. A return of 13275 * false will be used to cancel the draw. 13276 * @type array 13277 * @default [] 13278 */ 13279 "aoPreDrawCallback": [], 13280 13281 /** 13282 * Callback functions for when the table has been initialised. 13283 * @type array 13284 * @default [] 13285 */ 13286 "aoInitComplete": [], 13287 13288 13289 /** 13290 * Callbacks for modifying the settings to be stored for state saving, prior to 13291 * saving state. 13292 * @type array 13293 * @default [] 13294 */ 13295 "aoStateSaveParams": [], 13296 13297 /** 13298 * Callbacks for modifying the settings that have been stored for state saving 13299 * prior to using the stored values to restore the state. 13300 * @type array 13301 * @default [] 13302 */ 13303 "aoStateLoadParams": [], 13304 13305 /** 13306 * Callbacks for operating on the settings object once the saved state has been 13307 * loaded 13308 * @type array 13309 * @default [] 13310 */ 13311 "aoStateLoaded": [], 13312 13313 /** 13314 * Cache the table ID for quick access 13315 * @type string 13316 * @default <i>Empty string</i> 13317 */ 13318 "sTableId": "", 13319 13320 /** 13321 * The TABLE node for the main table 13322 * @type node 13323 * @default null 13324 */ 13325 "nTable": null, 13326 13327 /** 13328 * Permanent ref to the thead element 13329 * @type node 13330 * @default null 13331 */ 13332 "nTHead": null, 13333 13334 /** 13335 * Permanent ref to the tfoot element - if it exists 13336 * @type node 13337 * @default null 13338 */ 13339 "nTFoot": null, 13340 13341 /** 13342 * Permanent ref to the tbody element 13343 * @type node 13344 * @default null 13345 */ 13346 "nTBody": null, 13347 13348 /** 13349 * Cache the wrapper node (contains all DataTables controlled elements) 13350 * @type node 13351 * @default null 13352 */ 13353 "nTableWrapper": null, 13354 13355 /** 13356 * Indicate if when using server-side processing the loading of data 13357 * should be deferred until the second draw. 13358 * Note that this parameter will be set by the initialisation routine. To 13359 * set a default use {@link DataTable.defaults}. 13360 * @type boolean 13361 * @default false 13362 */ 13363 "bDeferLoading": false, 13364 13365 /** 13366 * Indicate if all required information has been read in 13367 * @type boolean 13368 * @default false 13369 */ 13370 "bInitialised": false, 13371 13372 /** 13373 * Information about open rows. Each object in the array has the parameters 13374 * 'nTr' and 'nParent' 13375 * @type array 13376 * @default [] 13377 */ 13378 "aoOpenRows": [], 13379 13380 /**
13381 * Dictate the positioning of DataTables' control elements - see 13382 * {@link DataTable.model.oInit.sDom}. 13383 * Note that this parameter will be set by the initialisation routine. To 13384 * set a default use {@link DataTable.defaults}. 13385 * @type string 13386 * @default null 13387 */ 13388 "sDom": null, 13389 13390 /** 13391 * Search delay (in mS) 13392 * @type integer 13393 * @default null 13394 */ 13395 "searchDelay": null, 13396 13397 /** 13398 * Which type of pagination should be used. 13399 * Note that this parameter will be set by the initialisation routine. To 13400 * set a default use {@link DataTable.defaults}. 13401 * @type string 13402 * @default two_button 13403 */ 13404 "sPaginationType": "two_button", 13405 13406 /** 13407 * The state duration (for `stateSave`) in seconds. 13408 * Note that this parameter will be set by the initialisation routine. To 13409 * set a default use {@link DataTable.defaults}. 13410 * @type int 13411 * @default 0 13412 */ 13413 "iStateDuration": 0, 13414 13415 /** 13416 * Array of callback functions for state saving. Each array element is an 13417 * object with the following parameters: 13418 * <ul> 13419 * <li>function:fn - function to call. Takes two parameters, oSettings 13420 * and the JSON string to save that has been thus far created. Returns 13421 * a JSON string to be inserted into a json object 13422 * (i.e. '"param": [ 0, 1, 2]')</li> 13423 * <li>string:sName - name of callback</li> 13424 * </ul> 13425 * @type array 13426 * @default [] 13427 */ 13428 "aoStateSave": [], 13429 13430 /** 13431 * Array of callback functions for state loading. Each array element is an 13432 * object with the following parameters: 13433 * <ul> 13434 * <li>function:fn - function to call. Takes two parameters, oSettings 13435 * and the object stored. May return false to cancel state loading</li> 13436 * <li>string:sName - name of callback</li> 13437 * </ul> 13438 * @type array 13439 * @default [] 13440 */ 13441 "aoStateLoad": [], 13442 13443 /** 13444 * State that was saved. Useful for back reference 13445 * @type object 13446 * @default null 13447 */ 13448 "oSavedState": null, 13449 13450 /** 13451 * State that was loaded. Useful for back reference 13452 * @type object 13453 * @default null 13454 */ 13455 "oLoadedState": null, 13456 13457 /** 13458 * Source url for AJAX data for the table. 13459 * Note that this parameter will be set by the initialisation routine. To 13460 * set a default use {@link DataTable.defaults}. 13461 * @type string 13462 * @default null 13463 */ 13464 "sAjaxSource": null, 13465 13466 /** 13467 * Property from a given object from which to read the table data from. This 13468 * can be an empty string (when not server-side processing), in which case 13469 * it is assumed an an array is given directly. 13470 * Note that this parameter will be set by the initialisation routine. To 13471 * set a default use {@link DataTable.defaults}. 13472 * @type string 13473 */ 13474 "sAjaxDataProp": null, 13475 13476 /** 13477 * Note if draw should be blocked while getting data 13478 * @type boolean 13479 * @default true 13480 */ 13481 "bAjaxDataGet": true, 13482 13483 /** 13484 * The last jQuery XHR object that was used for server-side data gathering. 13485 * This can be used for working with the XHR information in one of the 13486 * callbacks 13487 * @type object 13488 * @default null 13489 */ 13490 "jqXHR": null, 13491 13492 /** 13493 * JSON returned from the server in the last Ajax request 13494 * @type object 13495 * @default undefined 13496 */ 13497 "json": undefined, 13498 13499 /** 13500 * Data submitted as part of the last Ajax request 13501 * @type object 13502 * @default undefined 13503 */ 13504 "oAjaxData": undefined, 13505 13506 /** 13507 * Function to get the server-side data. 13508 * Note that this parameter will be set by the initialisation routine. To 13509 * set a default use {@link DataTable.defaults}. 13510 * @type function 13511 */ 13512 "fnServerData": null, 13513 13514 /** 13515 * Functions which are called prior to sending an Ajax request so extra 13516 * parameters can easily be sent to the server 13517 * @type array 13518 * @default [] 13519 */ 13520 "aoServerParams": [], 13521 13522 /** 13523 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if 13524 * required). 13525 * Note that this parameter will be set by the initialisation routine. To 13526 * set a default use {@link DataTable.defaults}. 13527 * @type string 13528 */ 13529 "sServerMethod": null, 13530 13531 /** 13532 * Format numbers for display. 13533 * Note that this parameter will be set by the initialisation routine. To 13534 * set a default use {@link DataTable.defaults}. 13535 * @type function 13536 */ 13537 "fnFormatNumber": null, 13538 13539 /** 13540 * List of options that can be used for the user selectable length menu. 13541 * Note that this parameter will be set by the initialisation routine. To 13542 * set a default use {@link DataTable.defaults}. 13543 * @type array 13544 * @default [] 13545 */ 13546 "aLengthMenu": null, 13547 13548 /** 13549 * Counter for the draws that the table does. Also used as a tracker for 13550 * server-side processing 13551 * @type int 13552 * @default 0 13553 */ 13554 "iDraw": 0, 13555 13556 /** 13557 * Indicate if a redraw is being done - useful for Ajax 13558 * @type boolean 13559 * @default false 13560 */ 13561 "bDrawing": false, 13562 13563 /** 13564 * Draw index (iDraw) of the last error when parsing the returned data 13565 * @type int 13566 * @default -1 13567 */ 13568 "iDrawError": -1, 13569 13570 /** 13571 * Paging display length 13572 * @type int 13573 * @default 10 13574 */ 13575 "_iDisplayLength": 10, 13576 13577 /** 13578 * Paging start point - aiDisplay index 13579 * @type int 13580 * @default 0 13581 */ 13582 "_iDisplayStart": 0, 13583 13584 /** 13585 * Server-side processing - number of records in the result set 13586 * (i.e. before filtering), Use fnRecordsTotal rather than 13587 * this property to get the value of the number of records, regardless of 13588 * the server-side processing setting. 13589 * @type int 13590 * @default 0 13591 * @private 13592 */ 13593 "_iRecordsTotal": 0, 13594 13595 /** 13596 * Server-side processing - number of records in the current display set 13597 * (i.e. after filtering). Use fnRecordsDisplay rather than 13598 * this property to get the value of the number of records, regardless of 13599 * the server-side processing setting. 13600 * @type boolean 13601 * @default 0 13602 * @private 13603 */ 13604 "_iRecordsDisplay": 0, 13605 13606 /** 13607 * Flag to indicate if jQuery UI marking and classes should be used. 13608 * Note that this parameter will be set by the initialisation routine. To 13609 * set a default use {@link DataTable.defaults}. 13610 * @type boolean 13611 */ 13612 "bJUI": null, 13613 13614 /** 13615 * The classes to use for the table 13616 * @type object 13617 * @default {} 13618 */
13619 "oClasses": {}, 13620 13621 /** 13622 * Flag attached to the settings object so you can check in the draw 13623 * callback if filtering has been done in the draw. Deprecated in favour of 13624 * events. 13625 * @type boolean 13626 * @default false 13627 * @deprecated 13628 */ 13629 "bFiltered": false, 13630 13631 /** 13632 * Flag attached to the settings object so you can check in the draw 13633 * callback if sorting has been done in the draw. Deprecated in favour of 13634 * events. 13635 * @type boolean 13636 * @default false 13637 * @deprecated 13638 */ 13639 "bSorted": false, 13640 13641 /** 13642 * Indicate that if multiple rows are in the header and there is more than 13643 * one unique cell per column, if the top one (true) or bottom one (false) 13644 * should be used for sorting / title by DataTables. 13645 * Note that this parameter will be set by the initialisation routine. To 13646 * set a default use {@link DataTable.defaults}. 13647 * @type boolean 13648 */ 13649 "bSortCellsTop": null, 13650 13651 /** 13652 * Initialisation object that is used for the table 13653 * @type object 13654 * @default null 13655 */ 13656 "oInit": null, 13657 13658 /** 13659 * Destroy callback functions - for plug-ins to attach themselves to the 13660 * destroy so they can clean up markup and events. 13661 * @type array 13662 * @default [] 13663 */ 13664 "aoDestroyCallback": [], 13665 13666 13667 /** 13668 * Get the number of records in the current record set, before filtering 13669 * @type function 13670 */ 13671 "fnRecordsTotal": function () 13672 { 13673 return _fnDataSource( this ) == 'ssp' ? 13674 this._iRecordsTotal * 1 : 13675 this.aiDisplayMaster.length; 13676 }, 13677 13678 /** 13679 * Get the number of records in the current record set, after filtering 13680 * @type function 13681 */ 13682 "fnRecordsDisplay": function () 13683 { 13684 return _fnDataSource( this ) == 'ssp' ? 13685 this._iRecordsDisplay * 1 : 13686 this.aiDisplay.length; 13687 }, 13688 13689 /** 13690 * Get the display end point - aiDisplay index 13691 * @type function 13692 */ 13693 "fnDisplayEnd": function () 13694 { 13695 var 13696 len = this._iDisplayLength, 13697 start = this._iDisplayStart, 13698 calc = start + len, 13699 records = this.aiDisplay.length, 13700 features = this.oFeatures, 13701 paginate = features.bPaginate; 13702 13703 if ( features.bServerSide ) { 13704 return paginate === false || len === -1 ? 13705 start + records : 13706 Math.min( start+len, this._iRecordsDisplay ); 13707 } 13708 else { 13709 return ! paginate || calc>records || len===-1 ? 13710 records : 13711 calc; 13712 } 13713 }, 13714 13715 /** 13716 * The DataTables object for this table 13717 * @type object 13718 * @default null 13719 */ 13720 "oInstance": null, 13721 13722 /** 13723 * Unique identifier for each instance of the DataTables object. If there 13724 * is an ID on the table node, then it takes that value, otherwise an 13725 * incrementing internal counter is used. 13726 * @type string 13727 * @default null 13728 */ 13729 "sInstance": null, 13730 13731 /** 13732 * tabindex attribute value that is added to DataTables control elements, allowing 13733 * keyboard navigation of the table and its controls. 13734 */ 13735 "iTabIndex": 0, 13736 13737 /** 13738 * DIV container for the footer scrolling table if scrolling 13739 */ 13740 "nScrollHead": null, 13741 13742 /** 13743 * DIV container for the footer scrolling table if scrolling 13744 */ 13745 "nScrollFoot": null, 13746 13747 /** 13748 * Last applied sort 13749 * @type array 13750 * @default [] 13751 */ 13752 "aLastSort": [], 13753 13754 /** 13755 * Stored plug-in instances 13756 * @type object 13757 * @default {} 13758 */ 13759 "oPlugins": {}, 13760 13761 /** 13762 * Function used to get a row's id from the row's data 13763 * @type function 13764 * @default null 13765 */ 13766 "rowIdFn": null, 13767 13768 /** 13769 * Data location where to store a row's id 13770 * @type string 13771 * @default null 13772 */ 13773 "rowId": null 13774 }; 13775 13776 /** 13777 * Extension object for DataTables that is used to provide all extension 13778 * options. 13779 * 13780 * Note that the `DataTable.ext` object is available through 13781 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is 13782 * also aliased to `jQuery.fn.dataTableExt` for historic reasons. 13783 * @namespace 13784 * @extends DataTable.models.ext 13785 */ 13786 13787 13788 /** 13789 * DataTables extensions 13790 * 13791 * This namespace acts as a collection area for plug-ins that can be used to 13792 * extend DataTables capabilities. Indeed many of the build in methods 13793 * use this method to provide their own capabilities (sorting methods for 13794 * example). 13795 * 13796 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy 13797 * reasons 13798 * 13799 * @namespace 13800 */ 13801 DataTable.ext = _ext = { 13802 /** 13803 * Buttons. For use with the Buttons extension for DataTables. This is 13804 * defined here so other extensions can define buttons regardless of load 13805 * order. It is _not_ used by DataTables core. 13806 * 13807 * @type object 13808 * @default {} 13809 */ 13810 buttons: {}, 13811 13812 13813 /** 13814 * Element class names 13815 * 13816 * @type object 13817 * @default {} 13818 */ 13819 classes: {}, 13820 13821 13822 /** 13823 * DataTables build type (expanded by the download builder) 13824 * 13825 * @type string 13826 */ 13827 build:"dt/dt-1.10.11,af-2.1.1,b-1.1.2,b-html5-1.1.2,fh-3.1.1", 13828 13829 13830 /** 13831 * Error reporting. 13832 * 13833 * How should DataTables report an error. Can take the value 'alert', 13834 * 'throw', 'none' or a function. 13835 * 13836 * @type string|function 13837 * @default alert 13838 */ 13839 errMode: "alert", 13840 13841 13842 /**
13843 * Feature plug-ins. 13844 * 13845 * This is an array of objects which describe the feature plug-ins that are 13846 * available to DataTables. These feature plug-ins are then available for 13847 * use through the `dom` initialisation option. 13848 * 13849 * Each feature plug-in is described by an object which must have the 13850 * following properties: 13851 * 13852 * * `fnInit` - function that is used to initialise the plug-in, 13853 * * `cFeature` - a character so the feature can be enabled by the `dom` 13854 * instillation option. This is case sensitive. 13855 * 13856 * The `fnInit` function has the following input parameters: 13857 * 13858 * 1. `{object}` DataTables settings object: see 13859 * {@link DataTable.models.oSettings} 13860 * 13861 * And the following return is expected: 13862 * 13863 * * {node|null} The element which contains your feature. Note that the 13864 * return may also be void if your plug-in does not require to inject any 13865 * DOM elements into DataTables control (`dom`) - for example this might 13866 * be useful when developing a plug-in which allows table control via 13867 * keyboard entry 13868 * 13869 * @type array 13870 * 13871 * @example 13872 * $.fn.dataTable.ext.features.push( { 13873 * "fnInit": function( oSettings ) { 13874 * return new TableTools( { "oDTSettings": oSettings } ); 13875 * }, 13876 * "cFeature": "T" 13877 * } ); 13878 */ 13879 feature: [], 13880 13881 13882 /** 13883 * Row searching. 13884 * 13885 * This method of searching is complimentary to the default type based 13886 * searching, and a lot more comprehensive as it allows you complete control 13887 * over the searching logic. Each element in this array is a function 13888 * (parameters described below) that is called for every row in the table, 13889 * and your logic decides if it should be included in the searching data set 13890 * or not. 13891 * 13892 * Searching functions have the following input parameters: 13893 * 13894 * 1. `{object}` DataTables settings object: see 13895 * {@link DataTable.models.oSettings} 13896 * 2. `{array|object}` Data for the row to be processed (same as the 13897 * original format that was passed in as the data source, or an array 13898 * from a DOM data source 13899 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which 13900 * can be useful to retrieve the `TR` element if you need DOM interaction. 13901 * 13902 * And the following return is expected: 13903 * 13904 * * {boolean} Include the row in the searched result set (true) or not 13905 * (false) 13906 * 13907 * Note that as with the main search ability in DataTables, technically this 13908 * is "filtering", since it is subtractive. However, for consistency in 13909 * naming we call it searching here. 13910 * 13911 * @type array 13912 * @default [] 13913 * 13914 * @example 13915 * // The following example shows custom search being applied to the 13916 * // fourth column (i.e. the data[3] index) based on two input values 13917 * // from the end-user, matching the data in a certain range. 13918 * $.fn.dataTable.ext.search.push( 13919 * function( settings, data, dataIndex ) { 13920 * var min = document.getElementById('min').value * 1; 13921 * var max = document.getElementById('max').value * 1; 13922 * var version = data[3] == "-" ? 0 : data[3]*1; 13923 * 13924 * if ( min == "" && max == "" ) { 13925 * return true; 13926 * } 13927 * else if ( min == "" && version < max ) { 13928 * return true; 13929 * } 13930 * else if ( min < version && "" == max ) { 13931 * return true; 13932 * } 13933 * else if ( min < version && version < max ) { 13934 * return true; 13935 * } 13936 * return false; 13937 * } 13938 * ); 13939 */ 13940 search: [], 13941 13942 13943 /** 13944 * Selector extensions 13945 * 13946 * The `selector` option can be used to extend the options available for the 13947 * selector modifier options (`selector-modifier` object data type) that 13948 * each of the three built in selector types offer (row, column and cell + 13949 * their plural counterparts). For example the Select extension uses this 13950 * mechanism to provide an option to select only rows, columns and cells 13951 * that have been marked as selected by the end user (`{selected: true}`), 13952 * which can be used in conjunction with the existing built in selector 13953 * options. 13954 * 13955 * Each property is an array to which functions can be pushed. The functions 13956 * take three attributes: 13957 * 13958 * * Settings object for the host table 13959 * * Options object (`selector-modifier` object type) 13960 * * Array of selected item indexes 13961 * 13962 * The return is an array of the resulting item indexes after the custom 13963 * selector has been applied. 13964 * 13965 * @type object 13966 */ 13967 selector: { 13968 cell: [], 13969 column: [], 13970 row: [] 13971 }, 13972 13973 13974 /** 13975 * Internal functions, exposed for used in plug-ins. 13976 * 13977 * Please note that you should not need to use the internal methods for 13978 * anything other than a plug-in (and even then, try to avoid if possible). 13979 * The internal function may change between releases. 13980 * 13981 * @type object 13982 * @default {} 13983 */ 13984 internal: {}, 13985 13986 13987 /** 13988 * Legacy configuration options. Enable and disable legacy options that 13989 * are available in DataTables. 13990 * 13991 * @type object 13992 */ 13993 legacy: { 13994 /** 13995 * Enable / disable DataTables 1.9 compatible server-side processing 13996 * requests 13997 * 13998 * @type boolean 13999 * @default null 14000 */ 14001 ajax: null 14002 }, 14003 14004 14005 /** 14006 * Pagination plug-in methods. 14007 * 14008 * Each entry in this object is a function and defines which buttons should 14009 * be shown by the pagination rendering method that is used for the table: 14010 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the 14011 * buttons are displayed in the document, while the functions here tell it 14012 * what buttons to display. This is done by returning an array of button 14013 * descriptions (what each button will do). 14014 * 14015 * Pagination types (the four built in options and any additional plug-in 14016 * options defined here) can be used through the `paginationType` 14017 * initialisation parameter. 14018 * 14019 * The functions defined take two parameters: 14020 * 14021 * 1. `{int} page` The current page index 14022 * 2. `{int} pages` The number of pages in the table 14023 * 14024 * Each function is expected to return an array where each element of the 14025 * array can be one of: 14026 * 14027 * * `first` - Jump to first page when activated 14028 * * `last` - Jump to last page when activated 14029 * * `previous` - Show previous page when activated 14030 * * `next` - Show next page when activated 14031 * * `{int}` - Show page of the index given 14032 * * `{array}` - A nested array containing the above elements to add a 14033 * containing 'DIV' element (might be useful for styling). 14034 * 14035 * Note that DataTables v1.9- used this object slightly differently whereby 14036 * an object with two functions would be defined for each plug-in. That 14037 * ability is still supported by DataTables 1.10+ to provide backwards 14038 * compatibility, but this option of use is now decremented and no longer 14039 * documented in DataTables 1.10+. 14040 * 14041 * @type object 14042 * @default {} 14043 * 14044 * @example 14045 * // Show previous, next and current page buttons only 14046 * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { 14047 * return [ 'previous', page, 'next' ]; 14048 * }; 14049 */ 14050 pager: {}, 14051 14052 14053 renderer: { 14054 pageButton: {}, 14055 header: {} 14056 }, 14057 14058 14059 /** 14060 * Ordering plug-ins - custom data source 14061 *
14062 * The extension options for ordering of data available here is complimentary 14063 * to the default type based ordering that DataTables typically uses. It 14064 * allows much greater control over the the data that is being used to 14065 * order a column, but is necessarily therefore more complex. 14066 * 14067 * This type of ordering is useful if you want to do ordering based on data 14068 * live from the DOM (for example the contents of an 'input' element) rather 14069 * than just the static string that DataTables knows of. 14070 * 14071 * The way these plug-ins work is that you create an array of the values you 14072 * wish to be ordering for the column in question and then return that 14073 * array. The data in the array much be in the index order of the rows in 14074 * the table (not the currently ordering order!). Which order data gathering 14075 * function is run here depends on the `dt-init columns.orderDataType` 14076 * parameter that is used for the column (if any). 14077 * 14078 * The functions defined take two parameters: 14079 * 14080 * 1. `{object}` DataTables settings object: see 14081 * {@link DataTable.models.oSettings} 14082 * 2. `{int}` Target column index 14083 * 14084 * Each function is expected to return an array: 14085 * 14086 * * `{array}` Data for the column to be ordering upon 14087 * 14088 * @type array 14089 * 14090 * @example 14091 * // Ordering using `input` node values 14092 * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) 14093 * { 14094 * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { 14095 * return $('input', td).val(); 14096 * } ); 14097 * } 14098 */ 14099 order: {}, 14100 14101 14102 /** 14103 * Type based plug-ins. 14104 * 14105 * Each column in DataTables has a type assigned to it, either by automatic 14106 * detection or by direct assignment using the `type` option for the column. 14107 * The type of a column will effect how it is ordering and search (plug-ins 14108 * can also make use of the column type if required). 14109 * 14110 * @namespace 14111 */ 14112 type: { 14113 /** 14114 * Type detection functions. 14115 * 14116 * The functions defined in this object are used to automatically detect 14117 * a column's type, making initialisation of DataTables super easy, even 14118 * when complex data is in the table. 14119 * 14120 * The functions defined take two parameters: 14121 * 14122 * 1. `{*}` Data from the column cell to be analysed 14123 * 2. `{settings}` DataTables settings object. This can be used to 14124 * perform context specific type detection - for example detection 14125 * based on language settings such as using a comma for a decimal 14126 * place. Generally speaking the options from the settings will not 14127 * be required 14128 * 14129 * Each function is expected to return: 14130 * 14131 * * `{string|null}` Data type detected, or null if unknown (and thus 14132 * pass it on to the other type detection functions. 14133 * 14134 * @type array 14135 * 14136 * @example 14137 * // Currency type detection plug-in: 14138 * $.fn.dataTable.ext.type.detect.push( 14139 * function ( data, settings ) { 14140 * // Check the numeric part 14141 * if ( ! $.isNumeric( data.substring(1) ) ) { 14142 * return null; 14143 * } 14144 * 14145 * // Check prefixed by currency 14146 * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) { 14147 * return 'currency'; 14148 * } 14149 * return null; 14150 * } 14151 * ); 14152 */ 14153 detect: [], 14154 14155 14156 /** 14157 * Type based search formatting. 14158 * 14159 * The type based searching functions can be used to pre-format the 14160 * data to be search on. For example, it can be used to strip HTML 14161 * tags or to de-format telephone numbers for numeric only searching. 14162 * 14163 * Note that is a search is not defined for a column of a given type, 14164 * no search formatting will be performed. 14165 * 14166 * Pre-processing of searching data plug-ins - When you assign the sType 14167 * for a column (or have it automatically detected for you by DataTables 14168 * or a type detection plug-in), you will typically be using this for 14169 * custom sorting, but it can also be used to provide custom searching 14170 * by allowing you to pre-processing the data and returning the data in 14171 * the format that should be searched upon. This is done by adding 14172 * functions this object with a parameter name which matches the sType 14173 * for that target column. This is the corollary of <i>afnSortData</i> 14174 * for searching data. 14175 * 14176 * The functions defined take a single parameter: 14177 * 14178 * 1. `{*}` Data from the column cell to be prepared for searching 14179 *
14180 * Each function is expected to return: 14181 * 14182 * * `{string|null}` Formatted string that will be used for the searching. 14183 * 14184 * @type object 14185 * @default {} 14186 * 14187 * @example 14188 * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { 14189 * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); 14190 * } 14191 */ 14192 search: {}, 14193 14194 14195 /** 14196 * Type based ordering. 14197 * 14198 * The column type tells DataTables what ordering to apply to the table 14199 * when a column is sorted upon. The order for each type that is defined, 14200 * is defined by the functions available in this object. 14201 * 14202 * Each ordering option can be described by three properties added to 14203 * this object: 14204 * 14205 * * `{type}-pre` - Pre-formatting function 14206 * * `{type}-asc` - Ascending order function 14207 * * `{type}-desc` - Descending order function 14208 * 14209 * All three can be used together, only `{type}-pre` or only 14210 * `{type}-asc` and `{type}-desc` together. It is generally recommended 14211 * that only `{type}-pre` is used, as this provides the optimal 14212 * implementation in terms of speed, although the others are provided 14213 * for compatibility with existing Javascript sort functions. 14214 * 14215 * `{type}-pre`: Functions defined take a single parameter: 14216 * 14217 * 1. `{*}` Data from the column cell to be prepared for ordering 14218 * 14219 * And return: 14220 * 14221 * * `{*}` Data to be sorted upon 14222 * 14223 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort 14224 * functions, taking two parameters: 14225 * 14226 * 1. `{*}` Data to compare to the second parameter 14227 * 2. `{*}` Data to compare to the first parameter 14228 * 14229 * And returning: 14230 * 14231 * * `{*}` Ordering match: <0 if first parameter should be sorted lower 14232 * than the second parameter, ===0 if the two parameters are equal and 14233 * >0 if the first parameter should be sorted height than the second 14234 * parameter. 14235 * 14236 * @type object 14237 * @default {} 14238 * 14239 * @example 14240 * // Numeric ordering of formatted numbers with a pre-formatter 14241 * $.extend( $.fn.dataTable.ext.type.order, { 14242 * "string-pre": function(x) { 14243 * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); 14244 * return parseFloat( a ); 14245 * } 14246 * } ); 14247 * 14248 * @example 14249 * // Case-sensitive string ordering, with no pre-formatting method 14250 * $.extend( $.fn.dataTable.ext.order, { 14251 * "string-case-asc": function(x,y) { 14252 * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 14253 * }, 14254 * "string-case-desc": function(x,y) { 14255 * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 14256 * } 14257 * } ); 14258 */ 14259 order: {} 14260 }, 14261 14262 /** 14263 * Unique DataTables instance counter 14264 * 14265 * @type int 14266 * @private 14267 */ 14268 _unique: 0, 14269 14270 14271 // 14272 // Depreciated 14273 // The following properties are retained for backwards compatiblity only. 14274 // The should not be used in new projects and will be removed in a future 14275 // version 14276 // 14277 14278 /** 14279 * Version check function. 14280 * @type function 14281 * @depreciated Since 1.10 14282 */ 14283 fnVersionCheck: DataTable.fnVersionCheck, 14284 14285 14286 /** 14287 * Index for what 'this' index API functions should use 14288 * @type int 14289 * @deprecated Since v1.10 14290 */ 14291 iApiIndex: 0, 14292 14293 14294 /** 14295 * jQuery UI class container 14296 * @type object 14297 * @deprecated Since v1.10 14298 */ 14299 oJUIClasses: {}, 14300 14301 14302 /** 14303 * Software version 14304 * @type string 14305 * @deprecated Since v1.10 14306 */ 14307 sVersion: DataTable.version 14308 }; 14309 14310 14311 // 14312 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts 14313 // 14314 $.extend( _ext, { 14315 afnFiltering: _ext.search, 14316 aTypes: _ext.type.detect, 14317 ofnSearch: _ext.type.search, 14318 oSort: _ext.type.order, 14319 afnSortData: _ext.order, 14320 aoFeatures: _ext.feature, 14321 oApi: _ext.internal, 14322 oStdClasses: _ext.classes, 14323 oPagination: _ext.pager 14324 } ); 14325 14326 14327 $.extend( DataTable.ext.classes, { 14328 "sTable": "dataTable", 14329 "sNoFooter": "no-footer", 14330 14331 /* Paging buttons */ 14332 "sPageButton": "paginate_button", 14333 "sPageButtonActive": "current", 14334 "sPageButtonDisabled": "disabled", 14335 14336 /* Striping classes */ 14337 "sStripeOdd": "odd", 14338 "sStripeEven": "even", 14339 14340 /* Empty row */ 14341 "sRowEmpty": "dataTables_empty", 14342 14343 /* Features */ 14344 "sWrapper": "dataTables_wrapper", 14345 "sFilter": "dataTables_filter", 14346 "sInfo": "dataTables_info", 14347 "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */ 14348 "sLength": "dataTables_length", 14349 "sProcessing": "dataTables_processing", 14350 14351 /* Sorting */ 14352 "sSortAsc": "sorting_asc", 14353 "sSortDesc": "sorting_desc", 14354 "sSortable": "sorting", /* Sortable in both directions */ 14355 "sSortableAsc": "sorting_asc_disabled", 14356 "sSortableDesc": "sorting_desc_disabled", 14357 "sSortableNone": "sorting_disabled", 14358 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ 14359 14360 /* Filtering */ 14361 "sFilterInput": "", 14362 14363 /* Page length */ 14364 "sLengthSelect": "", 14365 14366 /* Scrolling */ 14367 "sScrollWrapper": "dataTables_scroll", 14368 "sScrollHead": "dataTables_scrollHead", 14369 "sScrollHeadInner": "dataTables_scrollHeadInner", 14370 "sScrollBody": "dataTables_scrollBody", 14371 "sScrollFoot": "dataTables_scrollFoot", 14372 "sScrollFootInner": "dataTables_scrollFootInner", 14373 14374 /* Misc */ 14375 "sHeaderTH": "", 14376 "sFooterTH": "", 14377 14378 // Deprecated 14379 "sSortJUIAsc": "", 14380 "sSortJUIDesc": "", 14381 "sSortJUI": "", 14382 "sSortJUIAscAllowed": "", 14383 "sSortJUIDescAllowed": "", 14384 "sSortJUIWrapper": "", 14385 "sSortIcon": "", 14386 "sJUIHeader": "", 14387 "sJUIFooter": "" 14388 } ); 14389 14390 14391 (function() { 14392 14393 // Reused strings for better compression. Closure compiler appears to have a
14394 // weird edge case where it is trying to expand strings rather than use the 14395 // variable version. This results in about 200 bytes being added, for very 14396 // little preference benefit since it this run on script load only. 14397 var _empty = ''; 14398 _empty = ''; 14399 14400 var _stateDefault = _empty + 'ui-state-default'; 14401 var _sortIcon = _empty + 'css_right ui-icon ui-icon-'; 14402 var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix'; 14403 14404 $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, { 14405 /* Full numbers paging buttons */ 14406 "sPageButton": "fg-button ui-button "+_stateDefault, 14407 "sPageButtonActive": "ui-state-disabled", 14408 "sPageButtonDisabled": "ui-state-disabled", 14409 14410 /* Features */ 14411 "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+ 14412 "ui-buttonset-multi paging_", /* Note that the type is postfixed */ 14413 14414 /* Sorting */ 14415 "sSortAsc": _stateDefault+" sorting_asc", 14416 "sSortDesc": _stateDefault+" sorting_desc", 14417 "sSortable": _stateDefault+" sorting", 14418 "sSortableAsc": _stateDefault+" sorting_asc_disabled", 14419 "sSortableDesc": _stateDefault+" sorting_desc_disabled", 14420 "sSortableNone": _stateDefault+" sorting_disabled", 14421 "sSortJUIAsc": _sortIcon+"triangle-1-n", 14422 "sSortJUIDesc": _sortIcon+"triangle-1-s", 14423 "sSortJUI": _sortIcon+"carat-2-n-s", 14424 "sSortJUIAscAllowed": _sortIcon+"carat-1-n", 14425 "sSortJUIDescAllowed": _sortIcon+"carat-1-s", 14426 "sSortJUIWrapper": "DataTables_sort_wrapper", 14427 "sSortIcon": "DataTables_sort_icon", 14428 14429 /* Scrolling */ 14430 "sScrollHead": "dataTables_scrollHead "+_stateDefault, 14431 "sScrollFoot": "dataTables_scrollFoot "+_stateDefault, 14432 14433 /* Misc */ 14434 "sHeaderTH": _stateDefault, 14435 "sFooterTH": _stateDefault, 14436 "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr", 14437 "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br" 14438 } ); 14439 14440 }()); 14441 14442 14443 14444 var extPagination = DataTable.ext.pager; 14445 14446 function _numbers ( page, pages ) { 14447 var 14448 numbers = [], 14449 buttons = extPagination.numbers_length, 14450 half = Math.floor( buttons / 2 ), 14451 i = 1; 14452 14453 if ( pages <= buttons ) { 14454 numbers = _range( 0, pages ); 14455 } 14456 else if ( page <= half ) { 14457 numbers = _range( 0, buttons-2 ); 14458 numbers.push( 'ellipsis' ); 14459 numbers.push( pages-1 ); 14460 } 14461 else if ( page >= pages - 1 - half ) { 14462 numbers = _range( pages-(buttons-2), pages ); 14463 numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6 14464 numbers.splice( 0, 0, 0 ); 14465 } 14466 else { 14467 numbers = _range( page-half+2, page+half-1 ); 14468 numbers.push( 'ellipsis' ); 14469 numbers.push( pages-1 ); 14470 numbers.splice( 0, 0, 'ellipsis' ); 14471 numbers.splice( 0, 0, 0 ); 14472 } 14473 14474 numbers.DT_el = 'span'; 14475 return numbers; 14476 } 14477 14478 14479 $.extend( extPagination, { 14480 simple: function ( page, pages ) { 14481 return [ 'previous', 'next' ]; 14482 }, 14483 14484 full: function ( page, pages ) { 14485 return [ 'first', 'previous', 'next', 'last' ]; 14486 }, 14487 14488 numbers: function ( page, pages ) { 14489 return [ _numbers(page, pages) ]; 14490 }, 14491 14492 simple_numbers: function ( page, pages ) { 14493 return [ 'previous', _numbers(page, pages), 'next' ]; 14494 }, 14495 14496 full_numbers: function ( page, pages ) { 14497 return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ]; 14498 }, 14499 14500 // For testing and plug-ins to use 14501 _numbers: _numbers, 14502 14503 // Number of number buttons (including ellipsis) to show. _Must be odd!_ 14504 numbers_length: 7 14505 } ); 14506 14507 14508 $.extend( true, DataTable.ext.renderer, { 14509 pageButton: { 14510 _: function ( settings, host, idx, buttons, page, pages ) { 14511 var classes = settings.oClasses; 14512 var lang = settings.oLanguage.oPaginate; 14513 var aria = settings.oLanguage.oAria.paginate || {}; 14514 var btnDisplay, btnClass, counter=0; 14515 14516 var attach = function( container, buttons ) { 14517 var i, ien, node, button; 14518 var clickHandler = function ( e ) { 14519 _fnPageChange( settings, e.data.action, true ); 14520 }; 14521 14522 for ( i=0, ien=buttons.length ; i<ien ; i++ ) { 14523 button = buttons[i]; 14524 14525 if ( $.isArray( button ) ) { 14526 var inner = $( '<'+(button.DT_el || 'div')+'/>' ) 14527 .appendTo( container ); 14528 attach( inner, button ); 14529 } 14530 else { 14531 btnDisplay = null; 14532 btnClass = ''; 14533 14534 switch ( button ) { 14535 case 'ellipsis': 14536 container.append('<span class="ellipsis">…
14536</span>'); 14537 break; 14538 14539 case 'first': 14540 btnDisplay = lang.sFirst; 14541 btnClass = button + (page > 0 ? 14542 '' : ' '+classes.sPageButtonDisabled); 14543 break; 14544 14545 case 'previous': 14546 btnDisplay = lang.sPrevious; 14547 btnClass = button + (page > 0 ? 14548 '' : ' '+classes.sPageButtonDisabled); 14549 break; 14550 14551 case 'next': 14552 btnDisplay = lang.sNext; 14553 btnClass = button + (page < pages-1 ? 14554 '' : ' '+classes.sPageButtonDisabled); 14555 break; 14556 14557 case 'last': 14558 btnDisplay = lang.sLast; 14559 btnClass = button + (page < pages-1 ? 14560 '' : ' '+classes.sPageButtonDisabled); 14561 break; 14562 14563 default: 14564 btnDisplay = button + 1; 14565 btnClass = page === button ? 14566 classes.sPageButtonActive : ''; 14567 break; 14568 } 14569 14570 if ( btnDisplay !== null ) { 14571 node = $('<a>', { 14572 'class': classes.sPageButton+' '+btnClass, 14573 'aria-controls': settings.sTableId, 14574 'aria-label': aria[ button ], 14575 'data-dt-idx': counter, 14576 'tabindex': settings.iTabIndex, 14577 'id': idx === 0 && typeof button === 'string' ? 14578 settings.sTableId +'_'+ button : 14579 null 14580 } ) 14581 .html( btnDisplay ) 14582 .appendTo( container ); 14583 14584 _fnBindAction( 14585 node, {action: button}, clickHandler 14586 ); 14587 14588 counter++; 14589 } 14590 } 14591 } 14592 }; 14593 14594 // IE9 throws an 'unknown error' if document.activeElement is used 14595 // inside an iframe or frame. Try / catch the error. Not good for 14596 // accessibility, but neither are frames. 14597 var activeEl; 14598 14599 try { 14600 // Because this approach is destroying and recreating the paging 14601 // elements, focus is lost on the select button which is bad for 14602 // accessibility. So we want to restore focus once the draw has 14603 // completed 14604 activeEl = $(host).find(document.activeElement).data('dt-idx'); 14605 } 14606 catch (e) {} 14607 14608 attach( $(host).empty(), buttons ); 14609 14610 if ( activeEl ) { 14611 $(host).find( '[data-dt-idx='+activeEl+']' ).focus(); 14612 } 14613 } 14614 } 14615 } ); 14616 14617 14618 14619 // Built in type detection. See model.ext.aTypes for information about 14620 // what is required from this methods. 14621 $.extend( DataTable.ext.type.detect, [ 14622 // Plain numbers - first since V8 detects some plain numbers as dates 14623 // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...). 14624 function ( d, settings ) 14625 { 14626 var decimal = settings.oLanguage.sDecimal; 14627 return _isNumber( d, decimal ) ? 'num'+decimal : null; 14628 }, 14629 14630 // Dates (only those recognised by the browser's Date.parse) 14631 function ( d, settings ) 14632 { 14633 // V8 will remove any unknown characters at the start and end of the 14634 // expression, leading to false matches such as `$245.12` or `10%` being 14635 // a valid date. See forum thread 18941 for detail. 14636 if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) { 14637 return null; 14638 } 14639 var parsed = Date.parse(d); 14640 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; 14641 }, 14642 14643 // Formatted numbers 14644 function ( d, settings ) 14645 { 14646 var decimal = settings.oLanguage.sDecimal; 14647 return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null; 14648 }, 14649 14650 // HTML numeric 14651 function ( d, settings ) 14652 { 14653 var decimal = settings.oLanguage.sDecimal; 14654 return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null; 14655 }, 14656 14657 // HTML numeric, formatted 14658 function ( d, settings ) 14659 { 14660 var decimal = settings.oLanguage.sDecimal; 14661 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null; 14662 }, 14663 14664 // HTML (this is strict checking - there must be html) 14665 function ( d, settings ) 14666 { 14667 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? 14668 'html' : null; 14669 } 14670 ] ); 14671 14672 14673 14674 // Filter formatting functions. See model.ext.ofnSearch for information about 14675 // what is required from these methods. 14676 // 14677 // Note that additional search methods are added for the html numbers and
14678 // html formatted numbers by `_addNumericSort()` when we know what the decimal 14679 // place is 14680 14681 14682 $.extend( DataTable.ext.type.search, { 14683 html: function ( data ) { 14684 return _empty(data) ? 14685 data : 14686 typeof data === 'string' ? 14687 data 14688 .replace( _re_new_lines, " " ) 14689 .replace( _re_html, "" ) : 14690 ''; 14691 }, 14692 14693 string: function ( data ) { 14694 return _empty(data) ? 14695 data : 14696 typeof data === 'string' ? 14697 data.replace( _re_new_lines, " " ) : 14698 data; 14699 } 14700 } ); 14701 14702 14703 14704 var __numericReplace = function ( d, decimalPlace, re1, re2 ) { 14705 if ( d !== 0 && (!d || d === '-') ) { 14706 return -Infinity; 14707 } 14708 14709 // If a decimal place other than `.` is used, it needs to be given to the 14710 // function so we can detect it and replace with a `.` which is the only 14711 // decimal place Javascript recognises - it is not locale aware. 14712 if ( decimalPlace ) { 14713 d = _numToDecimal( d, decimalPlace ); 14714 } 14715 14716 if ( d.replace ) { 14717 if ( re1 ) { 14718 d = d.replace( re1, '' ); 14719 } 14720 14721 if ( re2 ) { 14722 d = d.replace( re2, '' ); 14723 } 14724 } 14725 14726 return d * 1; 14727 }; 14728 14729 14730 // Add the numeric 'deformatting' functions for sorting and search. This is done 14731 // in a function to provide an easy ability for the language options to add 14732 // additional methods if a non-period decimal place is used. 14733 function _addNumericSort ( decimalPlace ) { 14734 $.each( 14735 { 14736 // Plain numbers 14737 "num": function ( d ) { 14738 return __numericReplace( d, decimalPlace ); 14739 }, 14740 14741 // Formatted numbers 14742 "num-fmt": function ( d ) { 14743 return __numericReplace( d, decimalPlace, _re_formatted_numeric ); 14744 }, 14745 14746 // HTML numeric 14747 "html-num": function ( d ) { 14748 return __numericReplace( d, decimalPlace, _re_html ); 14749 }, 14750 14751 // HTML numeric, formatted 14752 "html-num-fmt": function ( d ) { 14753 return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric ); 14754 } 14755 }, 14756 function ( key, fn ) { 14757 // Add the ordering method 14758 _ext.type.order[ key+decimalPlace+'-pre' ] = fn; 14759 14760 // For HTML types add a search formatter that will strip the HTML 14761 if ( key.match(/^html\-/) ) { 14762 _ext.type.search[ key+decimalPlace ] = _ext.type.search.html; 14763 } 14764 } 14765 ); 14766 } 14767 14768 14769 // Default sort methods 14770 $.extend( _ext.type.order, { 14771 // Dates 14772 "date-pre": function ( d ) { 14773 return Date.parse( d ) || 0; 14774 }, 14775 14776 // html 14777 "html-pre": function ( a ) { 14778 return _empty(a) ? 14779 '' : 14780 a.replace ? 14781 a.replace( /<.*?>/g, "" ).toLowerCase() : 14782 a+''; 14783 }, 14784 14785 // string 14786 "string-pre": function ( a ) { 14787 // This is a little complex, but faster than always calling toString, 14788 // http://jsperf.com/tostring-v-check 14789 return _empty(a) ? 14790 '' : 14791 typeof a === 'string' ? 14792 a.toLowerCase() : 14793 ! a.toString ? 14794 '' : 14795 a.toString(); 14796 }, 14797 14798 // string-asc and -desc are retained only for compatibility with the old 14799 // sort methods 14800 "string-asc": function ( x, y ) { 14801 return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 14802 }, 14803 14804 "string-desc": function ( x, y ) { 14805 return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 14806 } 14807 } ); 14808 14809 14810 // Numeric sorting types - order doesn't matter here 14811 _addNumericSort( '' ); 14812 14813 14814 $.extend( true, DataTable.ext.renderer, { 14815 header: { 14816 _: function ( settings, cell, column, classes ) { 14817 // No additional mark-up required 14818 // Attach a sort listener to update on sort - note that using the 14819 // `DT` namespace will allow the event to be removed automatically 14820 // on destroy, while the `dt` namespaced event is the one we are 14821 // listening for 14822 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) { 14823 if ( settings !== ctx ) { // need to check this this is the host 14824 return; // table, not a nested one 14825 } 14826 14827 var colIdx = column.idx; 14828 14829 cell 14830 .removeClass( 14831 column.sSortingClass +' '+ 14832 classes.sSortAsc +' '+ 14833 classes.sSortDesc 14834 ) 14835 .addClass( columns[ colIdx ] == 'asc' ? 14836 classes.sSortAsc : columns[ colIdx ] == 'desc' ? 14837 classes.sSortDesc : 14838 column.sSortingClass 14839 ); 14840 } ); 14841 }, 14842 14843 jqueryui: function ( settings, cell, column, classes ) { 14844 $('<div/>') 14845 .addClass( classes.sSortJUIWrapper ) 14846 .append( cell.contents() ) 14847 .append( $('<span/>') 14848 .addClass( classes.sSortIcon+' '+column.sSortingClassJUI ) 14849 ) 14850 .appendTo( cell ); 14851 14852 // Attach a sort listener to update on sort 14853 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) { 14854 if ( settings !== ctx ) { 14855 return; 14856 } 14857 14858 var colIdx = column.idx; 14859 14860 cell 14861 .removeClass( classes.sSortAsc +" "+classes.sSortDesc ) 14862 .addClass( columns[ colIdx ] == 'asc' ? 14863 classes.sSortAsc : columns[ colIdx ] == 'desc' ? 14864 classes.sSortDesc : 14865 column.sSortingClass 14866 ); 14867 14868 cell 14869 .find( 'span.'+classes.sSortIcon ) 14870 .removeClass( 14871 classes.sSortJUIAsc +" "+ 14872 classes.sSortJUIDesc +" "+ 14873 classes.sSortJUI +" "+ 14874 classes.sSortJUIAscAllowed +" "+ 14875 classes.sSortJUIDescAllowed 14876 ) 14877 .addClass( columns[ colIdx ] == 'asc' ? 14878 classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ? 14879 classes.sSortJUIDesc : 14880 column.sSortingClassJUI 14881 ); 14882 } ); 14883 } 14884 } 14885 } ); 14886 14887 /* 14888 * Public helper functions. These aren't used internally by DataTables, or 14889 * called by any of the options passed into DataTables, but they can be used 14890 * externally by developers working with DataTables. They are helper functions 14891 * to make working with DataTables a little bit easier. 14892 */ 14893 14894 var __htmlEscapeEntities = function ( d ) { 14895 return typeof d === 'string' ? 14896 d.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') : 14897 d; 14898 }; 14899 14900 /** 14901 * Helpers for `columns.render`. 14902 * 14903 * The options defined here can be used with the `columns.render` initialisation 14904 * option to provide a display renderer. The following functions are defined: 14905 * 14906 * * `number` - Will format numeric data (defined by `columns.data`) for 14907 * display, retaining the original unformatted data for sorting and filtering. 14908 * It takes 5 parameters: 14909 * * `string` - Thousands grouping separator 14910 * * `string` - Decimal point indicator 14911 * * `integer` - Number of decimal points to show 14912 * * `string` (optional) - Prefix. 14913 * * `string` (optional) - Postfix (/suffix). 14914 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional 14915 * parameters. 14916 * 14917 * @example 14918 * // Column definition using the number renderer 14919 * { 14920 * data: "salary", 14921 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' ) 14922 * } 14923 * 14924 * @namespace 14925 */ 14926 DataTable.render = { 14927 number: function ( thousands, decimal, precision, prefix, postfix ) { 14928 return { 14929 display: function ( d ) { 14930 if ( typeof d !== 'number' && typeof d !== 'string' ) { 14931 return d; 14932 } 14933 14934 var negative = d < 0 ? '-' : ''; 14935 var flo = parseFloat( d ); 14936 14937 // If NaN then there isn't much formatting that we can do - just 14938 // return immediately, escaping any HTML (this was supposed to 14939 // be a number after all) 14940 if ( isNaN( flo ) ) { 14941 return __htmlEscapeEntities( d ); 14942 } 14943 14944 d = Math.abs( flo ); 14945 14946 var intPart = parseInt( d, 10 ); 14947 var floatPart = precision ? 14948 decimal+(d - intPart).toFixed( precision ).substring( 2 ): 14949 ''; 14950 14951 return negative + (prefix||'') + 14952 intPart.toString().replace( 14953 /\B(?=(\d{3})+(?!\d))/g, thousands 14954 ) + 14955 floatPart + 14956 (postfix||''); 14957 } 14958 }; 14959 }, 14960 14961 text: function () { 14962 return { 14963 display: __htmlEscapeEntities 14964 }; 14965 } 14966 }; 14967 14968 14969 /* 14970 * This is really a good bit rubbish this method of exposing the internal methods 14971 * publicly... - To be fixed in 2.0 using methods on the prototype 14972 */ 14973 14974 14975 /** 14976 * Create a wrapper function for exporting an internal functions to an external API. 14977 * @param {string} fn API function name 14978 * @returns {function} wrapped function 14979 * @memberof DataTable#internal 14980 */ 14981 function _fnExternApiFunc (fn) 14982 { 14983 return function() { 14984 var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat( 14985 Array.prototype.slice.call(arguments) 14986 ); 14987 return DataTable.ext.internal[fn].apply( this, args ); 14988 }; 14989 } 14990 14991 14992 /** 14993 * Reference to internal functions for use by plug-in developers. Note that 14994 * these methods are references to internal functions and are considered to be 14995 * private. If you use these methods, be aware that they are liable to change 14996 * between versions. 14997 * @namespace 14998 */ 14999 $.extend( DataTable.ext.internal, { 15000 _fnExternApiFunc: _fnExternApiFunc, 15001 _fnBuildAjax: _fnBuildAjax, 15002 _fnAjaxUpdate: _fnAjaxUpdate, 15003 _fnAjaxParameters: _fnAjaxParameters, 15004 _fnAjaxUpdateDraw: _fnAjaxUpdateDraw, 15005 _fnAjaxDataSrc: _fnAjaxDataSrc, 15006 _fnAddColumn: _fnAddColumn, 15007 _fnColumnOptions: _fnColumnOptions, 15008 _fnAdjustColumnSizing: _fnAdjustColumnSizing, 15009 _fnVisibleToColumnIndex: _fnVisibleToColumnIndex, 15010 _fnColumnIndexToVisible: _fnColumnIndexToVisible, 15011 _fnVisbleColumns: _fnVisbleColumns, 15012 _fnGetColumns: _fnGetColumns, 15013 _fnColumnTypes: _fnColumnTypes, 15014 _fnApplyColumnDefs: _fnApplyColumnDefs, 15015 _fnHungarianMap: _fnHungarianMap, 15016 _fnCamelToHungarian: _fnCamelToHungarian, 15017 _fnLanguageCompat: _fnLanguageCompat, 15018 _fnBrowserDetect: _fnBrowserDetect, 15019 _fnAddData: _fnAddData, 15020 _fnAddTr: _fnAddTr, 15021 _fnNodeToDataIndex: _fnNodeToDataIndex, 15022 _fnNodeToColumnIndex: _fnNodeToColumnIndex, 15023 _fnGetCellData: _fnGetCellData, 15024 _fnSetCellData: _fnSetCellData, 15025 _fnSplitObjNotation: _fnSplitObjNotation, 15026 _fnGetObjectDataFn: _fnGetObjectDataFn, 15027 _fnSetObjectDataFn: _fnSetObjectDataFn, 15028 _fnGetDataMaster: _fnGetDataMaster, 15029 _fnClearTable: _fnClearTable, 15030 _fnDeleteIndex: _fnDeleteIndex, 15031 _fnInvalidate: _fnInvalidate, 15032 _fnGetRowElements: _fnGetRowElements, 15033 _fnCreateTr: _fnCreateTr, 15034 _fnBuildHead: _fnBuildHead, 15035 _fnDrawHead: _fnDrawHead, 15036 _fnDraw: _fnDraw, 15037 _fnReDraw: _fnReDraw, 15038 _fnAddOptionsHtml: _fnAddOptionsHtml, 15039 _fnDetectHeader: _fnDetectHeader, 15040 _fnGetUniqueThs: _fnGetUniqueThs, 15041 _fnFeatureHtmlFilter: _fnFeatureHtmlFilter, 15042 _fnFilterComplete: _fnFilterComplete, 15043 _fnFilterCustom: _fnFilterCustom, 15044 _fnFilterColumn: _fnFilterColumn, 15045 _fnFilter: _fnFilter, 15046 _fnFilterCreateSearch: _fnFilterCreateSearch, 15047 _fnEscapeRegex: _fnEscapeRegex, 15048 _fnFilterData: _fnFilterData, 15049 _fnFeatureHtmlInfo: _fnFeatureHtmlInfo, 15050 _fnUpdateInfo: _fnUpdateInfo, 15051 _fnInfoMacros: _fnInfoMacros, 15052 _fnInitialise: _fnInitialise, 15053 _fnInitComplete: _fnInitComplete, 15054 _fnLengthChange: _fnLengthChange, 15055 _fnFeatureHtmlLength: _fnFeatureHtmlLength, 15056 _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate, 15057 _fnPageChange: _fnPageChange, 15058 _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing, 15059 _fnProcessingDisplay: _fnProcessingDisplay, 15060 _fnFeatureHtmlTable: _fnFeatureHtmlTable, 15061 _fnScrollDraw: _fnScrollDraw, 15062 _fnApplyToChildren: _fnApplyToChildren, 15063 _fnCalculateColumnWidths: _fnCalculateColumnWidths, 15064 _fnThrottle: _fnThrottle, 15065 _fnConvertToWidth: _fnConvertToWidth, 15066 _fnGetWidestNode: _fnGetWidestNode, 15067 _fnGetMaxLenString: _fnGetMaxLenString, 15068 _fnStringToCss: _fnStringToCss, 15069 _fnSortFlatten: _fnSortFlatten, 15070 _fnSort: _fnSort, 15071 _fnSortAria: _fnSortAria, 15072 _fnSortListener: _fnSortListener, 15073 _fnSortAttachListener: _fnSortAttachListener, 15074 _fnSortingClasses: _fnSortingClasses, 15075 _fnSortData: _fnSortData, 15076 _fnSaveState: _fnSaveState, 15077 _fnLoadState: _fnLoadState, 15078 _fnSettingsFromNode: _fnSettingsFromNode, 15079 _fnLog: _fnLog, 15080 _fnMap: _fnMap, 15081 _fnBindAction: _fnBindAction, 15082 _fnCallbackReg: _fnCallbackReg, 15083 _fnCallbackFire: _fnCallbackFire, 15084 _fnLengthOverflow: _fnLengthOverflow, 15085 _fnRenderer: _fnRenderer, 15086 _fnDataSource: _fnDataSource, 15087 _fnRowAttributes: _fnRowAttributes, 15088 _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant 15089 // in 1.10, so this dead-end function is 15090 // added to prevent errors 15091 } ); 15092 15093 15094 // jQuery access 15095 $.fn.dataTable = DataTable; 15096 15097 // Provide access to the host jQuery object (circular reference) 15098 DataTable.$ = $; 15099 15100 // Legacy aliases 15101 $.fn.dataTableSettings = DataTable.settings; 15102 $.fn.dataTableExt = DataTable.ext; 15103 15104 // With a capital `D` we return a DataTables API instance rather than a 15105 // jQuery object 15106 $.fn.DataTable = function ( opts ) { 15107 return $(this).dataTable( opts ).api(); 15108 }; 15109 15110 // All properties that are available to $.fn.dataTable should also be 15111 // available on $.fn.DataTable 15112 $.each( DataTable, function ( prop, val ) { 15113 $.fn.DataTable[ prop ] = val; 15114 } ); 15115 15116 15117 // Information about events fired by DataTables - for documentation. 15118 /** 15119 * Draw event, fired whenever the table is redrawn on the page, at the same 15120 * point as fnDrawCallback. This may be useful for binding events or 15121 * performing calculations when the table is altered at all. 15122 * @name DataTable#draw.dt 15123 * @event 15124 * @param {event} e jQuery event object 15125 * @param {object} o DataTables settings object {@link DataTable.models.oSettings} 15126 */ 15127 15128 /** 15129 * Search event, fired when the searching applied to the table (using the 15130 * built-in global search, or column filters) is altered. 15131 * @name DataTable#search.dt 15132 * @event 15133 * @param {event} e jQuery event object 15134 * @param {object} o DataTables settings object {@link DataTable.models.oSettings} 15135 */ 15136 15137 /** 15138 * Page change event, fired when the paging of the table is altered. 15139 * @name DataTable#page.dt 15140 * @event 15141 * @param {event} e jQuery event object 15142 * @param {object} o DataTables settings object {@link DataTable.models.oSettings} 15143 */ 15144 15145 /** 15146 * Order event, fired when the ordering applied to the table is altered. 15147 * @name DataTable#order.dt 15148 * @event 15149 * @param {event} e jQuery event object 15150 * @param {object} o DataTables settings object {@link DataTable.models.oSettings} 15151 */ 15152 15153 /** 15154 * DataTables initialisation complete event, fired when the table is fully 15155 * drawn, including Ajax data loaded, if Ajax data is required. 15156 * @name DataTable#init.dt 15157 * @event 15158 * @param {event} e jQuery event object 15159 * @param {object} oSettings DataTables settings object 15160 * @param {object} json The JSON object request from the server - only 15161 * present if client-side Ajax sourced data is used</li></ol> 15162 */ 15163 15164 /** 15165 * State save event, fired when the table has changed state a new state save 15166 * is required. This event allows modification of the state saving object 15167 * prior to actually doing the save, including addition or other state 15168 * properties (for plug-ins) or modification of a DataTables core property. 15169 * @name DataTable#stateSaveParams.dt 15170 * @event 15171 * @param {event} e jQuery event object 15172 * @param {object} oSettings DataTables settings object 15173 * @param {object} json The state information to be saved 15174 */ 15175 15176 /** 15177 * State load event, fired when the table is loading state from the stored 15178 * data, but prior to the settings object being modified by the saved state 15179 * - allowing modification of the saved state is required or loading of 15180 * state for a plug-in. 15181 * @name DataTable#stateLoadParams.dt 15182 * @event 15183 * @param {event} e jQuery event object 15184 * @param {object} oSettings DataTables settings object 15185 * @param {object} json The saved state information 15186 */ 15187 15188 /** 15189 * State loaded event, fired when state has been loaded from stored data and 15190 * the settings object has been modified by the loaded data. 15191 * @name DataTable#stateLoaded.dt 15192 * @event 15193 * @param {event} e jQuery event object 15194 * @param {object} oSettings DataTables settings object 15195 * @param {object} json The saved state information 15196 */ 15197 15198 /** 15199 * Processing event, fired when DataTables is doing some kind of processing 15200 * (be it, order, searcg or anything else). It can be used to indicate to 15201 * the end user that there is something happening, or that something has 15202 * finished. 15203 * @name DataTable#processing.dt 15204 * @event 15205 * @param {event} e jQuery event object 15206 * @param {object} oSettings DataTables settings object 15207 * @param {boolean} bShow Flag for if DataTables is doing processing or not 15208 */ 15209 15210 /** 15211 * Ajax (XHR) event, fired whenever an Ajax request is completed from a 15212 * request to made to the server for new data. This event is called before 15213 * DataTables processed the returned data, so it can also be used to pre- 15214 * process the data returned from th
15214e server, if needed. 15215 * 15216 * Note that this trigger is called in `fnServerData`, if you override 15217 * `fnServerData` and which to use this event, you need to trigger it in you 15218 * success function. 15219 * @name DataTable#xhr.dt 15220 * @event 15221 * @param {event} e jQuery event object 15222 * @param {object} o DataTables settings object {@link DataTable.models.oSettings} 15223 * @param {object} json JSON returned from the server 15224 * 15225 * @example 15226 * // Use a custom property returned from the server in another DOM element 15227 * $('#table').dataTable().on('xhr.dt', function (e, settings, json) { 15228 * $('#status').html( json.status ); 15229 * } ); 15230 * 15231 * @example 15232 * // Pre-process the data returned from the server 15233 * $('#table').dataTable().on('xhr.dt', function (e, settings, json) { 15234 * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) { 15235 * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two; 15236 * } 15237 * // Note no return - manipulate the data directly in the JSON object. 15238 * } ); 15239 */ 15240 15241 /** 15242 * Destroy event, fired when the DataTable is destroyed by calling fnDestroy 15243 * or passing the bDestroy:true parameter in the initialisation object. This 15244 * can be used to remove bound events, added DOM nodes, etc. 15245 * @name DataTable#destroy.dt 15246 * @event 15247 * @param {event} e jQuery event object 15248 * @param {object} o DataTables settings object {@link DataTable.models.oSettings} 15249 */ 15250 15251 /** 15252 * Page length change event, fired when number of records to show on each 15253 * page (the length) is changed. 15254 * @name DataTable#length.dt 15255 * @event 15256 * @param {event} e jQuery event object 15257 * @param {object} o DataTables settings object {@link DataTable.models.oSettings} 15258 * @param {integer} len New length 15259 */ 15260 15261 /** 15262 * Column sizing has changed. 15263 * @name DataTable#column-sizing.dt 15264 * @event 15265 * @param {event} e jQuery event object 15266 * @param {object} o DataTables settings object {@link DataTable.models.oSettings} 15267 */ 15268 15269 /** 15270 * Column visibility has changed. 15271 * @name DataTable#column-visibility.dt 15272 * @event 15273 * @param {event} e jQuery event object 15274 * @param {object} o DataTables settings object {@link DataTable.models.oSettings} 15275 * @param {int} column Column index 15276 * @param {bool} vis `false` if column now hidden, or `true` if visible 15277 */ 15278 15279 return $.fn.dataTable; 15280})); 15281 15282 15283/*! AutoFill 2.1.1 15284 * ©2008-2015 SpryMedia Ltd - datatables.net/license 15285 */ 15286 15287/** 15288 * @summary AutoFill 15289 * @description Add Excel like click and drag auto-fill options to DataTables 15290 * @version 2.1.1 15291 * @file dataTables.autoFill.js 15292 * @author SpryMedia Ltd (www.sprymedia.co.uk) 15293 * @contact www.sprymedia.co.uk/contact 15294 * @copyright Copyright 2010-2015 SpryMedia Ltd. 15295 * 15296 * This source file is free software, available under the following license: 15297 * MIT license - http://datatables.net/license/mit 15298 * 15299 * This source file is distributed in the hope that it will be useful, but 15300 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15301 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. 15302 * 15303 * For details please refer to: http://www.datatables.net 15304 */ 15305(function( factory ){ 15306 if ( typeof define === 'function' && define.amd ) { 15307 // AMD 15308 define( ['jquery', 'datatables.net'], function ( $ ) { 15309 return factory( $, window, document ); 15310 } ); 15311 } 15312 else if ( typeof exports === 'object' ) { 15313 // CommonJS 15314 module.exports = function (root, $) { 15315 if ( ! root ) { 15316 root = window; 15317 } 15318 15319 if ( ! $ || ! $.fn.dataTable ) { 15320 $ = require('datatables.net')(root, $).$; 15321 } 15322 15323 return factory( $, root, root.document ); 15324 }; 15325 } 15326 else { 15327 // Browser 15328 factory( jQuery, window, document ); 15329 } 15330}(function( $, window, document, undefined ) { 15331'use strict'; 15332var DataTable = $.fn.dataTable; 15333 15334 15335var _instance = 0; 15336 15337/** 15338 * AutoFill provides Excel like auto-fill features for a DataTable 15339 * 15340 * @class AutoFill 15341 * @constructor 15342 * @param {object} oTD DataTables settings object 15343 * @param {object} oConfig Configuration object for AutoFill 15344 */ 15345var AutoFill = function( dt, opts ) 15346{
15347 if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) { 15348 throw( "Warning: AutoFill requires DataTables 1.10.8 or greater"); 15349 } 15350 15351 // User and defaults configuration object 15352 this.c = $.extend( true, {}, 15353 DataTable.defaults.autoFill, 15354 AutoFill.defaults, 15355 opts 15356 ); 15357 15358 /** 15359 * @namespace Settings object which contains customisable information for AutoFill instance 15360 */ 15361 this.s = { 15362 /** @type {DataTable.Api} DataTables' API instance */ 15363 dt: new DataTable.Api( dt ), 15364 15365 /** @type {String} Unique namespace for events attached to the document */ 15366 namespace: '.autoFill'+(_instance++), 15367 15368 /** @type {Object} Cached dimension information for use in the mouse move event handler */ 15369 scroll: {}, 15370 15371 /** @type {integer} Interval object used for smooth scrolling */ 15372 scrollInterval: null, 15373 15374 handle: { 15375 height: 0, 15376 width: 0 15377 } 15378 }; 15379 15380 15381 /** 15382 * @namespace Common and useful DOM elements for the class instance 15383 */ 15384 this.dom = { 15385 /** @type {jQuery} AutoFill handle */ 15386 handle: $('<div class="dt-autofill-handle"/>'), 15387 15388 /** 15389 * @type {Object} Selected cells outline - Need to use 4 elements, 15390 * otherwise the mouse over if you back into the selected rectangle 15391 * will be over that element, rather than the cells! 15392 */ 15393 select: { 15394 top: $('<div class="dt-autofill-select top"/>'), 15395 right: $('<div class="dt-autofill-select right"/>'), 15396 bottom: $('<div class="dt-autofill-select bottom"/>'), 15397 left: $('<div class="dt-autofill-select left"/>') 15398 }, 15399 15400 /** @type {jQuery} Fill type chooser background */ 15401 background: $('<div class="dt-autofill-background"/>'), 15402 15403 /** @type {jQuery} Fill type chooser */ 15404 list: $('<div class="dt-autofill-list">'+this.s.dt.i18n('autoFill.info', '')+'<ul/></div>'), 15405 15406 /** @type {jQuery} DataTables scrolling container */ 15407 dtScroll: null, 15408 15409 /** @type {jQuery} Offset parent element */ 15410 offsetParent: null 15411 }; 15412 15413 15414 /* Constructor logic */ 15415 this._constructor(); 15416}; 15417 15418 15419 15420$.extend( AutoFill.prototype, { 15421 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 15422 * Constructor 15423 */ 15424 15425 /** 15426 * Initialise the RowReorder instance 15427 * 15428 * @private 15429 */ 15430 _constructor: function () 15431 { 15432 var that = this; 15433 var dt = this.s.dt; 15434 var dtScroll = $('div.dataTables_scrollBody', this.s.dt.table().container()); 15435 15436 if ( dtScroll.length ) { 15437 this.dom.dtScroll = dtScroll; 15438 15439 // Need to scroll container to be the offset parent 15440 if ( dtScroll.css('position') === 'static' ) { 15441 dtScroll.css( 'position', 'relative' ); 15442 } 15443 } 15444 15445 this._focusListener(); 15446 15447 this.dom.handle.on( 'mousedown', function (e) { 15448 that._mousedown( e ); 15449 return false; 15450 } ); 15451 15452 dt.on( 'destroy.autoFill', function () { 15453 dt.off( '.autoFill' ); 15454 $(dt.table().body()).off( that.s.namespace ); 15455 $(document.body).off( that.s.namespace ); 15456 } ); 15457 }, 15458 15459 15460 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 15461 * Private methods 15462 */ 15463 15464 /** 15465 * Display the AutoFill drag handle by appending it to a table cell. This 15466 * is the opposite of the _detach method. 15467 * 15468 * @param {node} node TD/TH cell to insert the handle into 15469 * @private 15470 */ 15471 _attach: function ( node ) 15472 { 15473 var dt = this.s.dt; 15474 var idx = dt.cell( node ).index(); 15475 var handle = this.dom.handle; 15476 var handleDim = this.s.handle; 15477 var dtScroll = $('div.dataTables_scrollBody', this.s.dt.table().container() ); 15478 15479 if ( ! idx || dt.columns( this.c.columns ).indexes().indexOf( idx.column ) === -1 ) { 15480 this._detach(); 15481 return; 15482 } 15483 15484 if ( ! this.dom.offsetParent ) { 15485 this.dom.offsetParent = $(node).offsetParent(); 15486 } 15487 15488 if ( ! handleDim.height || ! handleDim.width ) { 15489 // Append to document so we can get its size. Not expecting it to 15490 // change during the life time of the page 15491 handle.appendTo( 'body' ); 15492 handleDim.height = handle.outerHeight(); 15493 handleDim.width = handle.outerWidth(); 15494 } 15495 15496 var offset = $(node).position(); 15497 15498 this.dom.attachedTo = node; 15499 handle 15500 .css( { 15501 top: offset.top + node.offsetHeight - handleDim.height + dtScroll.scrollTop(), 15502 left: offset.left + node.offsetWidth - handleDim.width + dtScroll.
15502scrollLeft() 15503 } ) 15504 .appendTo( this.dom.offsetParent ); 15505 }, 15506 15507 15508 /** 15509 * Determine can the fill type should be. This can be automatic, or ask the 15510 * end user. 15511 * 15512 * @param {array} cells Information about the selected cells from the key 15513 * up function 15514 * @private 15515 */ 15516 _actionSelector: function ( cells ) 15517 { 15518 var that = this; 15519 var dt = this.s.dt; 15520 var actions = AutoFill.actions; 15521 var available = []; 15522 15523 // "Ask" each plug-in if it wants to handle this data 15524 $.each( actions, function ( key, action ) { 15525 if ( action.available( dt, cells ) ) { 15526 available.push( key ); 15527 } 15528 } ); 15529 15530 if ( available.length === 1 && this.c.alwaysAsk === false ) { 15531 // Only one action available - enact it immediately 15532 var result = actions[ available[0] ].execute( dt, cells ); 15533 this._update( result, cells ); 15534 } 15535 else { 15536 // Multiple actions available - ask the end user what they want to do 15537 var list = this.dom.list.children('ul').empty(); 15538 15539 // Add a cancel option 15540 available.push( 'cancel' ); 15541 15542 $.each( available, function ( i, name ) { 15543 list.append( $('<li/>') 15544 .append( 15545 '<div class="dt-autofill-question">'+ 15546 actions[ name ].option( dt, cells )+ 15547 '<div>' 15548 ) 15549 .append( $('<div class="dt-autofill-button">' ) 15550 .append( $('<button class="'+AutoFill.classes.btn+'">'+dt.i18n('autoFill.button', '>')+'</button>') 15551 .on( 'click', function () { 15552 var result = actions[ name ].execute( 15553 dt, cells, $(this).closest('li') 15554 ); 15555 that._update( result, cells ); 15556 15557 that.dom.background.remove(); 15558 that.dom.list.remove(); 15559 } ) 15560 ) 15561 ) 15562 ); 15563 } ); 15564 15565 this.dom.background.appendTo( 'body' ); 15566 this.dom.list.appendTo( 'body' ); 15567 15568 this.dom.list.css( 'margin-top', this.dom.list.outerHeight()/2 * -1 ); 15569 } 15570 }, 15571 15572 15573 /** 15574 * Remove the AutoFill handle from the document 15575 * 15576 * @private 15577 */ 15578 _detach: function () 15579 { 15580 this.dom.attachedTo = null; 15581 this.dom.handle.detach(); 15582 }, 15583 15584 15585 /** 15586 * Draw the selection outline by calculating the range between the start 15587 * and end cells, then placing the highlighting elements to draw a rectangle 15588 * 15589 * @param {node} target End cell 15590 * @param {object} e Originating event 15591 * @private 15592 */ 15593 _drawSelection: function ( target, e ) 15594 { 15595 // Calculate boundary for start cell to this one 15596 var dt = this.s.dt; 15597 var start = this.s.start; 15598 var startCell = $(this.dom.start); 15599 var endCell = $(target); 15600 var end = { 15601 row: dt.rows( { page: 'current' } ).nodes().indexOf( endCell.parent()[0] ), 15602 column: endCell.index() 15603 }; 15604 15605 // Be sure that is a DataTables controlled cell 15606 if ( ! dt.cell( endCell ).any() ) { 15607 return; 15608 } 15609 15610 // if target is not in the columns available - do nothing 15611 if ( dt.columns( this.c.columns ).indexes().indexOf( end.column ) === -1 ) { 15612 return; 15613 } 15614 15615 this.s.end = end; 15616 15617 var top, bottom, left, right, height, width; 15618 15619 top = start.row < end.row ? startCell : endCell; 15620 bottom = start.row < end.row ? endCell : startCell; 15621 left = start.column < end.column ? startCell : endCell; 15622 right = start.column < end.column ? endCell : startCell; 15623 15624 top = top.position().top; 15625 left = left.position().left; 15626 height = bottom.position().top + bottom.outerHeight() - top; 15627 width = right.position().left + right.outerWidth() - left; 15628 15629 var dtScroll = this.dom.dtScroll; 15630 if ( dtScroll ) { 15631 top += dtScroll.scrollTop(); 15632 left += dtScroll.scrollLeft(); 15633 } 15634 15635 var select = this.dom.select; 15636 select.top.css( { 15637 top: top, 15638 left: left, 15639 width: width 15640 } ); 15641 15642 select.left.css( { 15643 top: top, 15644 left: left, 15645 height: height 15646 } ); 15647 15648 select.bottom.css( { 15649 top: top + height, 15650 left: left, 15651 width: width 15652 } ); 15653 15654 select.right.css( { 15655 top: top, 15656 left: left + width, 15657 height: height 15658 } ); 15659 }, 15660 15661 15662 /** 15663 * Use the Editor API to perform an update based on the new data for the 15664 * cells 15665 * 15666 * @param {array} cells Information about the selected cells from the key 15667 * up function 15668 * @private 15669 */ 15670 _editor: function ( cells ) 15671 { 15672 var dt = this.s.dt; 15673 var editor = this.c.editor; 15674 15675 if ( ! editor ) { 15676 return; 15677 } 15678 15679 // Build the object structure for Editor's multi-row editing 15680 var idValues = {}; 15681 var nodes = []; 15682 var fields = editor.fields(); 15683 15684 for ( var i=0, ien=cells.length ; i<ien ; i++ ) { 15685 for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) { 15686 var cell = cells[i][j]; 15687 15688 // Determine the field name for the cell being edited 15689 var col = dt.settings()[0].aoColumns[ cell.index.column ]; 15690 var fieldName = col.editField; 15691 15692 if ( fieldName === undefined ) { 15693 var dataSrc = col.mData; 15694 15695 // dataSrc is the `field.data` property, but we need to set 15696 // using the field name, so we need to translate from the 15697 // data to the name 15698 for ( var k=0, ken=fields.length ; k<ken ; k++ ) { 15699 var field = editor.field( fields[k] ); 15700 15701 if ( field.dataSrc() === dataSrc ) { 15702 fieldName = field.name(); 15703 break; 15704 } 15705 } 15706 } 15707 15708 if ( ! fieldName ) { 15709 throw 'Could not automatically determine field data. '+ 15710 'Please see https://datatables.net/tn/11'; 15711 } 15712 15713 if ( ! idValues[ fieldName ] ) { 15714 idValues[ fieldName ] = {}; 15715 } 15716 15717 var id = dt.row( cell.index.row ).id(); 15718 idValues[ fieldName ][ id ] = cell.set; 15719 15720 // Keep a list of cells so we can activate the bubble editing 15721 // with them 15722 nodes.push( cell.index ); 15723 } 15724 } 15725 15726 // Perform the edit using bubble editing as it allows us to specify 15727 // the cells to be edited, rather than using full rows 15728 editor
15729 .bubble( nodes, false ) 15730 .multiSet( idValues ) 15731 .submit(); 15732 }, 15733 15734 15735 /** 15736 * Emit an event on the DataTable for listeners 15737 * 15738 * @param {string} name Event name 15739 * @param {array} args Event arguments 15740 * @private 15741 */ 15742 _emitEvent: function ( name, args ) 15743 { 15744 this.s.dt.iterator( 'table', function ( ctx, i ) { 15745 $(ctx.nTable).triggerHandler( name+'.dt', args ); 15746 } ); 15747 }, 15748 15749 15750 /** 15751 * Attach suitable listeners (based on the configuration) that will attach 15752 * and detach the AutoFill handle in the document. 15753 * 15754 * @private 15755 */ 15756 _focusListener: function () 15757 { 15758 var that = this; 15759 var dt = this.s.dt; 15760 var namespace = this.s.namespace; 15761 var focus = this.c.focus !== null ? 15762 this.c.focus : 15763 dt.settings()[0].keytable ? 15764 'focus' : 15765 'hover'; 15766 15767 // All event listeners attached here are removed in the `destroy` 15768 // callback in the constructor 15769 if ( focus === 'focus' ) { 15770 dt 15771 .on( 'key-focus.autoFill', function ( e, dt, cell ) { 15772 that._attach( cell.node() ); 15773 } ) 15774 .on( 'key-blur.autoFill', function ( e, dt, cell ) { 15775 that._detach(); 15776 } ); 15777 } 15778 else if ( focus === 'click' ) { 15779 $(dt.table().body()).on( 'click'+namespace, 'td, th', function (e) { 15780 that._attach( this ); 15781 } ); 15782 15783 $(document.body).on( 'click'+namespace, function (e) { 15784 if ( ! $(e.target).parents().filter( dt.table().body() ).length ) { 15785 that._detach(); 15786 } 15787 } ); 15788 } 15789 else { 15790 $(dt.table().body()) 15791 .on( 'mouseenter'+namespace, 'td, th', function (e) { 15792 that._attach( this ); 15793 } ) 15794 .on( 'mouseleave'+namespace, function (e) { 15795 if ( $(e.relatedTarget).hasClass('dt-autofill-handle') ) { 15796 return; 15797 } 15798 15799 that._detach(); 15800 } ); 15801 } 15802 }, 15803 15804 15805 /** 15806 * Start mouse drag - selects the start cell 15807 * 15808 * @param {object} e Mouse down event 15809 * @private 15810 */ 15811 _mousedown: function ( e ) 15812 { 15813 var that = this; 15814 var dt = this.s.dt; 15815 15816 this.dom.start = this.dom.attachedTo; 15817 this.s.start = { 15818 row: dt.rows( { page: 'current' } ).nodes().indexOf( $(this.dom.start).parent()[0] ), 15819 column: $(this.dom.start).index() 15820 }; 15821 15822 $(document.body) 15823 .on( 'mousemove.autoFill', function (e) { 15824 that._mousemove( e ); 15825 } ) 15826 .on( 'mouseup.autoFill', function (e) { 15827 that._mouseup( e ); 15828 } ); 15829 15830 var select = this.dom.select; 15831 var offsetParent = $(this.s.dt.table().body()).offsetParent(); 15832 select.top.appendTo( offsetParent ); 15833 select.left.appendTo( offsetParent ); 15834 select.right.appendTo( offsetParent ); 15835 select.bottom.appendTo( offsetParent ); 15836 15837 this._drawSelection( this.dom.start, e ); 15838 15839 this.dom.handle.css( 'display', 'none' ); 15840 15841 // Cache scrolling information so mouse move doesn't need to read. 15842 // This assumes that the window and DT scroller will not change size 15843 // during an AutoFill drag, which I think is a fair assumption 15844 var scrollWrapper = this.dom.dtScroll; 15845 this.s.scroll = { 15846 windowHeight: $(window).height(), 15847 windowWidth: $(window).width(), 15848 dtTop: scrollWrapper ? scrollWrapper.offset().top : null, 15849 dtLeft: scrollWrapper ? scrollWrapper.offset().left : null, 15850 dtHeight: scrollWrapper ? scrollWrapper.outerHeight() : null, 15851 dtWidth: scrollWrapper ? scrollWrapper.outerWidth() : null 15852 }; 15853 }, 15854 15855 15856 /** 15857 * Mouse drag - selects the end cell and update the selection display for 15858 * the end user 15859 * 15860 * @param {object} e Mouse move event 15861 * @private 15862 */ 15863 _mousemove: function ( e ) 15864 { 15865 var that = this; 15866 var dt = this.s.dt; 15867 var name = e.target.nodeName.toLowerCase(); 15868 if ( name !== 'td' && name !== 'th' ) { 15869 return; 15870 } 15871 15872 this._drawSelection( e.target, e ); 15873 this._shiftScroll( e ); 15874 }, 15875 15876 15877 /** 15878 * End mouse drag - perform the update actions 15879 * 15880 * @param {object} e Mouse up event 15881 * @private 15882 */ 15883 _mouseup: function ( e ) 15884 { 15885 $(document.body).off( '.autoFill' ); 15886 15887 var dt = this.s.dt; 15888 var select = this.dom.select; 15889 select.top.remove(); 15890 select.left.remove(); 15891 select.right.remove(); 15892 select.bottom.remove(); 15893 15894 this.dom.handle.css( 'display', 'block' ); 15895 15896 // Display complete - now do something useful with the selection! 15897 var start = this.s.start; 15898 var end = this.s.end; 15899 15900 // Haven't selected multiple cells, so nothing to do 15901 if ( start.row === end.row && start.column === end.column ) { 15902 return; 15903 } 15904 15905 // Build a matrix representation of the selected rows 15906 var rows = this._range( start.row, end.row ); 15907 var columns = this._range( start.column, end.column ); 15908 var selected = []; 15909 var dtSettings = dt.settings()[0]; 15910 var dtColumns = dtSettings.aoColumns; 15911 15912 // Can't use Array.prototype.map as IE8 doesn't support it 15913 // Can't use $.map as jQuery flattens 2D arrays
15914 // Need to use a good old fashioned for loop 15915 for ( var rowIdx=0 ; rowIdx<rows.length ; rowIdx++ ) { 15916 selected.push( 15917 $.map( columns, function (column) { 15918 var cell = dt.cell( ':eq('+rows[rowIdx]+')', column+':visible', {page:'current'} ); 15919 var data = cell.data(); 15920 var cellIndex = cell.index(); 15921 var editField = dtColumns[ cellIndex.column ].editField; 15922 15923 if ( editField !== undefined ) { 15924 data = dtSettings.oApi._fnGetObjectDataFn( editField )( dt.row( cellIndex.row ).data() ); 15925 } 15926 15927 return { 15928 cell: cell, 15929 data: data, 15930 label: cell.data(), 15931 index: cellIndex 15932 }; 15933 } ) 15934 ); 15935 } 15936 15937 this._actionSelector( selected ); 15938 15939 // Stop shiftScroll 15940 clearInterval( this.s.scrollInterval ); 15941 this.s.scrollInterval = null; 15942 }, 15943 15944 15945 /** 15946 * Create an array with a range of numbers defined by the start and end 15947 * parameters passed in (inclusive!). 15948 * 15949 * @param {integer} start Start 15950 * @param {integer} end End 15951 * @private 15952 */ 15953 _range: function ( start, end ) 15954 { 15955 var out = []; 15956 var i; 15957 15958 if ( start <= end ) { 15959 for ( i=start ; i<=end ; i++ ) { 15960 out.push( i ); 15961 } 15962 } 15963 else { 15964 for ( i=start ; i>=end ; i-- ) { 15965 out.push( i ); 15966 } 15967 } 15968 15969 return out; 15970 }, 15971 15972 15973 /** 15974 * Move the window and DataTables scrolling during a drag to scroll new 15975 * content into view. This is done by proximity to the edge of the scrolling 15976 * container of the mouse - for example near the top edge of the window 15977 * should scroll up. This is a little complicated as there are two elements 15978 * that can be scrolled - the window and the DataTables scrolling view port 15979 * (if scrollX and / or scrollY is enabled). 15980 * 15981 * @param {object} e Mouse move event object 15982 * @private 15983 */ 15984 _shiftScroll: function ( e ) 15985 { 15986 var that = this; 15987 var dt = this.s.dt; 15988 var scroll = this.s.scroll; 15989 var runInterval = false; 15990 var scrollSpeed = 5; 15991 var buffer = 65; 15992 var 15993 windowY = e.pageY - document.body.scrollTop, 15994 windowX = e.pageX - document.body.scrollLeft, 15995 windowVert, windowHoriz, 15996 dtVert, dtHoriz; 15997 15998 // Window calculations - based on the mouse position in the window, 15999 // regardless of scrolling 16000 if ( windowY < buffer ) { 16001 windowVert = scrollSpeed * -1; 16002 } 16003 else if ( windowY > scroll.windowHeight - buffer ) { 16004 windowVert = scrollSpeed; 16005 } 16006 16007 if ( windowX < buffer ) { 16008 windowHoriz = scrollSpeed * -1; 16009 } 16010 else if ( windowX > scroll.windowWidth - buffer ) { 16011 windowHoriz = scrollSpeed; 16012 } 16013 16014 // DataTables scrolling calculations - based on the table's position in 16015 // the document and the mouse position on the page 16016 if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) { 16017 dtVert = scrollSpeed * -1; 16018 } 16019 else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) { 16020 dtVert = scrollSpeed; 16021 } 16022 16023 if ( scroll.dtLeft !== null && e.pageX < scroll.dtLeft + buffer ) { 16024 dtHoriz = scrollSpeed * -1; 16025 } 16026 else if ( scroll.dtLeft !== null && e.pageX > scroll.dtLeft + scroll.dtWidth - buffer ) { 16027 dtHoriz = scrollSpeed; 16028 } 16029 16030 // This is where it gets interesting. We want to continue scrolling 16031 // without requiring a mouse move, so we need an interval to be 16032 // triggered. The interval should continue until it is no longer needed, 16033 // but it must also use the latest scroll commands (for example consider 16034 // that the mouse might move from scrolling up to scrolling left, all 16035 // with the same interval running. We use the `scroll` object to "pass" 16036 // this information to the interval. Can't use local variables as they 16037 // wouldn't be the ones that are used by an already existing interval! 16038 if ( windowVert || windowHoriz || dtVert || dtHoriz ) { 16039 scroll.windowVert = windowVert; 16040 scroll.windowHoriz = windowHoriz; 16041 scroll.dtVert = dtVert; 16042 scroll.dtHoriz = dtHoriz; 16043 runInterval = true; 16044 } 16045 else if ( this.s.scrollInterval ) { 16046 // Don't need to scroll - remove any existing timer 16047 clearInterval( this.s.scrollInterval ); 16048 this.s.scrollInterval = null; 16049 } 16050 16051 // If we need to run the interval to scroll and there is no existing 16052 // interval (if there is an existing one, it will continue to run) 16053 if ( ! this.s.scrollInterval && runInterval ) { 16054 this.s.scrollInterval = setInterval( function () { 16055 // Don't need to worry about setting scroll <0 or beyond the 16056 // scroll bound as the browser will just reject that. 16057 if ( scroll.windowVert ) { 16058 document.body.scrollTop += scroll.windowVert; 16059 } 16060 if ( scroll.windowHoriz ) { 16061 document.body.scrollLeft += scroll.windowHoriz; 16062 } 16063 16064 // DataTables scrolling 16065 if ( scroll.dtVert || scroll.dtHoriz ) { 16066 var scroller = that.dom.dtScroll[0]; 16067 16068 if ( scroll.dtVert ) { 16069 scroller.scrollTop += scroll.dtVert; 16070 } 16071 if ( scroll.dtHoriz ) { 16072 scroller.scrollLeft += scroll.dtHoriz; 16073 } 16074 } 16075 }, 20 ); 16076 } 16077 }, 16078 16079 16080 /** 16081 * Update the DataTable after the user has selected what they want to do 16082 * 16083 * @param {false|undefined} result Return from the `execute` method - can 16084 * be false internally to do nothing. This is not documented for plug
16084-ins 16085 * and is used only by the cancel option. 16086 * @param {array} cells Information about the selected cells from the key 16087 * up function, argumented with the set values 16088 * @private 16089 */ 16090 _update: function ( result, cells ) 16091 { 16092 // Do nothing on `false` return from an execute function 16093 if ( result === false ) { 16094 return; 16095 } 16096 16097 var dt = this.s.dt; 16098 var cell; 16099 16100 // Potentially allow modifications to the cells matrix 16101 this._emitEvent( 'preAutoFill', [ dt, cells ] ); 16102 16103 this._editor( cells ); 16104 16105 // Automatic updates are not performed if `update` is null and the 16106 // `editor` parameter is passed in - the reason being that Editor will 16107 // update the data once submitted 16108 var update = this.c.update !== null ? 16109 this.c.update : 16110 this.c.editor ? 16111 false : 16112 true; 16113 16114 if ( update ) { 16115 for ( var i=0, ien=cells.length ; i<ien ; i++ ) { 16116 for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) { 16117 cell = cells[i][j]; 16118 16119 cell.cell.data( cell.set ); 16120 } 16121 } 16122 16123 dt.draw(false); 16124 } 16125 16126 this._emitEvent( 'autoFill', [ dt, cells ] ); 16127 } 16128} ); 16129 16130 16131/** 16132 * AutoFill actions. The options here determine how AutoFill will fill the data 16133 * in the table when the user has selected a range of cells. Please see the 16134 * documentation on the DataTables site for full details on how to create plug- 16135 * ins. 16136 * 16137 * @type {Object} 16138 */ 16139AutoFill.actions = { 16140 increment: { 16141 available: function ( dt, cells ) { 16142 return $.isNumeric( cells[0][0].label ); 16143 }, 16144 16145 option: function ( dt, cells ) { 16146 return dt.i18n( 16147 'autoFill.increment', 16148 'Increment / decrement each cell by: <input type="number" value="1">' 16149 ); 16150 }, 16151 16152 execute: function ( dt, cells, node ) { 16153 var value = cells[0][0].data * 1; 16154 var increment = $('input', node).val() * 1; 16155 16156 for ( var i=0, ien=cells.length ; i<ien ; i++ ) { 16157 for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) { 16158 cells[i][j].set = value; 16159 16160 value += increment; 16161 } 16162 } 16163 } 16164 }, 16165 16166 fill: { 16167 available: function ( dt, cells ) { 16168 return true; 16169 }, 16170 16171 option: function ( dt, cells ) { 16172 return dt.i18n('autoFill.fill', 'Fill all cells with <i>'+cells[0][0].label+'</i>' ); 16173 }, 16174 16175 execute: function ( dt, cells, node ) { 16176 var value = cells[0][0].data; 16177 16178 for ( var i=0, ien=cells.length ; i<ien ; i++ ) { 16179 for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) { 16180 cells[i][j].set = value; 16181 } 16182 } 16183 } 16184 }, 16185 16186 fillHorizontal: { 16187 available: function ( dt, cells ) { 16188 return cells.length > 1 && cells[0].length > 1; 16189 }, 16190 16191 option: function ( dt, cells ) { 16192 return dt.i18n('autoFill.fillHorizontal', 'Fill cells horizontally' ); 16193 }, 16194 16195 execute: function ( dt, cells, node ) { 16196 for ( var i=0, ien=cells.length ; i<ien ; i++ ) { 16197 for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) { 16198 cells[i][j].set = cells[i][0].data; 16199 } 16200 } 16201 } 16202 }, 16203 16204 fillVertical: { 16205 available: function ( dt, cells ) { 16206 return cells.length > 1 && cells[0].length > 1; 16207 }, 16208 16209 option: function ( dt, cells ) { 16210 return dt.i18n('autoFill.fillVertical', 'Fill cells vertically' ); 16211 }, 16212 16213 execute: function ( dt, cells, node ) { 16214 for ( var i=0, ien=cells.length ; i<ien ; i++ ) { 16215 for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) { 16216 cells[i][j].set = cells[0][j].data; 16217 } 16218 } 16219 } 16220 }, 16221 16222 // Special type that does not make itself available, but is added 16223 // automatically by AutoFill if a multi-choice list is shown. This allows 16224 // sensible code reuse 16225 cancel: { 16226 available: function () { 16227 return false; 16228 }, 16229 16230 option: function ( dt ) { 16231 return dt.i18n('autoFill.cancel', 'Cancel' ); 16232 }, 16233 16234 execute: function () { 16235 return false; 16236 } 16237 } 16238}; 16239 16240 16241/** 16242 * AutoFill version 16243 * 16244 * @static 16245 * @type String 16246 */ 16247AutoFill.version = '2.1.1'; 16248 16249 16250/** 16251 * AutoFill defaults 16252 * 16253 * @namespace 16254 */ 16255AutoFill.defaults = { 16256 /** @type {Boolean} Ask user what they want to do, even for a single option */ 16257 alwaysAsk: false, 16258 16259 /** @type {string|null} What will trigger a focus */ 16260 focus: null, // focus, click, hover 16261 16262 /** @type {column-selector} Columns to provide auto fill for */ 16263 columns: '', // all 16264 16265 /** @type {boolean|null} Update the cells after a drag */ 16266 update: null, // false is editor given, true otherwise 16267 16268 /** @type {DataTable.Editor} Editor instance for automatic submission */ 16269 editor: null 16270}; 16271 16272 16273/** 16274 * Classes used by AutoFill that are configurable 16275 * 16276 * @namespace 16277 */ 16278AutoFill.classes = { 16279 /** @type {String} Class used by the selection button */ 16280 btn: 'btn' 16281}; 16282 16283 16284// Attach a listener to the document which listens for DataTables initialisation 16285// events so we can automatically initialise 16286$(document).on( 'preInit.dt.autofill', function (e, settings, json) { 16287 if ( e.namespace !== 'dt' ) { 16288 return; 16289 } 16290 16291 var init = settings.oInit.autoFill; 16292 var defaults = DataTable.defaults.autoFill; 16293 16294 if ( init || defaults ) { 16295 var opts = $.extend( {}, init, defaults ); 16296 16297 if ( init !== false ) { 16298 new AutoFill( settings, opts ); 16299 } 16300 } 16301} ); 16302 16303 16304// Alias for access 16305DataTable.AutoFill = AutoFill; 16306DataTable.AutoFill = AutoFill; 16307 16308 16309return AutoFill; 16310})); 16311 16312 16313/*! Buttons for DataTables 1.1.2 16314 * ©2015 SpryMedia Ltd - datatables.net/license 16315 */ 16316 16317(function( factory ){ 16318 if ( typeof define === 'function' && define.amd ) { 16319 // AMD 16320 define( ['jquery', 'datatables.net'], function ( $ ) { 16321 return factory( $, window, document ); 16322 } ); 16323 } 16324 else if ( typeof exports === 'object' ) { 16325 // CommonJS 16326 module.exports = function (root, $) { 16327 if ( ! root ) { 16328 root = window; 16329 } 16330 16331 if ( ! $ || ! $.fn.dataTable ) { 16332 $ = require('datatables.net')(root, $).$; 16333 } 16334 16335 return factory( $, root, root.document ); 16336 }; 16337 } 16338 else { 16339 // Browser 16340 factory( jQuery, window, document ); 16341 } 16342}(function( $, window, document, undefined ) { 16343'use strict'; 16344var DataTable = $.fn.dataTable; 16345 16346 16347// Used for namespacing events added to the document by each instance, so they 16348// can be removed on destroy 16349var _instCounter = 0; 16350 16351// Button namespacing counter for namespacing events on individual buttons 16352var _buttonCounter = 0; 16353 16354var _dtButtons = DataTable.ext.buttons; 16355 16356/** 16357 * [Buttons description] 16358 * @param {[type]} 16359 * @param {[type]} 16360 */ 16361var Buttons = function( dt, config ) 16362{ 16363 // Allow a boolean true for defaults 16364 if ( config === true ) { 16365 config = {}; 16366 } 16367 16368 // For easy configuration of buttons an array can be given 16369 if ( $.isArray( config ) ) { 16370 config = { buttons: config }; 16371 } 16372 16373 this.c = $.extend( true, {}, Buttons.defaults, config ); 16374 16375 // Don't want a deep copy for the buttons 16376 if ( config.buttons ) { 16377 this.c.buttons = config.buttons; 16378 } 16379 16380 this.s = { 16381 dt: new DataTable.Api( dt ), 16382 buttons: [], 16383 subButtons: [], 16384 listenKeys: '', 16385 namespace: 'dtb'+(_instCounter++) 16386 }; 16387 16388 this.dom = { 16389 container: $('<'+this.c.dom.container.tag+'/>') 16390 .addClass( this.c.dom.container.className ) 16391 }; 16392 16393 this._constructor(); 16394}; 16395 16396 16397$.extend( Buttons.prototype, { 16398 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 16399 * Public methods 16400 */ 16401 16402 /** 16403 * Get the action of a button 16404 * @param {int|string} Button index 16405 * @return {function} 16406 *//** 16407 * Set the action of a button 16408 * @param {int|string} Button index 16409 * @param {function} Function to set 16410 * @return {Buttons} Self for chaining 16411 */ 16412 action: function ( idx, action ) 16413 { 16414 var button = this._indexToButton( idx ).conf; 16415 var dt = this.s.dt; 16416 16417 if ( action === undefined ) { 16418 return button.action; 16419 } 16420 16421 button.action = action; 16422 16423 return this; 16424 }, 16425 16426 /** 16427 * Add an active class to the button to make to look active or get current 16428 * active state. 16429 * @param {int|string} Button index 16430 * @param {boolean} [flag] Enable / disable flag 16431 * @return {Buttons} Self for chaining or boolean for getter 16432 */ 16433 active: function ( idx, flag ) { 16434 var button = this._indexToButton( idx ); 16435 var klass = this.c.dom.button.active; 16436 16437 if ( flag === undefined ) { 16438 return button.node.hasClass( klass ); 16439 } 16440 16441 button.node.toggleClass( klass, flag === undefined ? true : flag ); 16442 16443 return this; 16444 }, 16445 16446 /** 16447 * Add a new button 16448 * @param {int|string} Button index for where to insert the button 16449 * @param {object} Button configuration object, base string name or function 16450 * @return {Buttons} Self for chaining 16451 */ 16452 add: function ( idx, config ) 16453 { 16454 if ( typeof idx === 'string' && idx.indexOf('-') !== -1 ) { 16455 var idxs = idx.split('-'); 16456 this.c.buttons[idxs[0]*1].buttons.splice( idxs[1]*1, 0, config ); 16457 } 16458 else { 16459 this.c.buttons.splice( idx*1, 0, config ); 16460 } 16461 16462 this.dom.container.empty(); 16463 this._buildButtons( this.c.buttons ); 16464 16465 return this; 16466 }, 16467 16468 /** 16469 * Get the container node for the buttons 16470 * @return {jQuery} Buttons node 16471 */ 16472 container: function () 16473 { 16474 return this.dom.container; 16475 }, 16476 16477 /** 16478 * Disable a button 16479 * @param {int|string} Button index 16480 * @return {Buttons} Self for chaining 16481 */ 16482 disable: function ( idx ) { 16483 var button = this._indexToButton( idx ); 16484 button.node.addClass( this.c.dom.button.disabled ); 16485 16486 return this; 16487 }, 16488 16489 /** 16490 * Destroy the instance, cleaning up event handlers and removing DOM 16491 * elements 16492 * @return {Buttons} Self for chaining 16493 */ 16494 destroy: function () 16495 { 16496 // Key event listener 16497 $('body').off( 'keyup.'+this.s.namespace ); 16498 16499 // Individual button destroy (so they can remove their own events if 16500 // needed 16501 var buttons = this.s.buttons; 16502 var subButtons = this.s.subButtons; 16503 var i, ien, j, jen; 16504 16505 for ( i=0, ien=buttons.length ; i<ien ; i++ ) { 16506 this.removePrep( i ); 16507 16508 for ( j=0, jen=subButtons[i].length ; j<jen ; j++ ) { 16509 this.removePrep( i+'-'+j ); 16510 } 16511 } 16512 16513 this.removeCommit(); 16514 16515 // Container 16516 this.dom.container.remove(); 16517
16518 // Remove from the settings object collection 16519 var buttonInsts = this.s.dt.settings()[0]; 16520 16521 for ( i=0, ien=buttonInsts.length ; i<ien ; i++ ) { 16522 if ( buttonInsts.inst === this ) { 16523 buttonInsts.splice( i, 1 ); 16524 break; 16525 } 16526 } 16527 16528 return this; 16529 }, 16530 16531 /** 16532 * Enable / disable a button 16533 * @param {int|string} Button index 16534 * @param {boolean} [flag=true] Enable / disable flag 16535 * @return {Buttons} Self for chaining 16536 */ 16537 enable: function ( idx, flag ) 16538 { 16539 if ( flag === false ) { 16540 return this.disable( idx ); 16541 } 16542 16543 var button = this._indexToButton( idx ); 16544 button.node.removeClass( this.c.dom.button.disabled ); 16545 16546 return this; 16547 }, 16548 16549 /** 16550 * Get the instance name for the button set selector 16551 * @return {string} Instance name 16552 */ 16553 name: function () 16554 { 16555 return this.c.name; 16556 }, 16557 16558 /** 16559 * Get a button's node 16560 * @param {int|string} Button index 16561 * @return {jQuery} Button element 16562 */ 16563 node: function ( idx ) 16564 { 16565 var button = this._indexToButton( idx ); 16566 return button.node; 16567 }, 16568 16569 /** 16570 * Tidy up any buttons that have been scheduled for removal. This is 16571 * required so multiple buttons can be removed without upsetting the button 16572 * indexes while removing them. 16573 * @param {int|string} Button index 16574 * @return {Buttons} Self for chaining 16575 */ 16576 removeCommit: function () 16577 { 16578 var buttons = this.s.buttons; 16579 var subButtons = this.s.subButtons; 16580 var i, ien, j; 16581 16582 for ( i=buttons.length-1 ; i>=0 ; i-- ) { 16583 if ( buttons[i] === null ) { 16584 buttons.splice( i, 1 ); 16585 subButtons.splice( i, 1 ); 16586 this.c.buttons.splice( i, 1 ); 16587 } 16588 } 16589 16590 for ( i=0, ien=subButtons.length ; i<ien ; i++ ) { 16591 for ( j=subButtons[i].length-1 ; j>=0 ; j-- ) { 16592 if ( subButtons[i][j] === null ) { 16593 subButtons[i].splice( j, 1 ); 16594 this.c.buttons[i].buttons.splice( j, 1 ); 16595 } 16596 } 16597 } 16598 16599 return this; 16600 }, 16601 16602 /** 16603 * Scheduled a button for removal. This is required so multiple buttons can 16604 * be removed without upsetting the button indexes while removing them. 16605 * @return {Buttons} Self for chaining 16606 */ 16607 removePrep: function ( idx ) 16608 { 16609 var button; 16610 var dt = this.s.dt; 16611 16612 if ( typeof idx === 'number' || idx.indexOf('-') === -1 ) { 16613 // Top level button 16614 button = this.s.buttons[ idx*1 ]; 16615 16616 if ( button.conf.destroy ) { 16617 button.conf.destroy.call( dt.button(idx), dt, button, button.conf ); 16618 } 16619 16620 button.node.remove(); 16621 this._removeKey( button.conf ); 16622 this.s.buttons[ idx*1 ] = null; 16623 } 16624 else { 16625 // Collection button 16626 var idxs = idx.split('-'); 16627 button = this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ]; 16628 16629 if ( button.conf.destroy ) { 16630 button.conf.destroy.call( dt.button(idx), dt, button, button.conf ); 16631 } 16632 16633 button.node.remove(); 16634 this._removeKey( button.conf ); 16635 this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ] = null; 16636 } 16637 16638 return this; 16639 }, 16640 16641 /** 16642 * Get the text for a button 16643 * @param {int|string} Button index 16644 * @return {string} Button text 16645 *//** 16646 * Set the text for a button 16647 * @param {int|string|function} Button index 16648 * @param {string} Text 16649 * @return {Buttons} Self for chaining 16650 */ 16651 text: function ( idx, label ) 16652 { 16653 var button = this._indexToButton( idx ); 16654 var buttonLiner = this.c.dom.collection.buttonLiner; 16655 var linerTag = typeof idx === 'string' && idx.indexOf( '-' ) !== -1 && buttonLiner && buttonLiner.tag ? 16656 buttonLiner.tag : 16657 this.c.dom.buttonLiner.tag; 16658 var dt = this.s.dt; 16659 var text = function ( opt ) { 16660 return typeof opt === 'function' ? 16661 opt( dt, button.node, button.conf ) : 16662 opt; 16663 }; 16664 16665 if ( label === undefined ) { 16666 return text( button.conf.text ); 16667 } 16668 16669 button.conf.text = label; 16670 16671 if ( linerTag ) { 16672 button.node.children( linerTag ).html( text(label) ); 16673 } 16674 else { 16675 button.node.html( text(label) ); 16676 } 16677 16678 return this; 16679 }, 16680 16681 /** 16682 * Calculate button index from a node 16683 * @param {node} Button node (_not_ a jQuery object) 16684 * @return {string} Index. Undefined if not found 16685 */ 16686 toIndex: function ( node ) 16687 { 16688 var i, ien, j, jen; 16689 var buttons = this.s.buttons; 16690 var subButtons = this.s.subButtons; 16691 16692 // Loop the main buttons first 16693 for ( i=0, ien=buttons.length ; i<ien ; i++ ) { 16694 if ( buttons[i].node[0] === node ) { 16695 return i+''; 16696 } 16697 } 16698 16699 // Then the sub-buttons 16700 for ( i=0, ien=subButtons.length ; i<ien ; i++ ) { 16701 for ( j=0, jen=subButtons[i].length ; j<jen ; j++ ) { 16702 if ( subButtons[i][j].node[0] === node ) { 16703 return i+'-'+j; 16704 } 16705 } 16706 } 16707 }, 16708 16709 16710 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 16711 * Constructor 16712 */ 16713 16714 /** 16715 * Buttons constructor 16716 * @private 16717 */ 16718 _constructor: function () 16719 { 16720 var that = this; 16721 var dt = this.s.dt; 16722 var dtSettings = dt.settings()[0]; 16723 16724 if ( ! dtSettings._buttons ) { 16725 dtSettings._buttons = []; 16726 } 16727 16728 dtSettings._buttons.push( { 16729 inst: this, 16730 name: this.c.name 16731 } ); 16732 16733 this._buildButtons( this.c.buttons ); 16734 16735 dt.on( 'destroy', function () { 16736 that.destroy(); 16737 } ); 16738 16739 // Global key event binding to listen for button keys 16740 $('body').on( 'keyup.'+this.s.namespace, function ( e ) { 16741 if ( ! document.activeElement || document.activeElement === document.body ) { 16742 // SUse a string of characters for fast lookup of if we need to 16743 // handle this 16744 var character = String.fromCharCode(e.keyCode).toLowerCase(); 16745 16746 if ( that.s.listenKeys.toLowerCase().indexOf( character ) !== -1 ) { 16747 that._keypress( character, e ); 16748 } 16749 } 16750 } ); 16751 }, 16752 16753 16754 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 16755 * Private methods 16756 */ 16757 16758 /** 16759 * Add a new button to the key press listener 16760 * @param {object} Resolved button configuration object 16761 * @private 16762 */ 16763 _addKey: function ( conf ) 16764 { 16765 if ( conf.key ) { 16766 this.s.listenKeys += $.isPlainObject( conf.key ) ? 16767 conf.key.key : 16768 conf.key; 16769 } 16770 }, 16771 16772 /** 16773 * Create buttons from an array of buttons 16774 * @param {array} Buttons to create 16775 * @param {jQuery} Container node into which the created button should be 16776 * inserted. 16777 * @param {int} Counter for sub-buttons to be stored in a collection 16778 * @private 16779 */ 16780 _buildButtons: function ( buttons, container, collectionCounter ) 16781 { 16782 var dt = this.s.dt; 16783 var buttonCounter = 0; 16784 16785 if ( ! container ) { 16786 container = this.dom.container; 16787 this.s.buttons = []; 16788 this.s.subButtons = []; 16789 } 16790 16791 for ( var i=0, ien=buttons.length ; i<ien ; i++ ) { 16792 var conf = this._resolveExtends( buttons[i] ); 16793 16794 if ( ! conf ) { 16795 continue; 16796 } 16797 16798 // If the configuration is an array, then expand the buttons at this 16799 // point 16800 if ( $.isArray( conf ) ) { 16801 this._buildButtons( conf, container, collectionCounter ); 16802 continue; 16803 } 16804 16805 var button = this._buildButton( 16806 conf, 16807 collectionCounter!==undefined ? true : false 16808 ); 16809 16810 if ( ! button ) { 16811 continue; 16812 } 16813 16814 var buttonNode = button.node; 16815 container.append( button.inserter ); 16816 16817 if ( collectionCounter === undefined ) { 16818 this.s.buttons.push( { 16819 node: buttonNode, 16820 conf: conf, 16821 inserter: button.inserter 16822 } ); 16823 this.s.subButtons.push( [] ); 16824 } 16825 else { 16826 this.s.subButtons[ collectionCounter ].push( { 16827 node: buttonNode, 16828 conf: conf, 16829 inserter: button.inserter 16830 } ); 16831 } 16832 16833 if ( conf.buttons ) { 16834 var collectionDom = this.c.dom.collection;
16835 conf._collection = $('<'+collectionDom.tag+'/>') 16836 .addClass( collectionDom.className ); 16837 16838 this._buildButtons( conf.buttons, conf._collection, buttonCounter ); 16839 } 16840 16841 // init call is made here, rather than buildButton as it needs to 16842 // have been added to the buttons / subButtons array first 16843 if ( conf.init ) { 16844 conf.init.call( dt.button( buttonNode ), dt, buttonNode, conf ); 16845 } 16846 16847 buttonCounter++; 16848 } 16849 }, 16850 16851 /** 16852 * Create an individual button 16853 * @param {object} config Resolved button configuration 16854 * @param {boolean} collectionButton `true` if a collection button 16855 * @return {jQuery} Created button node (jQuery) 16856 * @private 16857 */ 16858 _buildButton: function ( config, collectionButton ) 16859 { 16860 var that = this; 16861 var buttonDom = this.c.dom.button; 16862 var linerDom = this.c.dom.buttonLiner; 16863 var collectionDom = this.c.dom.collection; 16864 var dt = this.s.dt; 16865 var text = function ( opt ) { 16866 return typeof opt === 'function' ? 16867 opt( dt, button, config ) : 16868 opt; 16869 }; 16870 16871 if ( collectionButton && collectionDom.button ) { 16872 buttonDom = collectionDom.button; 16873 } 16874 16875 if ( collectionButton && collectionDom.buttonLiner ) { 16876 linerDom = collectionDom.buttonLiner; 16877 } 16878 16879 // Make sure that the button is available based on whatever requirements 16880 // it has. For example, Flash buttons require Flash 16881 if ( config.available && ! config.available( dt, config ) ) { 16882 return false; 16883 } 16884 16885 var action = function ( e, dt, button, config ) { 16886 config.action.call( dt.button( button ), e, dt, button, config ); 16887 16888 $(dt.table().node()).triggerHandler( 'buttons-action.dt', [ 16889 dt.button( button ), dt, button, config 16890 ] ); 16891 }; 16892 16893 var button = $('<'+buttonDom.tag+'/>') 16894 .addClass( buttonDom.className ) 16895 .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex ) 16896 .attr( 'aria-controls', this.s.dt.table().node().id ) 16897 .on( 'click.dtb', function (e) { 16898 e.preventDefault(); 16899 16900 if ( ! button.hasClass( buttonDom.disabled ) && config.action ) { 16901 action( e, dt, button, config ); 16902 } 16903 16904 button.blur(); 16905 } ) 16906 .on( 'keyup.dtb', function (e) { 16907 if ( e.keyCode === 13 ) { 16908 if ( ! button.hasClass( buttonDom.disabled ) && config.action ) { 16909 action( e, dt, button, config ); 16910 } 16911 } 16912 } ); 16913 16914 if ( linerDom.tag ) { 16915 button.append( 16916 $('<'+linerDom.tag+'/>') 16917 .html( text( config.text ) ) 16918 .addClass( linerDom.className ) 16919 ); 16920 } 16921 else { 16922 button.html( text( config.text ) ); 16923 } 16924 16925 if ( config.enabled === false ) { 16926 button.addClass( buttonDom.disabled ); 16927 } 16928 16929 if ( config.className ) { 16930 button.addClass( config.className ); 16931 } 16932 16933 if ( config.titleAttr ) { 16934 button.attr( 'title', config.titleAttr ); 16935 } 16936 16937 if ( ! config.namespace ) { 16938 config.namespace = '.dt-button-'+(_buttonCounter++); 16939 } 16940 16941 var buttonContainer = this.c.dom.buttonContainer; 16942 var inserter; 16943 if ( buttonContainer && buttonContainer.tag ) { 16944 inserter = $('<'+buttonContainer.tag+'/>') 16945 .addClass( buttonContainer.className ) 16946 .append( button ); 16947 } 16948 else { 16949 inserter = button; 16950 } 16951 16952 this._addKey( config ); 16953 16954 return { 16955 node: button, 16956 inserter: inserter 16957 }; 16958 }, 16959 16960 /** 16961 * Get a button's host information from a button index 16962 * @param {int|string} Button index 16963 * @return {object} Button information - object contains `node` and `conf` 16964 * properties 16965 * @private 16966 */ 16967 _indexToButton: function ( idx ) 16968 { 16969 if ( typeof idx === 'number' || idx.indexOf('-') === -1 ) { 16970 return this.s.buttons[ idx*1 ]; 16971 } 16972 16973 var idxs = idx.split('-'); 16974 return this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ]; 16975 }, 16976 16977 /** 16978 * Handle a key press - determine if any button's key configured matches 16979 * what was typed and trigger the action if so. 16980 * @param {string} The character pressed 16981 * @param {object} Key event that triggered this call 16982 * @private 16983 */ 16984 _keypress: function ( character, e ) 16985 { 16986 var i, ien, j, jen; 16987 var buttons = this.s.buttons; 16988 var subButtons = this.s.subButtons; 16989 var run = function ( conf, node ) { 16990 if ( ! conf.key ) { 16991 return; 16992 } 16993 16994 if ( conf.key === character ) { 16995 node.click(); 16996 } 16997 else if ( $.isPlainObject( conf.key ) ) { 16998 if ( conf.key.key !== character ) { 16999 return; 17000 } 17001
17002 if ( conf.key.shiftKey && ! e.shiftKey ) { 17003 return; 17004 } 17005 17006 if ( conf.key.altKey && ! e.altKey ) { 17007 return; 17008 } 17009 17010 if ( conf.key.ctrlKey && ! e.ctrlKey ) { 17011 return; 17012 } 17013 17014 if ( conf.key.metaKey && ! e.metaKey ) { 17015 return; 17016 } 17017 17018 // Made it this far - it is good 17019 node.click(); 17020 } 17021 }; 17022 17023 // Loop the main buttons first 17024 for ( i=0, ien=buttons.length ; i<ien ; i++ ) { 17025 run( buttons[i].conf, buttons[i].node ); 17026 } 17027 17028 // Then the sub-buttons 17029 for ( i=0, ien=subButtons.length ; i<ien ; i++ ) { 17030 for ( j=0, jen=subButtons[i].length ; j<jen ; j++ ) { 17031 run( subButtons[i][j].conf, subButtons[i][j].node ); 17032 } 17033 } 17034 }, 17035 17036 /** 17037 * Remove a key from the key listener for this instance (to be used when a 17038 * button is removed) 17039 * @param {object} Button configuration 17040 */ 17041 _removeKey: function ( conf ) 17042 { 17043 if ( conf.key ) { 17044 var character = $.isPlainObject( conf.key ) ? 17045 conf.key.key : 17046 conf.key; 17047 17048 // Remove only one character, as multiple buttons could have the 17049 // same listening key 17050 var a = this.s.listenKeys.split(''); 17051 var idx = $.inArray( character, a ); 17052 a.splice( idx, 1 ); 17053 this.s.listenKeys = a.join(''); 17054 } 17055 }, 17056 17057 /** 17058 * Resolve a button configuration 17059 * @param {string|function|object} Button config to resolve 17060 * @return {object} Button configuration 17061 */ 17062 _resolveExtends: function ( conf ) 17063 { 17064 var dt = this.s.dt; 17065 var i, ien; 17066 var toConfObject = function ( base ) { 17067 var loop = 0; 17068 17069 // Loop until we have resolved to a button configuration, or an 17070 // array of button configurations (which will be iterated 17071 // separately) 17072 while ( ! $.isPlainObject(base) && ! $.isArray(base) ) { 17073 if ( base === undefined ) { 17074 return; 17075 } 17076 17077 if ( typeof base === 'function' ) { 17078 base = base( dt, conf ); 17079 17080 if ( ! base ) { 17081 return false; 17082 } 17083 } 17084 else if ( typeof base === 'string' ) { 17085 if ( ! _dtButtons[ base ] ) { 17086 throw 'Unknown button type: '+base; 17087 } 17088 17089 base = _dtButtons[ base ]; 17090 } 17091 17092 loop++; 17093 if ( loop > 30 ) { 17094 // Protect against misconfiguration killing the browser 17095 throw 'Buttons: Too many iterations'; 17096 } 17097 } 17098 17099 return $.isArray( base ) ? 17100 base : 17101 $.extend( {}, base ); 17102 }; 17103 17104 conf = toConfObject( conf ); 17105 17106 while ( conf && conf.extend ) { 17107 // Use `toConfObject` in case the button definition being extended 17108 // is itself a string or a function 17109 if ( ! _dtButtons[ conf.extend ] ) { 17110 throw 'Cannot extend unknown button type: '+conf.extend; 17111 } 17112 17113 var objArray = toConfObject( _dtButtons[ conf.extend ] ); 17114 if ( $.isArray( objArray ) ) { 17115 return objArray; 17116 } 17117 else if ( ! objArray ) { 17118 // This is a little brutal as it might be possible to have a 17119 // valid button without the extend, but if there is no extend 17120 // then the host button would be acting in an undefined state 17121 return false; 17122 } 17123 17124 // Stash the current class name 17125 var originalClassName = objArray.className; 17126 17127 conf = $.extend( {}, objArray, conf ); 17128 17129 // The extend will have overwritten the original class name if the 17130 // `conf` object also assigned a class, but we want to concatenate 17131 // them so they are list that is combined from all extended buttons 17132 if ( originalClassName && conf.className !== originalClassName ) { 17133 conf.className = originalClassName+' '+conf.className; 17134 } 17135 17136 // Buttons to be added to a collection -gives the ability to define 17137 // if buttons should be added to the start or end of a collection 17138 var postfixButtons = conf.postfixButtons; 17139 if ( postfixButtons ) { 17140 if ( ! conf.buttons ) { 17141 conf.buttons = []; 17142 } 17143 17144 for ( i=0, ien=postfixButtons.length ; i<ien ; i++ ) { 17145 conf.buttons.push( postfixButtons[i] ); 17146 } 17147 17148 conf.postfixButtons = null; 17149 } 17150 17151 var prefixButtons = conf.prefixButtons; 17152 if ( prefixButtons ) { 17153 if ( ! conf.buttons ) { 17154 conf.buttons = []; 17155 } 17156 17157 for ( i=0, ien=prefixButtons.length ; i<ien ; i++ ) { 17158 conf.buttons.splice( i, 0, prefixButtons[i] ); 17159 } 17160 17161 conf.prefixButtons = null; 17162 } 17163 17164 // Although we want the `conf` object to overwrite almost all of 17165 // the properties of the object being extended, the `extend` 17166 // property should come from the object being extended 17167 conf.extend = objArray.extend; 17168 } 17169 17170 return conf; 17171 } 17172} ); 17173 17174 17175 17176/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 17177 * Statics 17178 */ 17179 17180/** 17181 * Show / hide a background layer behind a collection 17182 * @param {boolean} Flag to indicate if the background should be shown or 17183 * hidden 17184 * @param {string} Class to assign to the background 17185 * @static 17186 */ 17187Buttons.background = function ( show, className, fade ) { 17188 if ( fade === undefined ) { 17189 fade = 400; 17190 } 17191 17192 if ( show ) { 17193 $('<div/>') 17194 .addClass( className ) 17195 .css( 'display', 'none' ) 17196 .appendTo( 'body' ) 17197 .fadeIn( fade ); 17198 } 17199 else { 17200 $('body > div.'+className) 17201 .fadeOut( fade, function () { 17202 $(this).remove(); 17203 } ); 17204 } 17205}; 17206 17207/** 17208 * Instance selector - select Buttons instances based on an instance selector 17209 * value from the buttons assigned to a DataTable. This is only useful if 17210 * multiple instances are attached to a DataTable. 17211 * @param {string|int|array} Instance selector - see `instance-selector` 17212 * documentation on the DataTables site 17213 * @param {array} Button instance array that was attached to the DataTables 17214 * settings object 17215 * @return {array} Buttons instances 17216 * @static 17217 */ 17218Buttons.instanceSelector = function ( group, buttons ) 17219{ 17220 if ( ! group ) { 17221 return $.map( buttons, function ( v ) { 17222 return v.inst; 17223 } ); 17224 } 17225 17226 var ret = []; 17227 var names = $.map( buttons, function ( v ) { 17228 return v.name; 17229 } ); 17230 17231 // Flatten the group selector into an array of single options 17232 var process = function ( input ) { 17233 if ( $.isArray( input ) ) { 17234 for ( var i=0, ien=input.length ; i<ien ; i++ ) { 17235 process( input[i] ); 17236 } 17237 return; 17238 } 17239 17240 if ( typeof input === 'string' ) { 17241 if ( input.indexOf( ',' ) !== -1 ) { 17242 // String selector, list of names 17243 process( input.split(',') ); 17244 } 17245 else { 17246 // String selector individual name 17247 var idx = $.inArray( $.trim(input), names ); 17248 17249 if ( idx !== -1 ) {
17250 ret.push( buttons[ idx ].inst ); 17251 } 17252 } 17253 } 17254 else if ( typeof input === 'number' ) { 17255 // Index selector 17256 ret.push( buttons[ input ].inst ); 17257 } 17258 }; 17259 17260 process( group ); 17261 17262 return ret; 17263}; 17264 17265/** 17266 * Button selector - select one or more buttons from a selector input so some 17267 * operation can be performed on them. 17268 * @param {array} Button instances array that the selector should operate on 17269 * @param {string|int|node|jQuery|array} Button selector - see 17270 * `button-selector` documentation on the DataTables site 17271 * @return {array} Array of objects containing `inst` and `idx` properties of 17272 * the selected buttons so you know which instance each button belongs to. 17273 * @static 17274 */ 17275Buttons.buttonSelector = function ( insts, selector ) 17276{ 17277 var ret = []; 17278 var run = function ( selector, inst ) { 17279 var i, ien, j, jen; 17280 var buttons = []; 17281 17282 $.each( inst.s.buttons, function (i, v) { 17283 if ( v !== null ) { 17284 buttons.push( { 17285 node: v.node[0], 17286 name: v.conf.name 17287 } ); 17288 } 17289 } ); 17290 17291 $.each( inst.s.subButtons, function (i, v) { 17292 $.each( v, function (j, w) { 17293 if ( w !== null ) { 17294 buttons.push( { 17295 node: w.node[0], 17296 name: w.conf.name 17297 } ); 17298 } 17299 } ); 17300 } ); 17301 17302 var nodes = $.map( buttons, function (v) { 17303 return v.node; 17304 } ); 17305 17306 if ( $.isArray( selector ) || selector instanceof $ ) { 17307 for ( i=0, ien=selector.length ; i<ien ; i++ ) { 17308 run( selector[i], inst ); 17309 } 17310 return; 17311 } 17312 17313 if ( selector === null || selector === undefined || selector === '*' ) { 17314 // Select all 17315 for ( i=0, ien=buttons.length ; i<ien ; i++ ) { 17316 ret.push( { 17317 inst: inst, 17318 idx: inst.toIndex( buttons[i].node ) 17319 } ); 17320 } 17321 } 17322 else if ( typeof selector === 'number' ) { 17323 // Main button index selector 17324 ret.push( { 17325 inst: inst, 17326 idx: selector 17327 } ); 17328 } 17329 else if ( typeof selector === 'string' ) { 17330 if ( selector.indexOf( ',' ) !== -1 ) { 17331 // Split 17332 var a = selector.split(','); 17333 17334 for ( i=0, ien=a.length ; i<ien ; i++ ) { 17335 run( $.trim(a[i]), inst ); 17336 } 17337 } 17338 else if ( selector.match( /^\d+(\-\d+)?$/ ) ) { 17339 // Sub-button index selector 17340 ret.push( { 17341 inst: inst, 17342 idx: selector 17343 } ); 17344 } 17345 else if ( selector.indexOf( ':name' ) !== -1 ) { 17346 // Button name selector 17347 var name = selector.replace( ':name', '' ); 17348 17349 for ( i=0, ien=buttons.length ; i<ien ; i++ ) { 17350 if ( buttons[i].name === name ) { 17351 ret.push( { 17352 inst: inst, 17353 idx: inst.toIndex( buttons[i].node ) 17354 } ); 17355 } 17356 } 17357 } 17358 else { 17359 // jQuery selector on the nodes 17360 $( nodes ).filter( selector ).each( function () { 17361 ret.push( { 17362 inst: inst, 17363 idx: inst.toIndex( this ) 17364 } ); 17365 } ); 17366 } 17367 } 17368 else if ( typeof selector === 'object' && selector.nodeName ) { 17369 // Node selector 17370 var idx = $.inArray( selector, nodes ); 17371 17372 if ( idx !== -1 ) { 17373 ret.push( { 17374 inst: inst, 17375 idx: inst.toIndex( nodes[ idx ] ) 17376 } ); 17377 } 17378 } 17379 }; 17380 17381 17382 for ( var i=0, ien=insts.length ; i<ien ; i++ ) { 17383 var inst = insts[i]; 17384 17385 run( selector, inst ); 17386 } 17387 17388 return ret; 17389}; 17390 17391 17392/** 17393 * Buttons defaults. For full documentation, please refer to the docs/option 17394 * directory or the DataTables site. 17395 * @type {Object} 17396 * @static 17397 */ 17398Buttons.defaults = { 17399 buttons: [ 'copy', 'excel', 'csv', 'pdf', 'print' ], 17400 name: 'main', 17401 tabIndex: 0, 17402 dom: { 17403 container: { 17404 tag: 'div', 17405 className: 'dt-buttons' 17406 }, 17407 collection: { 17408 tag: 'div', 17409 className: 'dt-button-collection' 17410 }, 17411 button: { 17412 tag: 'a', 17413 className: 'dt-button', 17414 active: 'active', 17415 disabled: 'disabled' 17416 }, 17417 buttonLiner: { 17418 tag: 'span', 17419 className: '' 17420 } 17421 } 17422}; 17423 17424/** 17425 * Version information 17426 * @type {string} 17427 * @static 17428 */ 17429Buttons.version = '1.1.2'; 17430 17431 17432$.extend( _dtButtons, { 17433 collection: { 17434 text: function ( dt, button, config ) { 17435 return dt.i18n( 'buttons.collection', 'Collection' ); 17436 }, 17437 className: 'buttons-collection', 17438 action: function ( e, dt, button, config ) { 17439 var background; 17440 var host = button; 17441 var hostOffset = host.offset(); 17442 var tableContainer = $( dt.table().container() ); 17443 var multiLevel = false;
17444 17445 // Remove any old collection 17446 if ( $('div.dt-button-background').length ) { 17447 multiLevel = $('div.dt-button-collection').offset(); 17448 $(document).trigger( 'click.dtb-collection' ); 17449 } 17450 17451 config._collection 17452 .addClass( config.collectionLayout ) 17453 .css( 'display', 'none' ) 17454 .appendTo( 'body' ) 17455 .fadeIn( config.fade ); 17456 17457 var position = config._collection.css( 'position' ); 17458 17459 if ( multiLevel && position === 'absolute' ) { 17460 config._collection.css( { 17461 top: multiLevel.top + 5, // magic numbers for a little offset 17462 left: multiLevel.left + 5 17463 } ); 17464 } 17465 else if ( position === 'absolute' ) { 17466 config._collection.css( { 17467 top: hostOffset.top + host.outerHeight(), 17468 left: hostOffset.left 17469 } ); 17470 17471 var listRight = hostOffset.left + config._collection.outerWidth(); 17472 var tableRight = tableContainer.offset().left + tableContainer.width(); 17473 if ( listRight > tableRight ) { 17474 config._collection.css( 'left', hostOffset.left - ( listRight - tableRight ) ); 17475 } 17476 } 17477 else { 17478 // Fix position - centre on screen 17479 var top = config._collection.height() / 2; 17480 if ( top > $(window).height() / 2 ) { 17481 top = $(window).height() / 2; 17482 } 17483 17484 config._collection.css( 'marginTop', top*-1 ); 17485 } 17486 17487 if ( config.background ) { 17488 Buttons.background( true, config.backgroundClassName, config.fade ); 17489 } 17490 17491 // Need to break the 'thread' for the collection button being 17492 // activated by a click - it would also trigger this event 17493 setTimeout( function () { 17494 // This is bonkers, but if we don't have a click listener on the 17495 // background element, iOS Safari will ignore the body click 17496 // listener below. An empty function here is all that is 17497 // required to make it work... 17498 $('div.dt-button-background').on( 'click.dtb-collection', function () {} ); 17499 17500 $('body').on( 'click.dtb-collection', function (e) { 17501 if ( ! $(e.target).parents().andSelf().filter( config._collection ).length ) { 17502 config._collection 17503 .fadeOut( config.fade, function () { 17504 config._collection.detach(); 17505 } ); 17506 17507 $('div.dt-button-background').off( 'click.dtb-collection' ); 17508 Buttons.background( false, config.backgroundClassName, config.fade ); 17509 17510 $('body').off( 'click.dtb-collection' ); 17511 dt.off( 'buttons-action.b-internal' ); 17512 } 17513 } ); 17514 }, 10 ); 17515 17516 if ( config.autoClose ) { 17517 dt.on( 'buttons-action.b-internal', function () { 17518 $('div.dt-button-background').click(); 17519 } ); 17520 } 17521 }, 17522 background: true, 17523 collectionLayout: '', 17524 backgroundClassName: 'dt-button-background', 17525 autoClose: false, 17526 fade: 400 17527 }, 17528 copy: function ( dt, conf ) { 17529 if ( _dtButtons.copyHtml5 ) { 17530 return 'copyHtml5'; 17531 } 17532 if ( _dtButtons.copyFlash && _dtButtons.copyFlash.available( dt, conf ) ) { 17533 return 'copyFlash'; 17534 } 17535 }, 17536 csv: function ( dt, conf ) { 17537 // Common option that will use the HTML5 or Flash export buttons 17538 if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) { 17539 return 'csvHtml5'; 17540 } 17541 if ( _dtButtons.csvFlash && _dtButtons.csvFlash.available( dt, conf ) ) { 17542 return 'csvFlash'; 17543 } 17544 }, 17545 excel: function ( dt, conf ) { 17546 // Common option that will use the HTML5 or Flash export buttons 17547 if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) { 17548 return 'excelHtml5'; 17549 } 17550 if ( _dtButtons.excelFlash && _dtButtons.excelFlash.available( dt, conf ) ) { 17551 return 'excelFlash'; 17552 } 17553 }, 17554 pdf: function ( dt, conf ) { 17555 // Common option that will use the HTML5 or Flash export buttons 17556 if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) { 17557 return 'pdfHtml5'; 17558 } 17559 if ( _dtButtons.pdfFlash && _dtButtons.pdfFlash.available( dt, conf ) ) { 17560 return 'pdfFlash'; 17561 } 17562 }, 17563 pageLength: function ( dt, conf ) { 17564 var lengthMenu = dt.settings()[0].aLengthMenu; 17565 var vals = $.isArray( lengthMenu[0] ) ? lengthMenu[0] : lengthMenu; 17566 var lang = $.isArray( lengthMenu[0] ) ? lengthMenu[1] : lengthMenu; 17567 var text = function ( dt ) { 17568 return dt.i18n( 'buttons.pageLength', { 17569 "-1": 'Show all rows', 17570 _: 'Show %d rows' 17571 }, dt.page.len() ); 17572 }; 17573 17574 return { 17575 extend: 'collection', 17576 text: text, 17577 className: 'buttons-page-length', 17578 autoClose: true, 17579 buttons: $.map( vals, function ( val, i ) { 17580 return { 17581 text: lang[i], 17582 action: function ( e, dt, button, conf ) { 17583 dt.page.len( val ).draw(); 17584 }, 17585 init: function ( dt, node, conf ) { 17586 var that = this; 17587 var fn = function () { 17588 that.active( dt.page.len() === val ); 17589 }; 17590 17591 dt.on( 'length.dt'+conf.namespace, fn ); 17592 fn(); 17593 }, 17594 destroy: function ( dt, node, conf ) { 17595 dt.off( 'length.dt'+conf.namespace ); 17596 } 17597 }; 17598 } ), 17599 init: function ( dt, node, conf ) { 17600 var that = this; 17601 dt.on( 'length.dt'+conf.namespace, function () { 17602 that.text( text( dt ) ); 17603 } ); 17604 }, 17605 destroy: function ( dt, node, conf ) { 17606 dt.off( 'length.dt'+conf.namespace ); 17607 } 17608 }; 17609 } 17610} ); 17611 17612 17613/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 17614 * DataTables API 17615 * 17616 * For complete documentation, please refer to the docs/api directory or the 17617 * DataTables site 17618 */ 17619 17620// Buttons group and individual button selector 17621DataTable.Api.register( 'buttons()', function ( group, selector ) { 17622 // Argument shifting 17623 if ( selector === undefined ) { 17624 selector = group; 17625 group = undefined; 17626 } 17627 17628 return this.iterator( true, 'table', function ( ctx ) { 17629 if ( ctx._buttons ) { 17630 return Buttons.buttonSelector( 17631 Buttons.instanceSelector( group, ctx._buttons ), 17632 selector 17633 ); 17634 } 17635 }, true ); 17636} ); 17637 17638// Individual button selector 17639DataTable.Api.register( 'button()', function ( group, selector ) { 17640 // just run buttons() and truncate 17641 var buttons = this.buttons( group, selector ); 17642 17643 if ( buttons.length > 1 ) { 17644 buttons.splice( 1, buttons.length ); 17645 } 17646 17647 return buttons; 17648} ); 17649 17650// Active buttons 17651DataTable.Api.registerPlural( 'buttons().active()', 'button().active()', function ( flag ) { 17652 if ( flag === undefined ) { 17653 return this.map( function ( set ) { 17654 return set.inst.active( set.idx ); 17655 } ); 17656 } 17657 17658 return this.each( function ( set ) { 17659 set.inst.active( set.idx, flag ); 17660 } ); 17661} ); 17662 17663// Get / set button action 17664DataTable.Api.registerPlural( 'buttons().action()', 'button().action()', function ( action ) { 17665 if ( action === undefined ) { 17666 return this.map( function ( set ) { 17667 return set.inst.action( set.idx ); 17668 } ); 17669 } 17670 17671 return this.each( function ( set ) { 17672 set.inst.action( set.idx, action ); 17673 } ); 17674} ); 17675 17676// Enable / disable buttons 17677DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) { 17678 return this.each( function ( set ) { 17679 set.inst.enable( set.idx, flag ); 17680 } ); 17681} ); 17682 17683// Disable buttons 17684DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () { 17685 return this.each( function ( set ) { 17686 set.inst.disable( set.idx ); 17687 } ); 17688} ); 17689 17690// Get button nodes 17691DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () { 17692 var jq = $(); 17693 17694 // jQuery will automatically reduce duplicates to a single entry 17695 $( this.each( function ( set ) { 17696 jq = jq.add( set.inst.node( set.idx ) ); 17697 } ) ); 17698 17699 return jq; 17700} ); 17701 17702// Get / set button text (i.e. the button labels) 17703DataTable.Api.registerPlural( 'buttons().text()', 'button().text()', function ( label ) { 17704 if ( label === undefined ) { 17705 return this.map( function ( set ) { 17706 return set.inst.text( set.idx ); 17707 } ); 17708 } 17709 17710 return this.each( function ( set ) { 17711 set.inst.text( set.idx, label ); 17712 } ); 17713} ); 17714 17715// Trigger a button's action 17716DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () { 17717 return this.each( function ( set ) { 17718 set.inst.node( set.idx ).trigger( 'click' ); 17719 } ); 17720} ); 17721 17722// Get the container elements for the button sets selected 17723DataTable.Api.registerPlural( 'buttons().containers()', 'buttons().container()', function () { 17724 var jq = $(); 17725 17726 // jQuery will automatically reduce duplicates to a single entry 17727 $( this.each( function ( set ) { 17728 jq = jq.add( set.inst.container() ); 17729 } ) ); 17730 17731 return jq; 17732} ); 17733 17734// Add a new button 17735DataTable.Api.register( 'button().add()', function ( idx, conf ) { 17736 if ( this.length === 1 ) { 17737 this[0].inst.add( idx, conf ); 17738 } 17739 17740 return this.button( idx ); 17741} ); 17742
17743// Destroy the button sets selected 17744DataTable.Api.register( 'buttons().destroy()', function ( idx ) { 17745 this.pluck( 'inst' ).unique().each( function ( inst ) { 17746 inst.destroy(); 17747 } ); 17748 17749 return this; 17750} ); 17751 17752// Remove a button 17753DataTable.Api.registerPlural( 'buttons().remove()', 'buttons().remove()', function () { 17754 // Need to split into prep and commit so the indexes remain constant during the remove 17755 this.each( function ( set ) { 17756 set.inst.removePrep( set.idx ); 17757 } ); 17758 17759 this.pluck( 'inst' ).unique().each( function ( inst ) { 17760 inst.removeCommit(); 17761 } ); 17762 17763 return this; 17764} ); 17765 17766// Information box that can be used by buttons 17767var _infoTimer; 17768DataTable.Api.register( 'buttons.info()', function ( title, message, time ) { 17769 var that = this; 17770 17771 if ( title === false ) { 17772 $('#datatables_buttons_info').fadeOut( function () { 17773 $(this).remove(); 17774 } ); 17775 clearTimeout( _infoTimer ); 17776 _infoTimer = null; 17777 17778 return this; 17779 } 17780 17781 if ( _infoTimer ) { 17782 clearTimeout( _infoTimer ); 17783 } 17784 17785 if ( $('#datatables_buttons_info').length ) { 17786 $('#datatables_buttons_info').remove(); 17787 } 17788 17789 title = title ? '<h2>'+title+'</h2>' : ''; 17790 17791 $('<div id="datatables_buttons_info" class="dt-button-info"/>') 17792 .html( title ) 17793 .append( $('<div/>')[ typeof message === 'string' ? 'html' : 'append' ]( message ) ) 17794 .css( 'display', 'none' ) 17795 .appendTo( 'body' ) 17796 .fadeIn(); 17797 17798 if ( time !== undefined && time !== 0 ) { 17799 _infoTimer = setTimeout( function () { 17800 that.buttons.info( false ); 17801 }, time ); 17802 } 17803 17804 return this; 17805} ); 17806 17807// Get data from the table for export - this is common to a number of plug-in 17808// buttons so it is included in the Buttons core library 17809DataTable.Api.register( 'buttons.exportData()', function ( options ) { 17810 if ( this.context.length ) { 17811 return _exportData( new DataTable.Api( this.context[0] ), options ); 17812 } 17813} ); 17814 17815 17816var _exportTextarea = $('<textarea/>')[0]; 17817var _exportData = function ( dt, inOpts ) 17818{ 17819 var config = $.extend( true, {}, { 17820 rows: null, 17821 columns: '', 17822 modifier: { 17823 search: 'applied', 17824 order: 'applied' 17825 }, 17826 orthogonal: 'display', 17827 stripHtml: true, 17828 stripNewlines: true, 17829 decodeEntities: true, 17830 trim: true, 17831 format: { 17832 header: function ( d ) { 17833 return strip( d ); 17834 }, 17835 footer: function ( d ) { 17836 return strip( d ); 17837 }, 17838 body: function ( d ) { 17839 return strip( d ); 17840 } 17841 } 17842 }, inOpts ); 17843 17844 var strip = function ( str ) { 17845 if ( typeof str !== 'string' ) { 17846 return str; 17847 } 17848 17849 if ( config.stripHtml ) { 17850 str = str.replace( /<.*?>/g, '' ); 17851 } 17852 17853 if ( config.trim ) { 17854 str = str.replace( /^\s+|\s+$/g, '' ); 17855 } 17856 17857 if ( config.stripNewlines ) { 17858 str = str.replace( /\n/g, ' ' ); 17859 } 17860 17861 if ( config.decodeEntities ) { 17862 _exportTextarea.innerHTML = str; 17863 str = _exportTextarea.value; 17864 } 17865 17866 return str; 17867 }; 17868 17869 17870 var header = dt.columns( config.columns ).indexes().map( function (idx, i) { 17871 return config.format.header( dt.column( idx ).header().innerHTML, idx ); 17872 } ).toArray(); 17873 17874 var footer = dt.table().footer() ? 17875 dt.columns( config.columns ).indexes().map( function (idx, i) { 17876 var el = dt.column( idx ).footer(); 17877 return config.format.footer( el ? el.innerHTML : '', idx ); 17878 } ).toArray() : 17879 null; 17880 17881 var rowIndexes = dt.rows( config.rows, config.modifier ).indexes().toArray(); 17882 var cells = dt 17883 .cells( rowIndexes, config.columns ) 17884 .render( config.orthogonal ) 17885 .toArray(); 17886 var columns = header.length; 17887 var rows = columns > 0 ? cells.length / columns : 0; 17888 var body = new Array( rows ); 17889 var cellCounter = 0; 17890 17891 for ( var i=0, ien=rows ; i<ien ; i++ ) { 17892 var row = new Array( columns ); 17893 17894 for ( var j=0 ; j<columns ; j++ ) { 17895 row[j] = config.format.body( cells[ cellCounter ], j, i ); 17896 cellCounter++; 17897 } 17898 17899 body[i] = row; 17900 } 17901 17902 return { 17903 header: header, 17904 footer: footer, 17905 body: body 17906 }; 17907}; 17908 17909 17910/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 17911 * DataTables interface 17912 */ 17913 17914// Attach to DataTables objects for global access 17915$.fn.dataTable.Buttons = Buttons; 17916$.fn.DataTable.Buttons = Buttons; 17917 17918 17919 17920// DataTables creation - check if the buttons have been defined for this table, 17921// they will have been if the `B` option was used in `dom`, otherwise we should 17922// create the buttons instance here so they can be inserted into the document 17923// using the API. Listen for `init` for compatibility with pre 1.10.10, but to 17924// be removed in future. 17925$(document).on( 'init.dt plugin-init.dt', function (e, settings, json) { 17926 if ( e.namespace !== 'dt' ) { 17927 return; 17928 } 17929 17930 var opts = settings.oInit.buttons || DataTable.defaults.buttons; 17931 17932 if ( opts && ! settings._buttons ) { 17933 new Buttons( settings, opts ).container(); 17934 } 17935} ); 17936 17937// DataTables `dom` feature option 17938DataTable.ext.feature.push( { 17939 fnInit: function( settings ) { 17940 var api = new DataTable.Api( settings ); 17941 var opts = api.init().buttons || DataTable.defaults.buttons; 17942 17943 return new Buttons( api, opts ).container(); 17944 }, 17945 cFeature: "B" 17946} ); 17947 17948 17949return Buttons; 17950})); 17951 17952 17953/*! 17954 * HTML5 export buttons for Buttons and DataTables. 17955 * 2015 SpryMedia Ltd - datatables.net/license 17956 * 17957 * FileSaver.js (2015-05-07.2) - MIT license 17958 * Copyright © 2015 Eli Grey - http://eligrey.com 17959 */ 17960 17961(function( factory ){ 17962 if ( typeof define === 'function' && define.amd ) { 17963 // AMD 17964 define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) { 17965 return factory( $, window, document ); 17966 } ); 17967 } 17968 else if ( typeof exports === 'object' ) { 17969 // CommonJS 17970 module.exports = function (root, $) { 17971 if ( ! root ) { 17972 root = window; 17973 } 17974 17975 if ( ! $ || ! $.fn.dataTable ) { 17976 $ = require('datatables.net')(root, $).$; 17977 } 17978 17979 if ( ! $.fn.dataTable.Buttons ) { 17980 require('datatables.net-buttons')(root, $); 17981 } 17982 17983 return factory( $, root, root.document ); 17984 }; 17985 } 17986 else { 17987 // Browser 17988 factory( jQuery, window, document ); 17989 } 17990}(function( $, window, document, undefined ) { 17991'use strict'; 17992var DataTable = $.fn.dataTable; 17993 17994 17995/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 17996 * FileSaver.js dependency 17997 */ 17998 17999/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 18000 18001var _saveAs = (function(view) { 18002 // IE <10 is explicitly unsupported 18003 if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { 18004 return; 18005 } 18006 var 18007 doc = view.document 18008 // only get URL when necessary in case Blob.js hasn't overridden it yet 18009 , get_URL = function() { 18010 return view.URL || view.webkitURL || view; 18011 } 18012 , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 18013 , can_use_save_link = "download" in save_link 18014 , click = function(node) { 18015 var event = doc.createEvent("MouseEvents"); 18016 event.initMouseEvent( 18017 "click", true, false, view, 0, 0, 0, 0, 0 18018 , false, false, false, false, 0, null 18019 ); 18020 node.dispatchEvent(event); 18021 } 18022 , webkit_req_fs = view.webkitRequestFileSystem 18023 , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem 18024 , throw_outside = function(ex) { 18025 (view.setImmediate || view.setTimeout)(function() { 18026 throw ex; 18027 }, 0); 18028 } 18029 , force_saveable_type = "application/octet-stream" 18030 , fs_min_size = 0 18031 // See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and 18032 // https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047 18033 // for the reasoning behind the timeout and revocation flow 18034 , arbitrary_revoke_timeout = 500 // in ms 18035 , revoke = function(file) { 18036 var revoker = function() { 18037 if (typeof file === "string") { // file is an object URL 18038 get_URL().revokeObjectURL(file); 18039 } else { // file is a File 18040 file.remove(); 18041 } 18042 }; 18043 if (view.chrome) { 18044 revoker(); 18045 } else { 18046 setTimeout(revoker, arbitrary_revoke_timeout); 18047 } 18048 } 18049 , dispatch = function(filesaver, event_types, event) { 18050 event_types = [].concat(event_types); 18051 var i = event_types.length; 18052 while (i--) { 18053 var listener = filesaver["on" + event_types[i]]; 18054 if (typeof listener === "function") { 18055 try { 18056 listener.call(filesaver, event || filesaver); 18057 } catch (ex) { 18058 throw_outside(ex); 18059 } 18060 } 18061 } 18062 } 18063 , auto_bom = function(blob) { 18064 // prepend BOM for UTF-8 XML and text/* types (including HTML) 18065 if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 18066 return new Blob(["\ufeff", blob], {type: blob.type}); 18067 } 18068 return blob; 18069 } 18070 , FileSaver = function(blob, name) { 18071 blob = auto_bom(blob); 18072 // First try a.download, then web filesystem, then object URLs 18073 var 18074 filesaver = this 18075 , type = blob.type 18076 , blob_changed = false 18077 , object_url 18078 , target_view 18079 , dispatch_all = function() { 18080 dispatch(filesaver, "writestart progress write writeend".split(" ")); 18081 } 18082 // on any filesys errors revert to saving with object URLs 18083 , fs_error = function() { 18084 // don't create more object URLs than needed 18085 if (blob_changed || !object_url) { 18086 object_url = get_URL().createObjectURL(blob); 18087 } 18088 if (target_view) { 18089 target_view.location.href = object_url; 18090 } else { 18091 var new_tab = view.open(object_url, "_blank"); 18092 if (new_tab === undefined && typeof safari !== "undefined") { 18093 //Apple do not allow window.open, see http://bit.ly/1kZffRI 18094 view.location.href = object_url; 18095 } 18096 } 18097 filesaver.readyState = filesaver.DONE; 18098 dispatch_all(); 18099 revoke(object_url); 18100 } 18101 , abortable = function(func) { 18102 return function() { 18103 if (filesaver.readyState !== filesaver.DONE) { 18104 return func.apply(this, arguments); 18105 } 18106 }; 18107 } 18108 , create_if_not_found = {create: true, exclusive: false} 18109 , slice 18110 ; 18111 filesaver.readyState = filesaver.INIT; 18112 if (!name) { 18113 name = "download"; 18114 } 18115 if (can_use_save_link) { 18116 object_url = get_URL().createObjectURL(blob); 18117 save_link.href = object_url;
18118 save_link.download = name; 18119 click(save_link); 18120 filesaver.readyState = filesaver.DONE; 18121 dispatch_all(); 18122 revoke(object_url); 18123 return; 18124 } 18125 // Object and web filesystem URLs have a problem saving in Google Chrome when 18126 // viewed in a tab, so I force save with application/octet-stream 18127 // http://code.google.com/p/chromium/issues/detail?id=91158 18128 // Update: Google errantly closed 91158, I submitted it again: 18129 // https://code.google.com/p/chromium/issues/detail?id=389642 18130 if (view.chrome && type && type !== force_saveable_type) { 18131 slice = blob.slice || blob.webkitSlice; 18132 blob = slice.call(blob, 0, blob.size, force_saveable_type); 18133 blob_changed = true; 18134 } 18135 // Since I can't be sure that the guessed media type will trigger a download 18136 // in WebKit, I append .download to the filename. 18137 // https://bugs.webkit.org/show_bug.cgi?id=65440 18138 if (webkit_req_fs && name !== "download") { 18139 name += ".download"; 18140 } 18141 if (type === force_saveable_type || webkit_req_fs) { 18142 target_view = view; 18143 } 18144 if (!req_fs) { 18145 fs_error(); 18146 return; 18147 } 18148 fs_min_size += blob.size; 18149 req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { 18150 fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { 18151 var save = function() { 18152 dir.getFile(name, create_if_not_found, abortable(function(file) { 18153 file.createWriter(abortable(function(writer) { 18154 writer.onwriteend = function(event) { 18155 target_view.location.href = file.toURL(); 18156 filesaver.readyState = filesaver.DONE; 18157 dispatch(filesaver, "writeend", event); 18158 revoke(file); 18159 }; 18160 writer.onerror = function() { 18161 var error = writer.error; 18162 if (error.code !== error.ABORT_ERR) { 18163 fs_error(); 18164 } 18165 }; 18166 "writestart progress write abort".split(" ").forEach(function(event) { 18167 writer["on" + event] = filesaver["on" + event]; 18168 }); 18169 writer.write(blob); 18170 filesaver.abort = function() { 18171 writer.abort(); 18172 filesaver.readyState = filesaver.DONE; 18173 }; 18174 filesaver.readyState = filesaver.WRITING; 18175 }), fs_error); 18176 }), fs_error); 18177 }; 18178 dir.getFile(name, {create: false}, abortable(function(file) { 18179 // delete file if it already exists 18180 file.remove(); 18181 save(); 18182 }), abortable(function(ex) { 18183 if (ex.code === ex.NOT_FOUND_ERR) { 18184 save(); 18185 } else { 18186 fs_error(); 18187 } 18188 })); 18189 }), fs_error); 18190 }), fs_error); 18191 } 18192 , FS_proto = FileSaver.prototype 18193 , saveAs = function(blob, name) { 18194 return new FileSaver(blob, name); 18195 } 18196 ; 18197 // IE 10+ (native saveAs) 18198 if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { 18199 return function(blob, name) { 18200 return navigator.msSaveOrOpenBlob(auto_bom(blob), name); 18201 }; 18202 } 18203 18204 FS_proto.abort = function() { 18205 var filesaver = this; 18206 filesaver.readyState = filesaver.DONE; 18207 dispatch(filesaver, "abort"); 18208 }; 18209 FS_proto.readyState = FS_proto.INIT = 0; 18210 FS_proto.WRITING = 1; 18211 FS_proto.DONE = 2; 18212 18213 FS_proto.error = 18214 FS_proto.onwritestart = 18215 FS_proto.onprogress = 18216 FS_proto.onwrite = 18217 FS_proto.onabort = 18218 FS_proto.onerror = 18219 FS_proto.onwriteend = 18220 null; 18221 18222 return saveAs; 18223}(window)); 18224 18225 18226 18227/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 18228 * Local (private) functions 18229 */ 18230 18231/** 18232 * Get the file name for an exported file. 18233 * 18234 * @param {object} config Button configuration 18235 * @param {boolean} incExtension Include the file name extension 18236 */ 18237var _filename = function ( config, incExtension ) 18238{ 18239 // Backwards compatibility 18240 var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined ? 18241 config.title : 18242 config.filename; 18243 18244 if ( typeof filename === 'function' ) { 18245 filename = filename(); 18246 } 18247 18248 if ( filename.indexOf( '*' ) !== -1 ) { 18249 filename = filename.replace( '*', $('title').text() ); 18250 } 18251 18252 // Strip characters which the OS will object to 18253 filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, ""); 18254 18255 return incExtension === undefined || incExtension === true ? 18256 filename+config.extension : 18257 filename; 18258}; 18259 18260/** 18261 * Get the sheet name for Excel exports. 18262 * 18263 * @param {object} config Button configuration 18264 */ 18265var _sheetname = function ( config ) 18266{ 18267 var sheetName = 'Sheet1'; 18268 18269 if ( config.sheetName ) { 18270 sheetName = config.sheetName.replace(/[\[\]\*\/\\\?\:]/g, ''); 18271 } 18272 18273 return sheetName; 18274}; 18275 18276/** 18277 * Get the title for an exported file. 18278 * 18279 * @param {object} config Button configuration 18280 */ 18281var _title = function ( config ) 18282{ 18283 var title = config.title; 18284 18285 if ( typeof title === 'function' ) { 18286 title = title(); 18287 } 18288 18289 return title.indexOf( '*' ) !== -1 ? 18290 title.replace( '*', $('title').text() ) : 18291 title; 18292}; 18293 18294/** 18295 * Get the newline character(s) 18296 * 18297 * @param {object} config Button configuration 18298 * @return {string} Newline character 18299 */ 18300var _newLine = function ( config ) 18301{ 18302 return config.newline ? 18303 config.newline : 18304 navigator.userAgent.match(/Windows/) ? 18305 '\r\n' : 18306 '\n'; 18307}; 18308 18309/** 18310 * Combine the data from the `buttons.exportData` method into a string that 18311 * will be used in the export file. 18312 * 18313 * @param {DataTable.Api} dt DataTables API instance 18314 * @param {object} config Button configuration 18315 * @return {object} The data to export 18316 */ 18317var _exportData = function ( dt, config ) 18318{ 18319 var newLine = _newLine( config ); 18320 var data = dt.buttons.exportData( config.exportOptions ); 18321 var boundary = config.fieldBoundary; 18322 var separator = config.fieldSeparator;
18323 var reBoundary = new RegExp( boundary, 'g' ); 18324 var escapeChar = config.escapeChar !== undefined ? 18325 config.escapeChar : 18326 '\\'; 18327 var join = function ( a ) { 18328 var s = ''; 18329 18330 // If there is a field boundary, then we might need to escape it in 18331 // the source data 18332 for ( var i=0, ien=a.length ; i<ien ; i++ ) { 18333 if ( i > 0 ) { 18334 s += separator; 18335 } 18336 18337 s += boundary ? 18338 boundary + ('' + a[i]).replace( reBoundary, escapeChar+boundary ) + boundary : 18339 a[i]; 18340 } 18341 18342 return s; 18343 }; 18344 18345 var header = config.header ? join( data.header )+newLine : ''; 18346 var footer = config.footer && data.footer ? newLine+join( data.footer ) : ''; 18347 var body = []; 18348 18349 for ( var i=0, ien=data.body.length ; i<ien ; i++ ) { 18350 body.push( join( data.body[i] ) ); 18351 } 18352 18353 return { 18354 str: header + body.join( newLine ) + footer, 18355 rows: body.length 18356 }; 18357}; 18358 18359/** 18360 * Safari's data: support for creating and downloading files is really poor, so 18361 * various options need to be disabled in it. See 18362 * https://bugs.webkit.org/show_bug.cgi?id=102914 18363 * 18364 * @return {Boolean} `true` if Safari 18365 */ 18366var _isSafari = function () 18367{ 18368 return navigator.userAgent.indexOf('Safari') !== -1 && 18369 navigator.userAgent.indexOf('Chrome') === -1 && 18370 navigator.userAgent.indexOf('Opera') === -1; 18371}; 18372 18373 18374// Excel - Pre-defined strings to build a minimal XLSX file 18375var excelStrings = { 18376 "_rels/.rels": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\ 18377<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\ 18378 <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>\ 18379</Relationships>', 18380 18381 "xl/_rels/workbook.xml.rels": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\ 18382<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\ 18383 <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>\ 18384</Relationships>', 18385 18386 "[Content_Types].xml": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\ 18387<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">\ 18388 <Default Extension="xml" ContentType="application/xml"/>\ 18389 <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>\ 18390 <Default Extension="jpeg" ContentType="image/jpeg"/>\ 18391 <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>\ 18392 <Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>\ 18393</Types>', 18394 18395 "xl/workbook.xml": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\ 18396<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">\ 18397 <fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>\ 18398 <workbookPr showInkAnnotation="0" autoCompressPictures="0"/>\ 18399 <bookViews>\ 18400 <workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>\ 18401 </bookViews>\ 18402 <sheets>\ 18403 <sheet name="__SHEET_NAME__" sheetId="1" r:id="rId1"/>\ 18404 </sheets>\ 18405</workbook>', 18406 18407 "xl/worksheets/sheet1.xml": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\ 18408<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">\ 18409 <sheetData>\ 18410 __DATA__\ 18411 </sheetData>\ 18412</worksheet>' 18413}; 18414 18415 18416 18417/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 18418 * Buttons 18419 */ 18420 18421// 18422// Copy to clipboard 18423// 18424DataTable.ext.buttons.copyHtml5 = { 18425 className: 'buttons-copy buttons-html5', 18426 18427 text: function ( dt ) { 18428 return dt.i18n( 'buttons.copy', 'Copy' ); 18429 }, 18430 18431 action: function ( e, dt, button, config ) { 18432 var exportData = _exportData( dt, config ); 18433 var output = exportData.str;
18434 var hiddenDiv = $('<div/>') 18435 .css( { 18436 height: 1, 18437 width: 1, 18438 overflow: 'hidden', 18439 position: 'fixed', 18440 top: 0, 18441 left: 0 18442 } ); 18443 18444 if ( config.customize ) { 18445 output = config.customize( output, config ); 18446 } 18447 18448 var textarea = $('<textarea readonly/>') 18449 .val( output ) 18450 .appendTo( hiddenDiv ); 18451 18452 // For browsers that support the copy execCommand, try to use it 18453 if ( document.queryCommandSupported('copy') ) { 18454 hiddenDiv.appendTo( dt.table().container() ); 18455 textarea[0].focus(); 18456 textarea[0].select(); 18457 18458 try { 18459 document.execCommand( 'copy' ); 18460 hiddenDiv.remove(); 18461 18462 dt.buttons.info( 18463 dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ), 18464 dt.i18n( 'buttons.copySuccess', { 18465 1: "Copied one row to clipboard", 18466 _: "Copied %d rows to clipboard" 18467 }, exportData.rows ), 18468 2000 18469 ); 18470 18471 return; 18472 } 18473 catch (t) {} 18474 } 18475 18476 // Otherwise we show the text box and instruct the user to use it 18477 var message = $('<span>'+dt.i18n( 'buttons.copyKeys', 18478 'Press <i>ctrl</i> or <i>\u2318</i> + <i>C</i> to copy the table data<br>to your system clipboard.<br><br>'+ 18479 'To cancel, click this message or press escape.' )+'</span>' 18480 ) 18481 .append( hiddenDiv ); 18482 18483 dt.buttons.info( dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ), message, 0 ); 18484 18485 // Select the text so when the user activates their system clipboard 18486 // it will copy that text 18487 textarea[0].focus(); 18488 textarea[0].select(); 18489 18490 // Event to hide the message when the user is done 18491 var container = $(message).closest('.dt-button-info'); 18492 var close = function () { 18493 container.off( 'click.buttons-copy' ); 18494 $(document).off( '.buttons-copy' ); 18495 dt.buttons.info( false ); 18496 }; 18497 18498 container.on( 'click.buttons-copy', close ); 18499 $(document) 18500 .on( 'keydown.buttons-copy', function (e) { 18501 if ( e.keyCode === 27 ) { // esc 18502 close(); 18503 } 18504 } ) 18505 .on( 'copy.buttons-copy cut.buttons-copy', function () { 18506 close(); 18507 } ); 18508 }, 18509 18510 exportOptions: {}, 18511 18512 fieldSeparator: '\t', 18513 18514 fieldBoundary: '', 18515 18516 header: true, 18517 18518 footer: false 18519}; 18520 18521// 18522// CSV export 18523// 18524DataTable.ext.buttons.csvHtml5 = { 18525 className: 'buttons-csv buttons-html5', 18526 18527 available: function () { 18528 return window.FileReader !== undefined && window.Blob; 18529 }, 18530 18531 text: function ( dt ) { 18532 return dt.i18n( 'buttons.csv', 'CSV' ); 18533 }, 18534 18535 action: function ( e, dt, button, config ) { 18536 // Set the text 18537 var newLine = _newLine( config ); 18538 var output = _exportData( dt, config ).str; 18539 var charset = config.charset; 18540 18541 if ( config.customize ) { 18542 output = config.customize( output, config ); 18543 } 18544 18545 if ( charset !== false ) { 18546 if ( ! charset ) { 18547 charset = document.characterSet || document.charset; 18548 } 18549 18550 if ( charset ) { 18551 charset = ';charset='+charset; 18552 } 18553 } 18554 else { 18555 charset = ''; 18556 } 18557 18558 _saveAs( 18559 new Blob( [output], {type: 'text/csv'+charset} ), 18560 _filename( config ) 18561 ); 18562 }, 18563 18564 filename: '*', 18565 18566 extension: '.csv', 18567 18568 exportOptions: {}, 18569 18570 fieldSeparator: ',', 18571 18572 fieldBoundary: '"', 18573 18574 escapeChar: '"', 18575 18576 charset: null, 18577 18578 header: true, 18579 18580 footer: false 18581}; 18582 18583// 18584// Excel (xlsx) export 18585// 18586DataTable.ext.buttons.excelHtml5 = { 18587 className: 'buttons-excel buttons-html5', 18588 18589 available: function () { 18590 return window.FileReader !== undefined && window.JSZip !== undefined && ! _isSafari(); 18591 }, 18592 18593 text: function ( dt ) { 18594 return dt.i18n( 'buttons.excel', 'Excel' ); 18595 }, 18596 18597 action: function ( e, dt, button, config ) { 18598 // Set the text 18599 var xml = ''; 18600 var data = dt.buttons.exportData( config.exportOptions ); 18601 var addRow = function ( row ) { 18602 var cells = []; 18603 18604 for ( var i=0, ien=row.length ; i<ien ; i++ ) { 18605 if ( row[i] === null || row[i] === undefined ) { 18606 row[i] = ''; 18607 } 18608 18609 // Don't match numbers with leading zeros or a negative anywhere 18610 // but the start 18611 cells.push( typeof row[i] === 'number' || (row[i].match && $.trim(row[i]).match(/^-?\d+(\.\d+)?$/) && row[i].charAt(0) !== '0') ? 18612 '<c t="n"><v>'+row[i]+'</v></c>' : 18613 '<c t="inlineStr"><is><t>'+( 18614 ! row[i].replace ? 18615 row[i] : 18616 row[i] 18617 .replace(/&(?!amp;)/g, '&') 18618 .replace(/</g, '<') 18619 .replace(/>/g, '>') 18620 .replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, ''))+ // remove control characters 18621 '</t></is></c>' // they are not valid in XML 18622 ); 18623 } 18624 18625 return '<row>'+cells.join('')+'</row>'; 18626 }; 18627 18628 if ( config.header ) { 18629 xml += addRow( data.header ); 18630 } 18631 18632 for ( var i=0, ien=data.body.length ; i<ien ; i++ ) { 18633 xml += addRow( data.body[i] ); 18634 } 18635 18636 if ( config.footer ) { 18637 xml += addRow( data.footer ); 18638 } 18639 18640 var zip = new window.JSZip(); 18641 var _rels = zip.folder("_rels"); 18642 var xl = zip.folder("xl"); 18643 var xl_rels = zip.folder("xl/_rels"); 18644 var xl_worksheets = zip.folder("xl/worksheets"); 18645 18646 zip.file( '[Content_Types].xml', excelStrings['[Content_Types].xml'] ); 18647 _rels.file( '.rels', excelStrings['_rels/.rels'] ); 18648 xl.file( 'workbook.xml', excelStrings['xl/workbook.xml'].replace( '__SHEET_NAME__', _sheetname( config ) ) ); 18649 xl_rels.file( 'workbook.xml.rels', excelStrings['xl/_rels/workbook.xml.rels'] ); 18650 xl_worksheets.file( 'sheet1.xml', excelStrings['xl/worksheets/sheet1.xml'].replace( '__DATA__', xml ) ); 18651 18652 _saveAs( 18653 zip.generate( {type:"blob", mimeType:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'} ), 18654 _filename( config ) 18655 ); 18656 }, 18657 18658 filename: '*', 18659 18660 extension: '.xlsx', 18661 18662 exportOptions: {}, 18663 18664 header: true, 18665
18666 footer: false 18667}; 18668 18669// 18670// PDF export - using pdfMake - http://pdfmake.org 18671// 18672DataTable.ext.buttons.pdfHtml5 = { 18673 className: 'buttons-pdf buttons-html5', 18674 18675 available: function () { 18676 return window.FileReader !== undefined && window.pdfMake; 18677 }, 18678 18679 text: function ( dt ) { 18680 return dt.i18n( 'buttons.pdf', 'PDF' ); 18681 }, 18682 18683 action: function ( e, dt, button, config ) { 18684 var newLine = _newLine( config ); 18685 var data = dt.buttons.exportData( config.exportOptions ); 18686 var rows = []; 18687 18688 if ( config.header ) { 18689 rows.push( $.map( data.header, function ( d ) { 18690 return { 18691 text: typeof d === 'string' ? d : d+'', 18692 style: 'tableHeader' 18693 }; 18694 } ) ); 18695 } 18696 18697 for ( var i=0, ien=data.body.length ; i<ien ; i++ ) { 18698 rows.push( $.map( data.body[i], function ( d ) { 18699 return { 18700 text: typeof d === 'string' ? d : d+'', 18701 style: i % 2 ? 'tableBodyEven' : 'tableBodyOdd' 18702 }; 18703 } ) ); 18704 } 18705 18706 if ( config.footer ) { 18707 rows.push( $.map( data.footer, function ( d ) { 18708 return { 18709 text: typeof d === 'string' ? d : d+'', 18710 style: 'tableFooter' 18711 }; 18712 } ) ); 18713 } 18714 18715 var doc = { 18716 pageSize: config.pageSize, 18717 pageOrientation: config.orientation, 18718 content: [ 18719 { 18720 table: { 18721 headerRows: 1, 18722 body: rows 18723 }, 18724 layout: 'noBorders' 18725 } 18726 ], 18727 styles: { 18728 tableHeader: { 18729 bold: true, 18730 fontSize: 11, 18731 color: 'white', 18732 fillColor: '#2d4154', 18733 alignment: 'center' 18734 }, 18735 tableBodyEven: {}, 18736 tableBodyOdd: { 18737 fillColor: '#f3f3f3' 18738 }, 18739 tableFooter: { 18740 bold: true, 18741 fontSize: 11, 18742 color: 'white', 18743 fillColor: '#2d4154' 18744 }, 18745 title: { 18746 alignment: 'center', 18747 fontSize: 15 18748 }, 18749 message: {} 18750 }, 18751 defaultStyle: { 18752 fontSize: 10 18753 } 18754 }; 18755 18756 if ( config.message ) { 18757 doc.content.unshift( { 18758 text: config.message, 18759 style: 'message', 18760 margin: [ 0, 0, 0, 12 ] 18761 } ); 18762 } 18763 18764 if ( config.title ) { 18765 doc.content.unshift( { 18766 text: _title( config, false ), 18767 style: 'title', 18768 margin: [ 0, 0, 0, 12 ] 18769 } ); 18770 } 18771 18772 if ( config.customize ) { 18773 config.customize( doc, config ); 18774 } 18775 18776 var pdf = window.pdfMake.createPdf( doc ); 18777 18778 if ( config.download === 'open' && ! _isSafari() ) { 18779 pdf.open(); 18780 } 18781 else { 18782 pdf.getBuffer( function (buffer) { 18783 var blob = new Blob( [buffer], {type:'application/pdf'} ); 18784 18785 _saveAs( blob, _filename( config ) ); 18786 } ); 18787 } 18788 }, 18789 18790 title: '*', 18791 18792 filename: '*', 18793 18794 extension: '.pdf', 18795 18796 exportOptions: {}, 18797 18798 orientation: 'portrait', 18799 18800 pageSize: 'A4', 18801 18802 header: true, 18803 18804 footer: false, 18805 18806 message: null, 18807 18808 customize: null, 18809 18810 download: 'download' 18811}; 18812 18813 18814return DataTable.Buttons; 18815})); 18816 18817 18818/*! FixedHeader 3.1.1 18819 * ©2009-2016 SpryMedia Ltd - datatables.net/license 18820 */ 18821 18822/** 18823 * @summary FixedHeader 18824 * @description Fix a table's header or footer, so it is always visible while 18825 * scrolling 18826 * @version 3.1.1 18827 * @file dataTables.fixedHeader.js 18828 * @author SpryMedia Ltd (www.sprymedia.co.uk) 18829 * @contact www.sprymedia.co.uk/contact 18830 * @copyright Copyright 2009-2016 SpryMedia Ltd. 18831 * 18832 * This source file is free software, available under the following license: 18833 * MIT license - http://datatables.net/license/mit 18834 * 18835 * This source file is distributed in the hope that it will be useful, but 18836 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 18837 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. 18838 * 18839 * For details please refer to: http://www.datatables.net 18840 */ 18841 18842(function( factory ){ 18843 if ( typeof define === 'function' && define.amd ) { 18844 // AMD 18845 define( ['jquery', 'datatables.net'], function ( $ ) { 18846 return factory( $, window, document ); 18847 } ); 18848 } 18849 else if ( typeof exports === 'object' ) { 18850 // CommonJS 18851 module.exports = function (root, $) { 18852 if ( ! root ) { 18853 root = window; 18854 } 18855 18856 if ( ! $ || ! $.fn.dataTable ) { 18857 $ = require('datatables.net')(root, $).$; 18858 } 18859 18860 return factory( $, root, root.document ); 18861 }; 18862 } 18863 else { 18864 // Browser 18865 factory( jQuery, window, document ); 18866 } 18867}(function( $, window, document, undefined ) { 18868'use strict'; 18869var DataTable = $.fn.dataTable; 18870 18871 18872var _instCounter = 0; 18873 18874var FixedHeader = function ( dt, config ) { 18875 // Sanity check - you just know it will happen 18876 if ( ! (this instanceof FixedHeader) ) { 18877 throw "FixedHeader must be initialised with the 'new' keyword."; 18878 } 18879 18880 // Allow a boolean true for defaults 18881 if ( config === true ) { 18882 config = {}; 18883 } 18884 18885 dt = new DataTable.Api( dt ); 18886 18887 this.c = $.extend( true, {}, FixedHeader.defaults, config ); 18888 18889 this.s = { 18890 dt: dt, 18891 position: { 18892 theadTop: 0, 18893 tbodyTop: 0, 18894 tfootTop: 0, 18895 tfootBottom: 0, 18896 width: 0, 18897 left: 0, 18898 tfootHeight: 0, 18899 theadHeight: 0, 18900 windowHeight: $(window).height(), 18901 visible: true 18902 }, 18903 headerMode: null, 18904 footerMode: null, 18905 autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
18906 namespace: '.dtfc'+(_instCounter++), 18907 scrollLeft: { 18908 header: -1, 18909 footer: -1 18910 }, 18911 enable: true 18912 }; 18913 18914 this.dom = { 18915 floatingHeader: null, 18916 thead: $(dt.table().header()), 18917 tbody: $(dt.table().body()), 18918 tfoot: $(dt.table().footer()), 18919 header: { 18920 host: null, 18921 floating: null, 18922 placeholder: null 18923 }, 18924 footer: { 18925 host: null, 18926 floating: null, 18927 placeholder: null 18928 } 18929 }; 18930 18931 this.dom.header.host = this.dom.thead.parent(); 18932 this.dom.footer.host = this.dom.tfoot.parent(); 18933 18934 var dtSettings = dt.settings()[0]; 18935 if ( dtSettings._fixedHeader ) { 18936 throw "FixedHeader already initialised on table "+dtSettings.nTable.id; 18937 } 18938 18939 dtSettings._fixedHeader = this; 18940 18941 this._constructor(); 18942}; 18943 18944 18945/* 18946 * Variable: FixedHeader 18947 * Purpose: Prototype for FixedHeader 18948 * Scope: global 18949 */ 18950$.extend( FixedHeader.prototype, { 18951 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 18952 * API methods 18953 */ 18954 18955 /** 18956 * Enable / disable the fixed elements 18957 * 18958 * @param {boolean} enable `true` to enable, `false` to disable 18959 */ 18960 enable: function ( enable ) 18961 { 18962 this.s.enable = enable; 18963 18964 if ( this.c.header ) { 18965 this._modeChange( 'in-place', 'header', true ); 18966 } 18967 18968 if ( this.c.footer && this.dom.tfoot.length ) { 18969 this._modeChange( 'in-place', 'footer', true ); 18970 } 18971 18972 this.update(); 18973 }, 18974 18975 /** 18976 * Set header offset 18977 * 18978 * @param {int} new value for headerOffset 18979 */ 18980 headerOffset: function ( offset ) 18981 { 18982 if ( offset !== undefined ) { 18983 this.c.headerOffset = offset; 18984 this.update(); 18985 } 18986 18987 return this.c.headerOffset; 18988 }, 18989 18990 /** 18991 * Set footer offset 18992 * 18993 * @param {int} new value for footerOffset 18994 */ 18995 footerOffset: function ( offset ) 18996 { 18997 if ( offset !== undefined ) { 18998 this.c.footerOffset = offset; 18999 this.update(); 19000 } 19001 19002 return this.c.footerOffset; 19003 }, 19004 19005 19006 /** 19007 * Recalculate the position of the fixed elements and force them into place 19008 */ 19009 update: function () 19010 { 19011 this._positions(); 19012 this._scroll( true ); 19013 }, 19014 19015 19016 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 19017 * Constructor 19018 */ 19019 19020 /** 19021 * FixedHeader constructor - adding the required event listeners and 19022 * simple initialisation 19023 * 19024 * @private 19025 */ 19026 _constructor: function () 19027 { 19028 var that = this; 19029 var dt = this.s.dt; 19030 19031 $(window) 19032 .on( 'scroll'+this.s.namespace, function () { 19033 that._scroll(); 19034 } ) 19035 .on( 'resize'+this.s.namespace, function () { 19036 that.s.position.windowHeight = $(window).height(); 19037 that.update(); 19038 } ); 19039 19040 dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc', function () { 19041 that.update(); 19042 } ); 19043 19044 dt.on( 'destroy.dtfc', function () { 19045 dt.off( '.dtfc' ); 19046 $(window).off( that.s.namespace ); 19047 } ); 19048 19049 this._positions(); 19050 this._scroll(); 19051 }, 19052 19053 19054 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 19055 * Private methods 19056 */ 19057 19058 /** 19059 * Clone a fixed item to act as a place holder for the original element 19060 * which is moved into a clone of the table element, and moved around the 19061 * document to give the fixed effect. 19062 * 19063 * @param {string} item 'header' or 'footer' 19064 * @param {boolean} force Force the clone to happen, or allow automatic 19065 * decision (reuse existing if available) 19066 * @private 19067 */ 19068 _clone: function ( item, force ) 19069 { 19070 var dt = this.s.dt; 19071 var itemDom = this.dom[ item ]; 19072 var itemElement = item === 'header' ? 19073 this.dom.thead : 19074 this.dom.tfoot; 19075 19076 if ( ! force && itemDom.floating ) { 19077 // existing floating element - reuse it 19078 itemDom.floating.removeClass( 'fixedHeader-floating fixedHeader-locked' ); 19079 } 19080 else { 19081 if ( itemDom.floating ) { 19082 itemDom.placeholder.remove(); 19083 this._unsize( item ); 19084 itemDom.floating.children().detach(); 19085 itemDom.floating.remove(); 19086 } 19087 19088 itemDom.floating = $( dt.table().node().cloneNode( false ) ) 19089 .css( 'table-layout', 'fixed' ) 19090 .removeAttr( 'id' ) 19091 .append( itemElement ) 19092 .appendTo( 'body' ); 19093 19094 // Insert a fake thead/tfoot into the DataTable to stop it jumping around 19095 itemDom.placeholder = itemElement.clone( false ); 19096 itemDom.host.prepend( itemDom.placeholder ); 19097 19098 // Clone widths 19099 this._matchWidths( itemDom.placeholder, itemDom.floating ); 19100 } 19101 }, 19102 19103 /** 19104 * Copy widths from the cells in one element to another. This is required 19105 * for the footer as the footer in the main table takes its sizes from the 19106 * header columns. That isn't present in the footer so to have it still 19107 * align correctly, the sizes need to be copied over. It is also required 19108 * for the header when auto width is not enabled 19109 * 19110 * @param {jQuery}
19110 from Copy widths from 19111 * @param {jQuery} to Copy widths to 19112 * @private 19113 */ 19114 _matchWidths: function ( from, to ) { 19115 var get = function ( name ) { 19116 return $(name, from) 19117 .map( function () { 19118 return $(this).width(); 19119 } ).toArray(); 19120 }; 19121 19122 var set = function ( name, toWidths ) { 19123 $(name, to).each( function ( i ) { 19124 $(this).css( { 19125 width: toWidths[i], 19126 minWidth: toWidths[i] 19127 } ); 19128 } ); 19129 }; 19130 19131 var thWidths = get( 'th' ); 19132 var tdWidths = get( 'td' ); 19133 19134 set( 'th', thWidths ); 19135 set( 'td', tdWidths ); 19136 }, 19137 19138 /** 19139 * Remove assigned widths from the cells in an element. This is required 19140 * when inserting the footer back into the main table so the size is defined 19141 * by the header columns and also when auto width is disabled in the 19142 * DataTable. 19143 * 19144 * @param {string} item The `header` or `footer` 19145 * @private 19146 */ 19147 _unsize: function ( item ) { 19148 var el = this.dom[ item ].floating; 19149 19150 if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) { 19151 $('th, td', el).css( { 19152 width: '', 19153 minWidth: '' 19154 } ); 19155 } 19156 else if ( el && item === 'header' ) { 19157 $('th, td', el).css( 'min-width', '' ); 19158 } 19159 }, 19160 19161 /** 19162 * Reposition the floating elements to take account of horizontal page 19163 * scroll 19164 * 19165 * @param {string} item The `header` or `footer` 19166 * @param {int} scrollLeft Document scrollLeft 19167 * @private 19168 */ 19169 _horizontal: function ( item, scrollLeft ) 19170 { 19171 var itemDom = this.dom[ item ]; 19172 var position = this.s.position; 19173 var lastScrollLeft = this.s.scrollLeft; 19174 19175 if ( itemDom.floating && lastScrollLeft[ item ] !== scrollLeft ) { 19176 itemDom.floating.css( 'left', position.left - scrollLeft ); 19177 19178 lastScrollLeft[ item ] = scrollLeft; 19179 } 19180 }, 19181 19182 /** 19183 * Change from one display mode to another. Each fixed item can be in one 19184 * of: 19185 * 19186 * * `in-place` - In the main DataTable 19187 * * `in` - Floating over the DataTable 19188 * * `below` - (Header only) Fixed to the bottom of the table body 19189 * * `above` - (Footer only) Fixed to the top of the table body 19190 * 19191 * @param {string} mode Mode that the item should be shown in 19192 * @param {string} item 'header' or 'footer' 19193 * @param {boolean} forceChange Force a redraw of the mode, even if already 19194 * in that mode. 19195 * @private 19196 */ 19197 _modeChange: function ( mode, item, forceChange ) 19198 { 19199 var dt = this.s.dt; 19200 var itemDom = this.dom[ item ]; 19201 var position = this.s.position; 19202 19203 if ( mode === 'in-place' ) { 19204 // Insert the header back into the table's real header 19205 if ( itemDom.placeholder ) { 19206 itemDom.placeholder.remove(); 19207 itemDom.placeholder = null; 19208 } 19209 19210 this._unsize( item ); 19211 19212 if ( item === 'header' ) { 19213 itemDom.host.prepend( this.dom.thead ); 19214 } 19215 else { 19216 itemDom.host.append( this.dom.tfoot ); 19217 } 19218 19219 if ( itemDom.floating ) { 19220 itemDom.floating.remove(); 19221 itemDom.floating = null; 19222 } 19223 } 19224 else if ( mode === 'in' ) { 19225 // Remove the header from the read header and insert into a fixed 19226 // positioned floating table clone 19227 this._clone( item, forceChange ); 19228 19229 itemDom.floating 19230 .addClass( 'fixedHeader-floating' ) 19231 .css( item === 'header' ? 'top' : 'bottom', this.c[item+'Offset'] ) 19232 .css( 'left', position.left+'px' ) 19233 .css( 'width', position.width+'px' ); 19234 19235 if ( item === 'footer' ) { 19236 itemDom.floating.css( 'top', '' ); 19237 } 19238 } 19239 else if ( mode === 'below' ) { // only used for the header 19240 // Fix the position of the floating header at base of the table body 19241 this._clone( item, forceChange ); 19242 19243 itemDom.floating 19244 .addClass( 'fixedHeader-locked' ) 19245 .css( 'top', position.tfootTop - position.theadHeight ) 19246 .css( 'left', position.left+'px' ) 19247 .css( 'width', position.width+'px' ); 19248 } 19249 else if ( mode === 'above' ) { // only used for the footer 19250 // Fix the position of the floating footer at top of the table body 19251 this._clone( item, forceChange ); 19252 19253 itemDom.floating 19254 .addClass( 'fixedHeader-locked' ) 19255 .css( 'top', position.tbodyTop ) 19256 .css( 'left', position.left+'px' ) 19257 .css( 'width', position.width+'px' ); 19258 } 19259 19260 this.s.scrollLeft.header = -1; 19261 this.s.scrollLeft.footer = -1; 19262 this.s[item+'Mode'] = mode; 19263 }, 19264 19265 /** 19266 * Cache the positional information that is required for the mode 19267 * calculations that FixedHeader performs. 19268 * 19269 * @private 19270 */ 19271 _positions: function () 19272 { 19273 var dt = this.s.dt; 19274 var table = dt.table(); 19275 var position = this.s.position; 19276 var dom = this.dom; 19277 var tableNode = $(table.node()); 19278 19279 // Need to use the header and footer that are in the main table, 19280 // regardless of if they are clones, since they hold the positions we 19281 // want to measure from 19282 var thead = tableNode.children('thead'); 19283 var tfoot = tableNode.children('tfoot'); 19284 var tbody = dom.tbody; 19285 19286 position.visible = tableNode.is(':visible'); 19287 position.width = tableNode.outerWidth(); 19288 position.left = tableNode.offset().left; 19289 position.theadTop = thead.offset().top; 19290 position.tbodyTop = tbody.offset().top; 19291 position.theadHeight = position.tbodyTop - position.theadTop; 19292 19293 if ( tfoot.length ) { 19294 position.tfootTop = tfoot.offset().top; 19295 position.tfootBottom = position.tfootTop + tfoot.outerHeight(); 19296 position.tfootHeight = position.tfootBottom - position.tfootTop; 19297 } 19298 else { 19299 position.tfootTop = position.tbodyTop + tbody.outerHeight(); 19300 position.tfootBottom = position.tfootTop; 19301 position.tfootHeight = position.tfootTop; 19302 } 19303 }, 19304 19305 19306 /** 19307 * Mode calculation - determine what mode the fixed items should be placed 19308 * into. 19309 * 19310 * @param {boolean} forceChange Force a redraw of the mode, even if already 19311 * in that mode. 19312 * @private 19313 */ 19314 _scroll: function ( forceChange ) 19315 { 19316 var windowTop = $(document).scrollTop(); 19317 var windowLeft = $(document).scrollLeft(); 19318 var position = this.s.position; 19319 var headerMode, footerMode; 19320 19321 if ( ! this.s.enable ) { 19322 return; 19323 } 19324 19325 if ( this.c.header ) { 19326 if ( ! position.visible || windowTop <= position.theadTop - this.c.headerOffset ) { 19327 headerMode = 'in-place'; 19328 } 19329 else if ( windowTop <= position.tfootTop - position.theadHeight - this.c.headerOffset ) { 19330 headerMode = 'in'; 19331 } 19332 else { 19333 headerMode = 'below'; 19334 } 19335 19336 if ( forceChange || headerMode !== this.s.headerMode ) { 19337 this._modeChange( headerMode, 'header', forceChange ); 19338 } 19339 19340 this._horizontal( 'header', windowLeft ); 19341 } 19342 19343 if ( this.c.footer && this.dom.tfoot.length ) { 19344 if ( ! position.visible || windowTop + position.windowHeight >= position.tfootBottom + this.c.footerOffset ) { 19345 footerMode = 'in-place'; 19346 } 19347 else if ( position.windowHeight + windowTop > position.tbodyTop + position.tfootHeight + this.c.footerOffset ) { 19348 footerMode = 'in'; 19349 } 19350 else { 19351 footerMode = 'above'; 19352 } 19353 19354 if ( forceChange || footerMode !== this.s.footerMode ) { 19355 this._modeChange( footerMode, 'footer', forceChange ); 19356 } 19357 19358 this._horizontal( 'footer', windowLeft ); 19359 } 19360 } 19361} ); 19362 19363 19364/** 19365 * Version 19366 * @type {String} 19367 * @static 19368 */ 19369FixedHeader.version = "3.1.1"; 19370 19371/** 19372 * Defaults 19373 * @type {Object} 19374 * @static 19375 */ 19376FixedHeader.defaults = { 19377 header: true, 19378 footer: false, 19379 headerOffset: 0, 19380 footerOffset: 0 19381}; 19382 19383 19384/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 19385 * DataTables interfaces 19386 */ 19387 19388// Attach for constructor access 19389$.fn.dataTable.FixedHeader = FixedHeader; 19390$.fn.DataTable.FixedHeader = FixedHeader; 19391 19392 19393// DataTables creation - check if the FixedHeader option has been defined on the 19394// table and if so, initialise 19395$(document).on( 'init.dt.dtfh', function (e, settings, json) { 19396 if ( e.namespace !== 'dt' ) { 19397 return; 19398 } 19399 19400 var init = settings.oInit.fixedHeader; 19401 var defaults = DataTable.defaults.fixedHeader; 19402 19403 if ( (init || defaults) && ! settings._fixedHeader ) { 19404 var opts = $.extend( {}, defaults, init ); 19405 19406 if ( init !== false ) { 19407 new FixedHeader( settings, opts ); 19408 } 19409 } 19410} ); 19411 19412// DataTables API methods 19413DataTable.Api.register( 'fixedHeader()', function () {} ); 19414 19415DataTable.Api.register( 'fixedHeader.adjust()', function () { 19416 return this.iterator( 'table', function ( ctx ) { 19417 var fh = ctx._fixedHeader; 19418 19419 if ( fh ) { 19420 fh.update(); 19421 } 19422 } ); 19423} ); 19424 19425DataTable.Api.register( 'fixedHeader.enable()', function ( flag ) { 19426 return this.iterator( 'table', function ( ctx ) { 19427 var fh = ctx._fixedHeader; 19428 19429 if ( fh ) { 19430 fh.enable( flag !== undefined ? flag : true ); 19431 } 19432 } ); 19433} ); 19434 19435DataTable.Api.register( 'fixedHeader.disable()', function ( ) { 19436 return this.iterator( 'table', function ( ctx ) { 19437 var fh = ctx._fixedHeader; 19438 19439 if ( fh ) { 19440 fh.enable( false ); 19441 } 19442 } ); 19443} ); 19444 19445$.each( ['header', 'footer'], function ( i, el ) { 19446 DataTable.Api.register( 'fixedHeader.'+el+'Offset()', function ( offset ) { 19447 var ctx = this.context; 19448 19449 if ( offset === undefined ) { 19450 return ctx.length && ctx[0]._fixedHeader ? 19451 ctx[0]._fixedHeader[el +'Offset']() : 19452 undefined; 19453 } 19454 19455 return this.iterator( 'table', function ( ctx ) { 19456 var fh = ctx._fixedHeader; 19457 19458 if ( fh ) { 19459 fh[ el +'Offset' ]( offset ); 19460 } 19461 } ); 19462 } ); 19463} ); 19464 19465 19466return FixedHeader; 19467})); 19468 19469
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.