1/* 2 * File: jquery.dataTables.js 3 * Version: 1.7.6 4 * Description: Paginate, search and sort HTML tables 5 * Author: Allan Jardine (www.sprymedia.co.uk) 6 * Created: 28/3/2008 7 * Language: Javascript 8 * License: GPL v2 or BSD 3 point style 9 * Project: Mtaala 10 * Contact: [email protected] 11 * 12 * Copyright 2008-2010 Allan Jardine, all rights reserved. 13 * 14 * This source file is free software, under either the GPL v2 license or a 15 * BSD style license, as supplied with this software. 16 * 17 * This source file is distributed in the hope that it will be useful, but 18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 19 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. 20 * 21 * For details please refer to: http://www.datatables.net 22 */ 23 24/* 25 * When considering jsLint, we need to allow eval() as it it is used for reading cookies and 26 * building the dynamic multi-column sort functions. 27 */ 28/*jslint evil: true, undef: true, browser: true */ 29/*globals $, jQuery,_fnExternApiFunc,_fnInitalise,_fnInitComplete,_fnLanguageProcess,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnGatherData,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxUpdateDraw,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnArrayCmp,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap*/ 30 31(function($, window, document) { 32 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 33 * Section - DataTables variables 34 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 35 36 /* 37 * Variable: dataTableSettings 38 * Purpose: Store the settings for each dataTables instance 39 * Scope: jQuery.fn 40 */ 41 $.fn.dataTableSettings = []; 42 var _aoSettings = $.fn.dataTableSettings; /* Short reference for fast internal lookup */ 43 44 /* 45 * Variable: dataTableExt 46 * Purpose: Container for customisable parts of DataTables 47 * Scope: jQuery.fn 48 */ 49 $.fn.dataTableExt = {}; 50 var _oExt = $.fn.dataTableExt; 51 52 53 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 54 * Section - DataTables extensible objects 55 * 56 * The _oExt object is used to provide an area where user dfined plugins can be 57 * added to DataTables. The following properties of the object are used: 58 * oApi - Plug-in API functions 59 * aTypes - Auto-detection of types 60 * oSort - Sorting functions used by DataTables (based on the type) 61 * oPagination - Pagination functions for different input styles 62 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 63 64 /* 65 * Variable: sVersion 66 * Purpose: Version string for plug-ins to check compatibility 67 * Scope: jQuery.fn.dataTableExt 68 * Notes: Allowed format is a.b.c.d.e where: 69 * a:int, b:int, c:int, d:string(dev|beta), e:int. d and e are optional 70 */ 71 _oExt.sVersion = "1.7.6"; 72 73 /* 74 * Variable: sErrMode 75 * Purpose: How should DataTables report an error. Can take the value 'alert' or 'throw' 76 * Scope: jQuery.fn.dataTableExt 77 */ 78 _oExt.sErrMode = "alert"; 79 80 /* 81 * Variable: iApiIndex 82 * Purpose: Index for what 'this' index API functions should use 83 * Scope: jQuery.fn.dataTableExt 84 */ 85 _oExt.iApiIndex = 0; 86 87 /* 88 * Variable: oApi 89 * Purpose: Container for plugin API functions 90 * Scope: jQuery.fn.dataTableExt 91 */ 92 _oExt.oApi = { }; 93 94 /* 95 * Variable: aFiltering 96 * Purpose: Container for plugin filtering functions 97 * Scope: jQuery.fn.dataTableExt 98 */ 99 _oExt.afnFiltering = [ ]; 100 101 /* 102 * Variable: aoFeatures 103 * Purpose: Container for plugin function functions 104 * Scope: jQuery.fn.dataTableExt 105 * Notes: Array of objects with the following parameters: 106 * fnInit: Function for initialisation of Feature. Takes oSettings and returns node 107 * cFeature: Character that will be matched in sDom - case sensitive 108 * sFeature: Feature name - just for completeness :-) 109 */ 110 _oExt.aoFeatures = [ ]; 111 112 /* 113 * Variable: ofnSearch 114 * Purpose: Container for custom filtering functions 115 * Scope: jQuery.fn.dataTableExt 116 * Notes: This is an object (the name should match the type) for custom filtering function, 117 * which can be used for live DOM checking or formatted text filtering 118 */ 119 _oExt.ofnSearch = { }; 120 121 /* 122 * Variable: afnSortData 123 * Purpose: Container for custom sorting data source functions 124 * Scope: jQuery.fn.dataTableExt 125 * Notes: Array (associative) of functions which is run prior to a column of this 126 * 'SortDataType' being sorted upon. 127 * Function input parameters: 128 * object:oSettings- DataTables settings object 129 * int:iColumn - Target column number 130 * Return value: Array of data which exactly matched the full data set size for the column to 131 * be sorted upon 132 */ 133 _oExt.afnSortData = [ ]; 134 135 /* 136 * Variable: oStdClasses 137 * Purpose: Storage for the various classes that DataTables uses 138 * Scope: jQuery.fn.dataTableExt 139 */ 140 _oExt.oStdClasses = { 141 /* Two buttons buttons */ 142 "sPagePrevEnabled": "paginate_enabled_previous", 143 "sPagePrevDisabled": "paginate_disabled_previous", 144 "sPageNextEnabled": "paginate_enabled_next", 145 "sPageNextDisabled": "paginate_disabled_next", 146 "sPageJUINext": "", 147 "sPageJUIPrev": "", 148 149 /* Full numbers paging buttons */ 150 "sPageButton": "paginate_button", 151 "sPageButtonActive": "paginate_active", 152 "sPageButtonStaticDisabled": "paginate_button", 153 "sPageFirst": "first", 154 "sPagePrevious": "previous", 155 "sPageNext": "next", 156 "sPageLast": "last", 157 158 /* Stripping classes */ 159 "sStripOdd": "odd", 160 "sStripEven": "even", 161 162 /* Empty row */ 163 "sRowEmpty": "dataTables_empty", 164 165 /* Features */ 166 "sWrapper": "dataTables_wrapper", 167 "sFilter": "dataTables_filter", 168 "sInfo": "dataTables_info", 169 "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */ 170 "sLength": "dataTables_length", 171 "sProcessing": "dataTables_processing", 172 173 /* Sorting */ 174 "sSortAsc": "sorting_asc", 175 "sSortDesc": "sorting_desc", 176 "sSortable": "sorting", /* Sortable in both directions */ 177 "sSortableAsc": "sorting_asc_disabled", 178 "sSortableDesc": "sorting_desc_disabled", 179 "sSortableNone": "sorting_disabled", 180 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ 181 "sSortJUIAsc": "", 182 "sSortJUIDesc": "", 183 "sSortJUI": "", 184 "sSortJUIAscAllowed": "", 185 "sSortJUIDescAllowed": "", 186 "sSortJUIWrapper": "", 187 188 /* Scrolling */ 189 "sScrollWrapper": "dataTables_scroll", 190 "sScrollHead": "dataTables_scrollHead", 191 "sScrollHeadInner": "dataTables_scrollHeadInner", 192 "sScrollBody": "dataTables_scrollBody", 193 "sScrollFoot": "dataTables_scrollFoot", 194 "sScrollFootInner": "dataTables_scrollFootInner", 195 196 /* Misc */ 197 "sFooterTH": "" 198 }; 199 200 /* 201 * Variable: oJUIClasses 202 * Purpose: Storage for the various classes that DataTables uses - jQuery UI suitable 203 * Scope: jQuery.fn.dataTableExt 204 */ 205 _oExt.oJUIClasses = { 206 /* Two buttons buttons */ 207 "sPagePrevEnabled": "fg-button ui-button ui-state-default ui-corner-left", 208 "sPagePrevDisabled": "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled", 209 "sPageNextEnabled": "fg-button ui-button ui-state-default ui-corner-right", 210 "sPageNextDisabled": "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled", 211 "sPageJUINext": "ui-icon ui-icon-circle-arrow-e", 212 "sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w", 213 214 /* Full numbers paging buttons */ 215 "sPageButton": "fg-button ui-button ui-state-default", 216 "sPageButtonActive": "fg-button ui-button ui-state-default ui-state-disabled", 217 "sPageButtonStaticDisabled": "fg-button ui-button ui-state-default ui-state-disabled", 218 "sPageFirst": "first ui-corner-tl ui-corner-bl", 219 "sPagePrevious": "previous", 220 "sPageNext": "next", 221 "sPageLast": "last ui-corner-tr ui-corner-br", 222 223 /* Stripping classes */ 224 "sStripOdd": "odd", 225 "sStripEven": "even", 226 227 /* Empty row */ 228 "sRowEmpty": "dataTables_empty", 229 230 /* Features */ 231 "sWrapper": "dataTables_wrapper", 232 "sFilter": "dataTables_filter", 233 "sInfo": "dataTables_info", 234 "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+ 235 "ui-buttonset-multi paging_", /* Note that the type is postfixed */ 236 "sLength": "dataTables_length", 237 "sProcessing": "dataTables_processing", 238 239 /* Sorting */ 240 "sSortAsc": "ui-state-default", 241 "sSortDesc": "ui-state-default", 242 "sSortable": "ui-state-default", 243 "sSortableAsc": "ui-state-default", 244 "sSortableDesc": "ui-state-default", 245 "sSortableNone": "ui-state-default", 246 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ 247 "sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n", 248 "sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s", 249 "sSortJUI": "css_right ui-icon ui-icon-carat-2-n-s", 250 "sSortJUIAscAllowed": "css_right ui-icon ui-icon-carat-1-n", 251 "sSortJUIDescAllowed": "css_right ui-icon ui-icon-carat-1-s", 252 "sSortJUIWrapper": "DataTables_sort_wrapper", 253 254 /* Scrolling */ 255 "sScrollWrapper": "dataTables_scroll", 256 "sScrollHead": "dataTables_scrollHead ui-state-default", 257 "sScrollHeadInner": "dataTables_scrollHeadInner", 258 "sScrollBody": "dataTables_scrollBody", 259 "sScrollFoot": "dataTables_scrollFoot ui-state-default", 260 "sScrollFootInner": "dataTables_scrollFootInner", 261 262 /* Misc */ 263 "sFooterTH": "ui-state-default" 264 }; 265 266 /* 267 * Variable: oPagination 268 * Purpose: Container for the various type of pagination that dataTables supports 269 * Scope: jQuery.fn.dataTableExt 270 */ 271 _oExt.oPagination = { 272 /* 273 * Variable: two_button 274 * Purpose: Standard two button (forward/back) pagination 275 * Scope: jQuery.fn.dataTableExt.oPagination 276 */ 277 "two_button": { 278 /* 279 * Function: oPagination.two_button.fnInit 280 * Purpose: Initalise dom elements required for pagination with forward/back buttons only 281 * Returns: - 282 * Inputs: object:oSettings - dataTables settings object 283 * node:nPaging - the DIV which contains this pagination control 284 * function:fnCallbackDraw - draw function which must be called on update 285 */ 286 "fnInit": function ( oSettings, nPaging, fnCallbackDraw ) 287 { 288 var nPrevious, nNext, nPreviousInner, nNextInner; 289 290 /* Store the next and previous elements in the oSettings object as they can be very 291 * usful for automation - particularly testing 292 */ 293 if ( !oSettings.bJUI ) 294 { 295 nPrevious = document.createElement( 'div' ); 296 nNext = document.createElement( 'div' ); 297 } 298 else 299 { 300 nPrevious = document.createElement( 'a' ); 301 nNext = document.createElement( 'a' ); 302 303 nNextInner = document.createElement('span'); 304 nNextInner.className = oSettings.oClasses.sPageJUINext; 305 nNext.appendChild( nNextInner ); 306 307 nPreviousInner = document.createElement('span'); 308 nPreviousInner.className = oSettings.oClasses.sPageJUIPrev; 309 nPrevious.appendChild( nPreviousInner ); 310 } 311 312 nPrevious.className = oSettings.oClasses.sPagePrevDisabled; 313 nNext.className = oSettings.oClasses.sPageNextDisabled; 314 315 nPrevious.title = oSettings.oLanguage.oPaginate.sPrevious; 316 nNext.title = oSettings.oLanguage.oPaginate.sNext; 317 318 nPaging.appendChild( nPrevious ); 319 nPaging.appendChild( nNext ); 320 321 $(nPrevious).bind( 'click.DT', function() { 322 if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) ) 323 { 324 /* Only draw when the page has actually changed */ 325 fnCallbackDraw( oSettings ); 326 } 327 } ); 328 329 $(nNext).bind( 'click.DT', function() { 330 if ( oSettings.oApi._fnPageChange( oSettings, "next" ) ) 331 { 332 fnCallbackDraw( oSettings ); 333 } 334 } ); 335 336 /* Take the brutal approach to cancelling text selection */ 337 $(nPrevious).bind( 'selectstart.DT', function () { return false; } ); 338 $(nNext).bind( 'selectstart.DT', function () { return false; } ); 339 340 /* ID the first elements only */ 341 if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" ) 342 { 343 nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' ); 344 nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' ); 345 nNext.setAttribute( 'id', oSettings.sTableId+'_next' ); 346 } 347 }, 348 349 /* 350 * Function: oPagination.two_button.fnUpdate 351 * Purpose: Update the two button pagination at the end of the draw 352 * Returns: - 353 * Inputs: object:oSettings - dataTables settings object 354 * function:fnCallbackDraw - draw function to call on page change 355 */ 356 "fnUpdate": function ( oSettings, fnCallbackDraw ) 357 { 358 if ( !oSettings.aanFeatures.p ) 359 { 360 return; 361 } 362 363 /* Loop over each instance of the pager */ 364 var an = oSettings.aanFeatures.p; 365 for ( var i=0, iLen=an.length ; i<iLen ; i++ ) 366 { 367 if ( an[i].childNodes.length !== 0 ) 368 { 369 an[i].childNodes[0].className = 370 ( oSettings._iDisplayStart === 0 ) ? 371 oSettings.oClasses.sPagePrevDisabled : oSettings.oClasses.sPagePrevEnabled; 372 373 an[i].childNodes[1].className = 374 ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ? 375 oSettings.oClasses.sPageNextDisabled : oSettings.oClasses.sPageNextEnabled; 376 } 377 } 378 } 379 }, 380 381 382 /* 383 * Variable: iFullNumbersShowPages 384 * Purpose: Change the number of pages which can be seen 385 * Scope: jQuery.fn.dataTableExt.oPagination 386 */ 387 "iFullNumbersShowPages": 5, 388 389 /* 390 * Variable: full_numbers 391 * Purpose: Full numbers pagination 392 * Scope: jQuery.fn.dataTableExt.oPagination 393 */ 394 "full_numbers": { 395 /* 396 * Function: oPagination.full_numbers.fnInit 397 * Purpose: Initalise dom elements required for pagination with a list of the pages 398 * Returns: - 399 * Inputs: object:oSettings - dataTables settings object 400 * node:nPaging - the DIV which contains this pagination control 401 * function:fnCallbackDraw - draw function which must be called on update 402 */ 403 "fnInit": function ( oSettings, nPaging, fnCallbackDraw ) 404 { 405 var nFirst = document.createElement( 'span' ); 406 var nPrevious = document.createElement( 'span' ); 407 var nList = document.createElement( 'span' ); 408 var nNext = document.createElement( 'span' ); 409 var nLast = document.createElement( 'span' ); 410 411 nFirst.innerHTML = oSettings.oLanguage.oPaginate.sFirst; 412 nPrevious.innerHTML = oSettings.oLanguage.oPaginate.sPrevious; 413 nNext.innerHTML = oSettings.oLanguage.oPaginate.sNext; 414 nLast.innerHTML = oSettings.oLanguage.oPaginate.sLast; 415 416 var oClasses = oSettings.oClasses;
417 nFirst.className = oClasses.sPageButton+" "+oClasses.sPageFirst; 418 nPrevious.className = oClasses.sPageButton+" "+oClasses.sPagePrevious; 419 nNext.className= oClasses.sPageButton+" "+oClasses.sPageNext; 420 nLast.className = oClasses.sPageButton+" "+oClasses.sPageLast; 421 422 nPaging.appendChild( nFirst ); 423 nPaging.appendChild( nPrevious ); 424 nPaging.appendChild( nList ); 425 nPaging.appendChild( nNext ); 426 nPaging.appendChild( nLast ); 427 428 $(nFirst).bind( 'click.DT', function () { 429 if ( oSettings.oApi._fnPageChange( oSettings, "first" ) ) 430 { 431 fnCallbackDraw( oSettings ); 432 } 433 } ); 434 435 $(nPrevious).bind( 'click.DT', function() { 436 if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) ) 437 { 438 fnCallbackDraw( oSettings ); 439 } 440 } ); 441 442 $(nNext).bind( 'click.DT', function() { 443 if ( oSettings.oApi._fnPageChange( oSettings, "next" ) ) 444 { 445 fnCallbackDraw( oSettings ); 446 } 447 } ); 448 449 $(nLast).bind( 'click.DT', function() { 450 if ( oSettings.oApi._fnPageChange( oSettings, "last" ) ) 451 { 452 fnCallbackDraw( oSettings ); 453 } 454 } ); 455 456 /* Take the brutal approach to cancelling text selection */ 457 $('span', nPaging) 458 .bind( 'mousedown.DT', function () { return false; } ) 459 .bind( 'selectstart.DT', function () { return false; } ); 460 461 /* ID the first elements only */ 462 if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" ) 463 { 464 nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' );
465 nFirst.setAttribute( 'id', oSettings.sTableId+'_first' ); 466 nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' ); 467 nNext.setAttribute( 'id', oSettings.sTableId+'_next' ); 468 nLast.setAttribute( 'id', oSettings.sTableId+'_last' ); 469 } 470 }, 471 472 /* 473 * Function: oPagination.full_numbers.fnUpdate 474 * Purpose: Update the list of page buttons shows 475 * Returns: - 476 * Inputs: object:oSettings - dataTables settings object 477 * function:fnCallbackDraw - draw function to call on page change 478 */ 479 "fnUpdate": function ( oSettings, fnCallbackDraw ) 480 { 481 if ( !oSettings.aanFeatures.p ) 482 { 483 return; 484 } 485 486 var iPageCount = _oExt.oPagination.iFullNumbersShowPages; 487 var iPageCountHalf = Math.floor(iPageCount / 2); 488 var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength); 489 var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1; 490 var sList = ""; 491 var iStartButton, iEndButton, i, iLen; 492 var oClasses = oSettings.oClasses; 493 494 /* Pages calculation */ 495 if (iPages < iPageCount) 496 { 497 iStartButton = 1; 498 iEndButton = iPages; 499 } 500 else 501 { 502 if (iCurrentPage <= iPageCountHalf) 503 { 504 iStartButton = 1; 505 iEndButton = iPageCount; 506 } 507 else 508 { 509 if (iCurrentPage >= (iPages - iPageCountHalf)) 510 { 511 iStartButton = iPages - iPageCount + 1; 512 iEndButton = iPages; 513 } 514 else 515 { 516 iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1; 517 iEndButton = iStartButton + iPageCount - 1; 518 } 519 } 520 } 521 522 /* Build the dynamic list */ 523 for ( i=iStartButton ; i<=iEndButton ; i++ ) 524 { 525 if ( iCurrentPage != i ) 526 { 527 sList += '<span class="'+oClasses.sPageButton+'">'+i+'</span>'; 528 } 529 else 530 { 531 sList += '<span class="'+oClasses.sPageButtonActive+'">'+i+'</span>'; 532 } 533 } 534 535 /* Loop over each instance of the pager */ 536 var an = oSettings.aanFeatures.p; 537 var anButtons, anStatic, nPaginateList; 538 var fnClick = function() { 539 /* Use the information in the element to jump to the required page */ 540 var iTarget = (this.innerHTML * 1) - 1; 541 oSettings._iDisplayStart = iTarget * oSettings._iDisplayLength; 542 fnCallbackDraw( oSettings ); 543 return false; 544 }; 545 var fnFalse = function () { return false; }; 546 547 for ( i=0, iLen=an.length ; i<iLen ; i++ ) 548 { 549 if ( an[i].childNodes.length === 0 ) 550 { 551 continue; 552 } 553 554 /* Build up the dynamic list forst - html and listeners */ 555 var qjPaginateList = $('span:eq(2)', an[i]); 556 qjPaginateList.html( sList ); 557 $('span', qjPaginateList).bind( 'click.DT', fnClick ).bind( 'mousedown.DT', fnFalse ) 558 .bind( 'selectstart.DT', fnFalse ); 559 560 /* Update the 'premanent botton's classes */ 561 anButtons = an[i].getElementsByTagName('span'); 562 anStatic = [ 563 anButtons[0], anButtons[1], 564 anButtons[anButtons.length-2], anButtons[anButtons.length-1] 565 ]; 566 $(anStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive+" "+oClasses.sPageButtonStaticDisabled ); 567 if ( iCurrentPage == 1 ) 568 { 569 anStatic[0].className += " "+oClasses.sPageButtonStaticDisabled; 570 anStatic[1].className += " "+oClasses.sPageButtonStaticDisabled; 571 } 572 else 573 { 574 anStatic[0].className += " "+oClasses.sPageButton; 575 anStatic[1].className += " "+oClasses.sPageButton; 576 } 577 578 if ( iPages === 0 || iCurrentPage == iPages || oSettings._iDisplayLength == -1 ) 579 { 580 anStatic[2].className += " "+oClasses.sPageButtonStaticDisabled; 581 anStatic[3].className += " "+oClasses.sPageButtonStaticDisabled; 582 } 583 else 584 { 585 anStatic[2].className += " "+oClasses.sPageButton; 586 anStatic[3].className += " "+oClasses.sPageButton; 587 } 588 } 589 } 590 } 591 }; 592 593 /* 594 * Variable: oSort 595 * Purpose: Wrapper for the sorting functions that can be used in DataTables 596 * Scope: jQuery.fn.dataTableExt 597 * Notes: The functions provided in this object are basically standard javascript sort
598 * functions - they expect two inputs which they then compare and then return a priority 599 * result. For each sort method added, two functions need to be defined, an ascending sort and 600 * a descending sort. 601 */ 602 _oExt.oSort = { 603 /* 604 * text sorting 605 */ 606 "string-asc": function ( a, b ) 607 { 608 var x = a.toLowerCase(); 609 var y = b.toLowerCase(); 610 return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 611 }, 612 613 "string-desc": function ( a, b ) 614 { 615 var x = a.toLowerCase(); 616 var y = b.toLowerCase(); 617 return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 618 }, 619 620 621 /* 622 * html sorting (ignore html tags) 623 */ 624 "html-asc": function ( a, b ) 625 { 626 var x = a.replace( /<.*?>/g, "" ).toLowerCase(); 627 var y = b.replace( /<.*?>/g, "" ).toLowerCase(); 628 return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 629 }, 630 631 "html-desc": function ( a, b ) 632 { 633 var x = a.replace( /<.*?>/g, "" ).toLowerCase(); 634 var y = b.replace( /<.*?>/g, "" ).toLowerCase(); 635 return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 636 }, 637 638 639 /* 640 * date sorting 641 */ 642 "date-asc": function ( a, b ) 643 { 644 var x = Date.parse( a ); 645 var y = Date.parse( b ); 646 647 if ( isNaN(x) || x==="" ) 648 { 649 x = Date.parse( "01/01/1970 00:00:00" ); 650 } 651 if ( isNaN(y) || y==="" ) 652 { 653 y = Date.parse( "01/01/1970 00:00:00" ); 654 } 655 656 return x - y; 657 }, 658 659 "date-desc": function ( a, b ) 660 { 661 var x = Date.parse( a ); 662 var y = Date.parse( b ); 663 664 if ( isNaN(x) || x==="" ) 665 { 666 x = Date.parse( "01/01/1970 00:00:00" ); 667 } 668 if ( isNaN(y) || y==="" ) 669 { 670 y = Date.parse( "01/01/1970 00:00:00" ); 671 } 672 673 return y - x; 674 }, 675 676 677 /* 678 * numerical sorting 679 */ 680 "numeric-asc": function ( a, b ) 681 { 682 var x = (a=="-" || a==="") ? 0 : a*1; 683 var y = (b=="-" || b==="") ? 0 : b*1; 684 return x - y; 685 }, 686 687 "numeric-desc": function ( a, b ) 688 { 689 var x = (a=="-" || a==="") ? 0 : a*1; 690 var y = (b=="-" || b==="") ? 0 : b*1; 691 return y - x; 692 } 693 }; 694 695 696 /* 697 * Variable: aTypes 698 * Purpose: Container for the various type of type detection that dataTables supports 699 * Scope: jQuery.fn.dataTableExt 700 * Notes: The functions in this array are expected to parse a string to see if it is a data 701 * type that it recognises. If so then the function should return the name of the type (a 702 * corresponding sort function should be defined!), if the type is not recognised then the 703 * function should return null such that the parser and move on to check the next type. 704 * Note that ordering is important in this array - the functions are processed linearly, 705 * starting at index 0. 706 * Note that the input for these functions is always a string! It cannot be any other data 707 * type 708 */ 709 _oExt.aTypes = [ 710 /* 711 * Function: - 712 * Purpose: Check to see if a string is numeric 713 * Returns: string:'numeric' or null 714 * Inputs: string:sText - string to check 715 */ 716 function ( sData ) 717 { 718 /* Allow zero length strings as a number */ 719 if ( sData.length === 0 ) 720 { 721 return 'numeric'; 722 } 723 724 var sValidFirstChars = "0123456789-"; 725 var sValidChars = "0123456789."; 726 var Char; 727 var bDecimal = false; 728 729 /* Check for a valid first char (no period and allow negatives) */ 730 Char = sData.charAt(0); 731 if (sValidFirstChars.indexOf(Char) == -1) 732 { 733 return null; 734 } 735 736 /* Check all the other characters are valid */ 737 for ( var i=1 ; i<sData.length ; i++ ) 738 { 739 Char = sData.charAt(i); 740 if (sValidChars.indexOf(Char) == -1) 741 { 742 return null; 743 } 744 745 /* Only allowed one decimal place... */ 746 if ( Char == "." ) 747 { 748 if ( bDecimal ) 749 { 750 return null; 751 } 752 bDecimal = true; 753 } 754 } 755 756 return 'numeric'; 757 }, 758 759 /* 760 * Function: - 761 * Purpose: Check to see if a string is actually a formatted date 762 * Returns: string:'date' or null 763 * Inputs: string:sText - string to check 764 */ 765 function ( sData ) 766 { 767 var iParse = Date.parse(sData); 768 if ( (iParse !== null && !isNaN(iParse)) || sData.length === 0 ) 769 { 770 return 'date'; 771 } 772 return null; 773 }, 774 775 /* 776 * Function: - 777 * Purpose: Check to see if a string should be treated as an HTML string 778 * Returns: string:'html' or null 779 * Inputs: string:sText - string to check 780 */ 781 function ( sData ) 782 {
783 if ( sData.indexOf('<') != -1 && sData.indexOf('>') != -1 ) 784 { 785 return 'html'; 786 } 787 return null; 788 } 789 ]; 790 791 /* 792 * Function: fnVersionCheck 793 * Purpose: Check a version string against this version of DataTables. Useful for plug-ins 794 * Returns: bool:true -this version of DataTables is greater or equal to the required version 795 * false -this version of DataTales is not suitable 796 * Inputs: string:sVersion - the version to check against. May be in the following formats: 797 * "a", "a.b" or "a.b.c" 798 * Notes: This function will only check the first three parts of a version string. It is 799 * assumed that beta and dev versions will meet the requirements. This might change in future 800 */ 801 _oExt.fnVersionCheck = function( sVersion ) 802 { 803 /* This is cheap, but very effective */ 804 var fnZPad = function (Zpad, count) 805 { 806 while(Zpad.length < count) { 807 Zpad += '0'; 808 } 809 return Zpad; 810 }; 811 var aThis = _oExt.sVersion.split('.'); 812 var aThat = sVersion.split('.'); 813 var sThis = '', sThat = ''; 814 815 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) 816 { 817 sThis += fnZPad( aThis[i], 3 ); 818 sThat += fnZPad( aThat[i], 3 ); 819 } 820 821 return parseInt(sThis, 10) >= parseInt(sThat, 10); 822 }; 823 824 /* 825 * Variable: _oExternConfig 826 * Purpose: Store information for DataTables to access globally about other instances 827 * Scope: jQuery.fn.dataTableExt 828 */ 829 _oExt._oExternConfig = { 830 /* int:iNextUnique - next unique number for an instance */ 831 "iNextUnique": 0 832 }; 833 834 835 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 836 * Section - DataTables prototype 837 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 838 839 /* 840 * Function: dataTable 841 * Purpose: DataTables information 842 * Returns: - 843 * Inputs: object:oInit - initalisation options for the table 844 */ 845 $.fn.dataTable = function( oInit ) 846 { 847 /* 848 * Function: classSettings 849 * Purpose: Settings container function for all 'class' properties which are required 850 * by dataTables 851 * Returns: - 852 * Inputs: - 853 */ 854 function classSettings () 855 { 856 this.fnRecordsTotal = function () 857 { 858 if ( this.oFeatures.bServerSide ) { 859 return parseInt(this._iRecordsTotal, 10); 860 } else { 861 return this.aiDisplayMaster.length; 862 } 863 }; 864 865 this.fnRecordsDisplay = function () 866 { 867 if ( this.oFeatures.bServerSide ) { 868 return parseInt(this._iRecordsDisplay, 10); 869 } else { 870 return this.aiDisplay.length; 871 } 872 }; 873 874 this.fnDisplayEnd = function () 875 { 876 if ( this.oFeatures.bServerSide ) { 877 if ( this.oFeatures.bPaginate === false || this._iDisplayLength == -1 ) { 878 return this._iDisplayStart+this.aiDisplay.length; 879 } else { 880 return Math.min( this._iDisplayStart+this._iDisplayLength, 881 this._iRecordsDisplay ); 882 } 883 } else { 884 return this._iDisplayEnd; 885 } 886 }; 887 888 /* 889 * Variable: oInstance 890 * Purpose: The DataTables object for this table 891 * Scope: jQuery.dataTable.classSettings 892 */ 893 this.oInstance = null; 894 895 /* 896 * Variable: sInstance 897 * Purpose: Unique idendifier for each instance of the DataTables object 898 * Scope: jQuery.dataTable.classSettings 899 */ 900 this.sInstance = null; 901 902 /* 903 * Variable: oFeatures 904 * Purpose: Indicate the enablement of key dataTable features 905 * Scope: jQuery.dataTable.classSettings 906 */ 907 this.oFeatures = { 908 "bPaginate": true, 909 "bLengthChange": true, 910 "bFilter": true, 911 "bSort": true, 912 "bInfo": true, 913 "bAutoWidth": true, 914 "bProcessing": false, 915 "bSortClasses": true, 916 "bStateSave": false, 917 "bServerSide": false 918 }; 919 920 /* 921 * Variable: oScroll 922 * Purpose: Container for scrolling options 923 * Scope: jQuery.dataTable.classSettings 924 */ 925 this.oScroll = { 926 "sX": "", 927 "sXInner": "", 928 "sY": "", 929 "bCollapse": false, 930 "bInfinite": false, 931 "iLoadGap": 100, 932 "iBarWidth": 0, 933 "bAutoCss": true 934 }; 935 936 /* 937 * Variable: aanFeatures 938 * Purpose: Array referencing the nodes which are used for the features 939 * Scope: jQuery.dataTable.classSettings 940 * Notes: The parameters of this object match what is allowed by sDom - i.e. 941 * 'l' - Length changing 942 * 'f' - Filtering input 943 * 't' - The table! 944 * 'i' - Information 945 * 'p' - Pagination 946 * 'r' - pRocessing 947 */ 948 this.aanFeatures = []; 949 950 /* 951 * Variable: oLanguage 952 * Purpose: Store the language strings used by dataTables 953 * Scope: jQuery.dataTable.classSettings 954 * Notes: The words in the format _VAR_ are variables which are dynamically replaced 955 * by javascript 956 */ 957 this.oLanguage = { 958 "sProcessing": "Processing...", 959 "sLengthMenu": "_MENU_",
960 "sZeroRecords": "No matching records found", 961 "sEmptyTable": "No data available in table", 962 "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries", 963 "sInfoEmpty": "Showing 0 to 0 of 0 entries", 964 "sInfoFiltered": "(filtered from _MAX_ total entries)", 965 "sInfoPostFix": "", 966 "sSearch": "Search:", 967 "sUrl": "", 968 "oPaginate": { 969 "sFirst": "First", 970 "sPrevious": "Previous", 971 "sNext": "Next", 972 "sLast": "Last" 973 }, 974 "fnInfoCallback": null 975 }; 976 977 /* 978 * Variable: aoData 979 * Purpose: Store data information 980 * Scope: jQuery.dataTable.classSettings 981 * Notes: This is an array of objects with the following parameters: 982 * int: _iId - internal id for tracking 983 * array: _aData - internal data - used for sorting / filtering etc 984 * node: nTr - display node 985 * array node: _anHidden - hidden TD nodes 986 * string: _sRowStripe 987 */ 988 this.aoData = []; 989 990 /* 991 * Variable: aiDisplay 992 * Purpose: Array of indexes which are in the current display (after filtering etc) 993 * Scope: jQuery.dataTable.classSettings 994 */ 995 this.aiDisplay = []; 996 997 /* 998 * Variable: aiDisplayMaster 999 * Purpose: Array of indexes for display - no filtering 1000 * Scope: jQuery.dataTable.classSettings 1001 */ 1002 this.aiDisplayMaster = []; 1003 1004 /* 1005 * Variable: aoColumns 1006 * Purpose: Store information about each column that is in use 1007 * Scope: jQuery.dataTable.classSettings 1008 */ 1009 this.aoColumns = []; 1010 1011 /* 1012 * Variable: iNextId 1013 * Purpose: Store the next unique id to be used for a new row 1014 * Scope: jQuery.dataTable.classSettings 1015 */ 1016 this.iNextId = 0; 1017 1018 /* 1019 * Variable: asDataSearch 1020 * Purpose: Search data array for regular expression searching 1021 * Scope: jQuery.dataTable.classSettings 1022 */ 1023 this.asDataSearch = []; 1024 1025 /* 1026 * Variable: oPreviousSearch 1027 * Purpose: Store the previous search incase we want to force a re-search 1028 * or compare the old search to a new one 1029 * Scope: jQuery.dataTable.classSettings 1030 */ 1031 this.oPreviousSearch = { 1032 "sSearch": "", 1033 "bRegex": false, 1034 "bSmart": true 1035 }; 1036 1037 /* 1038 * Variable: aoPreSearchCols 1039 * Purpose: Store the previous search for each column 1040 * Scope: jQuery.dataTable.classSettings 1041 */ 1042 this.aoPreSearchCols = []; 1043 1044 /* 1045 * Variable: aaSorting 1046 * Purpose: Sorting information 1047 * Scope: jQuery.dataTable.classSettings 1048 * Notes: Index 0 - column number 1049 * Index 1 - current sorting direction 1050 * Index 2 - index of asSorting for this column 1051 */ 1052 this.aaSorting = [ [0, 'asc', 0] ]; 1053 1054 /* 1055 * Variable: aaSortingFixed 1056 * Purpose: Sorting information that is always applied 1057 * Scope: jQuery.dataTable.classSettings 1058 */ 1059 this.aaSortingFixed = null; 1060 1061 /* 1062 * Variable: asStripClasses 1063 * Purpose: Classes to use for the striping of a table 1064 * Scope: jQuery.dataTable.classSettings 1065 */ 1066 this.asStripClasses = []; 1067 1068 /* 1069 * Variable: asDestoryStrips 1070 * Purpose: If restoring a table - we should restore it's striping classes as well 1071 * Scope: jQuery.dataTable.classSettings 1072 */ 1073 this.asDestoryStrips = []; 1074 1075 /* 1076 * Variable: sDestroyWidth 1077 * Purpose: If restoring a table - we should restore it's width 1078 * Scope: jQuery.dataTable.classSettings 1079 */ 1080 this.sDestroyWidth = 0; 1081 1082 /* 1083 * Variable: fnRowCallback 1084 * Purpose: Call this function every time a row is inserted (draw) 1085 * Scope: jQuery.dataTable.classSettings 1086 */ 1087 this.fnRowCallback = null; 1088 1089 /* 1090 * Variable: fnHeaderCallback 1091 * Purpose: Callback function for the header on each draw 1092 * Scope: jQuery.dataTable.classSettings 1093 */ 1094 this.fnHeaderCallback = null; 1095 1096 /* 1097 * Variable: fnFooterCallback 1098 * Purpose: Callback function for the footer on each draw 1099 * Scope: jQuery.dataTable.classSettings 1100 */ 1101 this.fnFooterCallback = null; 1102 1103 /* 1104 * Variable: aoDrawCallback 1105 * Purpose: Array of callback functions for draw callback functions 1106 * Scope: jQuery.dataTable.classSettings 1107 * Notes: Each array element is an object with the following parameters: 1108 * function:fn - function to call 1109 * string:sName - name callback (feature). useful for arranging array 1110 */
1111 this.aoDrawCallback = []; 1112 1113 /* 1114 * Variable: fnInitComplete 1115 * Purpose: Callback function for when the table has been initalised 1116 * Scope: jQuery.dataTable.classSettings 1117 */ 1118 this.fnInitComplete = null; 1119 1120 /* 1121 * Variable: sTableId 1122 * Purpose: Cache the table ID for quick access 1123 * Scope: jQuery.dataTable.classSettings 1124 */ 1125 this.sTableId = ""; 1126 1127 /* 1128 * Variable: nTable 1129 * Purpose: Cache the table node for quick access 1130 * Scope: jQuery.dataTable.classSettings 1131 */ 1132 this.nTable = null; 1133 1134 /* 1135 * Variable: nTHead 1136 * Purpose: Permanent ref to the thead element 1137 * Scope: jQuery.dataTable.classSettings 1138 */ 1139 this.nTHead = null; 1140 1141 /* 1142 * Variable: nTFoot 1143 * Purpose: Permanent ref to the tfoot element - if it exists 1144 * Scope: jQuery.dataTable.classSettings 1145 */ 1146 this.nTFoot = null; 1147 1148 /* 1149 * Variable: nTBody 1150 * Purpose: Permanent ref to the tbody element 1151 * Scope: jQuery.dataTable.classSettings 1152 */ 1153 this.nTBody = null; 1154 1155 /* 1156 * Variable: nTableWrapper 1157 * Purpose: Cache the wrapper node (contains all DataTables controlled elements) 1158 * Scope: jQuery.dataTable.classSettings 1159 */ 1160 this.nTableWrapper = null; 1161 1162 /* 1163 * Variable: bInitialised 1164 * Purpose: Indicate if all required information has been read in 1165 * Scope: jQuery.dataTable.classSettings 1166 */ 1167 this.bInitialised = false; 1168 1169 /* 1170 * Variable: aoOpenRows 1171 * Purpose: Information about open rows 1172 * Scope: jQuery.dataTable.classSettings 1173 * Notes: Has the parameters 'nTr' and 'nParent' 1174 */ 1175 this.aoOpenRows = []; 1176 1177 /* 1178 * Variable: sDom 1179 * Purpose: Dictate the positioning that the created elements will take 1180 * Scope: jQuery.dataTable.classSettings 1181 * Notes: 1182 * The following options are allowed: 1183 * 'l' - Length changing 1184 * 'f' - Filtering input 1185 * 't' - The table! 1186 * 'i' - Information 1187 * 'p' - Pagination 1188 * 'r' - pRocessing 1189 * The following constants are allowed: 1190 * 'H' - jQueryUI theme "header" classes 1191 * 'F' - jQueryUI theme "footer" classes 1192 * The following syntax is expected: 1193 * '<' and '>' - div elements 1194 * '<"class" and '>' - div with a class 1195 * Examples: 1196 * '<"wrapper"flipt>', '<lf<t>ip>' 1197 */ 1198 this.sDom = 'lfrtip'; 1199 1200 /* 1201 * Variable: sPaginationType 1202 * Purpose: Note which type of sorting should be used 1203 * Scope: jQuery.dataTable.classSettings 1204 */ 1205 this.sPaginationType = "two_button"; 1206 1207 /* 1208 * Variable: iCookieDuration 1209 * Purpose: The cookie duration (for bStateSave) in seconds - default 2 hours 1210 * Scope: jQuery.dataTable.classSettings 1211 */ 1212 this.iCookieDuration = 60 * 60 * 2; 1213 1214 /* 1215 * Variable: sCookiePrefix 1216 * Purpose: The cookie name prefix 1217 * Scope: jQuery.dataTable.classSettings 1218 */ 1219 this.sCookiePrefix = "SpryMedia_DataTables_"; 1220 1221 /* 1222 * Variable: fnCookieCallback 1223 * Purpose: Callback function for cookie creation 1224 * Scope: jQuery.dataTable.classSettings 1225 */ 1226 this.fnCookieCallback = null; 1227 1228 /* 1229 * Variable: aoStateSave 1230 * Purpose: Array of callback functions for state saving 1231 * Scope: jQuery.dataTable.classSettings 1232 * Notes: Each array element is an object with the following parameters: 1233 * function:fn - function to call. Takes two parameters, oSettings and the JSON string to 1234 * save that has been thus far created. Returns a JSON string to be inserted into a 1235 * json object (i.e. '"param": [ 0, 1, 2]') 1236 * string:sName - name of callback 1237 */ 1238 this.aoStateSave = []; 1239 1240 /* 1241 * Variable: aoStateLoad 1242 * Purpose: Array of callback functions for state loading 1243 * Scope: jQuery.dataTable.classSettings 1244 * Notes: Each array element is an object with the following parameters: 1245 * function:fn - function to call. Takes two parameters, oSettings and the object stored. 1246 * May return false to cancel state loading. 1247 * string:sName - name of callback 1248 */ 1249 this.aoStateLoad = []; 1250 1251 /* 1252 * Variable: oLoadedState 1253 * Purpose: State that was loaded from the cookie. Useful for back reference 1254 * Scope: jQuery.dataTable.classSettings 1255 */ 1256 this.oLoadedState = null; 1257 1258 /* 1259 * Variable: sAjaxSource 1260 * Purpose: Source url for AJAX data for the table 1261 * Scope: jQuery.dataTable.classSettings 1262 */ 1263 this.sAjaxSource = null; 1264 1265 /* 1266 * Variable: bAjaxDataGet 1267 * Purpose: Note if draw should be blocked while getting data 1268 * Scope: jQuery.dataTable.classSettings 1269 */ 1270 this.bAjaxDataGet = true; 1271 1272 /* 1273 * Variable: fnServerData 1274 * Purpose: Function to get the server-side data - can be overruled by the developer 1275 * Scope: jQuery.dataTable.classSettings 1276 */ 1277 this.fnServerData = function ( url, data, callback ) { 1278 $.ajax( { 1279 "url": url, 1280 "data": data, 1281 "success": callback, 1282 "dataType": "json", 1283 "cache": false, 1284 "error": function (xhr, error, thrown) { 1285 if ( error == "parsererror" ) { 1286 alert( "DataTables warning: JSON data from server could not be parsed. "+ 1287 "This is caused by a JSON formatting error." ); 1288 } 1289 } 1290 } ); 1291 }; 1292 1293 /* 1294 * Variable: fnFormatNumber 1295 * Purpose: Format numbers for display 1296 * Scope: jQuery.dataTable.classSettings 1297 */ 1298 this.fnFormatNumber = function ( iIn ) 1299 { 1300 if ( iIn < 1000 ) 1301 { 1302 /* A small optimisation for what is likely to be the vast majority of use cases */ 1303 return iIn; 1304 } 1305 else 1306 { 1307 var s=(iIn+""), a=s.split(""), out="", iLen=s.length; 1308 1309 for ( var i=0 ; i<iLen ; i++ ) 1310 { 1311 if ( i%3 === 0 && i !== 0 ) 1312 { 1313 out = ','+out; 1314 } 1315 out = a[iLen-i-1]+out; 1316 } 1317 } 1318 return out; 1319 }; 1320 1321 /* 1322 * Variable: aLengthMenu 1323 * Purpose: List of options that can be used for the user selectable length menu 1324 * Scope: jQuery.dataTable.classSettings 1325 * Note: This varaible can take for form of a 1D array, in which case the value and the 1326 * displayed value in the menu are the same, or a 2D array in which case the value comes 1327 * from the first array, and the displayed value to the end user comes from the second 1328 * array. 2D example: [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, 'All' ] ]; 1329 */ 1330 this.aLengthMenu = [ 10, 25, 50, 100 ]; 1331 1332 /* 1333 * Variable: iDraw 1334 * Purpose: Counter for the draws that the table does. Also used as a tracker for 1335 * server-side processing 1336 * Scope: jQuery.dataTable.classSettings 1337 */ 1338 this.iDraw = 0; 1339 1340 /* 1341 * Variable: bDrawing 1342 * Purpose: Indicate if a redraw is being done - useful for Ajax 1343 * Scope: jQuery.dataTable.classSettings 1344 */ 1345 this.bDrawing = 0; 1346 1347 /* 1348 * Variable: iDrawError 1349 * Purpose: Last draw error 1350 * Scope: jQuery.dataTable.classSettings 1351 */ 1352 this.iDrawError = -1; 1353 1354 /* 1355 * Variable: _iDisplayLength, _iDisplayStart, _iDisplayEnd 1356 * Purpose: Display length variables 1357 * Scope: jQuery.dataTable.classSettings 1358 * Notes: These variable must NOT be used externally to get the data length. Rather, use 1359 * the fnRecordsTotal() (etc) functions. 1360 */ 1361 this._iDisplayLength = 10; 1362 this._iDisplayStart = 0; 1363 this._iDisplayEnd = 10; 1364 1365 /* 1366 * Variable: _iRecordsTotal, _iRecordsDisplay 1367 * Purpose: Display length variables used for server side processing 1368 * Scope: jQuery.dataTable.classSettings 1369 * Notes: These variable must NOT be used externally to get the data length. Rather, use 1370 * the fnRecordsTotal() (etc) functions. 1371 */ 1372 this._iRecordsTotal = 0; 1373 this._iRecordsDisplay = 0; 1374 1375 /* 1376 * Variable: bJUI 1377 * Purpose: Should we add the markup needed for jQuery UI theming? 1378 * Scope: jQuery.dataTable.classSettings 1379 */ 1380 this.bJUI = false;
1381 1382 /* 1383 * Variable: bJUI 1384 * Purpose: Should we add the markup needed for jQuery UI theming? 1385 * Scope: jQuery.dataTable.classSettings 1386 */ 1387 this.oClasses = _oExt.oStdClasses; 1388 1389 /* 1390 * Variable: bFiltered and bSorted 1391 * Purpose: Flags to allow callback functions to see what actions have been performed 1392 * Scope: jQuery.dataTable.classSettings 1393 */ 1394 this.bFiltered = false; 1395 this.bSorted = false; 1396 1397 /* 1398 * Variable: oInit 1399 * Purpose: Initialisation object that is used for the table 1400 * Scope: jQuery.dataTable.classSettings 1401 */ 1402 this.oInit = null; 1403 } 1404 1405 /* 1406 * Variable: oApi 1407 * Purpose: Container for publicly exposed 'private' functions 1408 * Scope: jQuery.dataTable 1409 */ 1410 this.oApi = {}; 1411 1412 1413 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1414 * Section - API functions 1415 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1416 1417 /* 1418 * Function: fnDraw 1419 * Purpose: Redraw the table 1420 * Returns: - 1421 * Inputs: bool:bComplete - Refilter and resort (if enabled) the table before the draw. 1422 * Optional: default - true 1423 */ 1424 this.fnDraw = function( bComplete ) 1425 { 1426 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1427 if ( typeof bComplete != 'undefined' && bComplete === false ) 1428 { 1429 _fnCalculateEnd( oSettings ); 1430 _fnDraw( oSettings ); 1431 } 1432 else 1433 { 1434 _fnReDraw( oSettings ); 1435 } 1436 }; 1437 1438 /* 1439 * Function: fnFilter 1440 * Purpose: Filter the input based on data 1441 * Returns: - 1442 * Inputs: string:sInput - string to filter the table on 1443 * int:iColumn - optional - column to limit filtering to 1444 * bool:bRegex - optional - treat as regular expression or not - default false 1445 * bool:bSmart - optional - perform smart filtering or not - default true 1446 * bool:bShowGlobal - optional - show the input global filter in it's input box(es) 1447 * - default true 1448 */ 1449 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal ) 1450 { 1451 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1452 1453 if ( !oSettings.oFeatures.bFilter ) 1454 { 1455 return; 1456 } 1457 1458 if ( typeof bRegex == 'undefined' ) 1459 { 1460 bRegex = false; 1461 } 1462 1463 if ( typeof bSmart == 'undefined' ) 1464 { 1465 bSmart = true; 1466 } 1467 1468 if ( typeof bShowGlobal == 'undefined' ) 1469 { 1470 bShowGlobal = true; 1471 } 1472 1473 if ( typeof iColumn == "undefined" || iColumn === null ) 1474 { 1475 /* Global filter */ 1476 _fnFilterComplete( oSettings, { 1477 "sSearch":sInput, 1478 "bRegex": bRegex, 1479 "bSmart": bSmart 1480 }, 1 ); 1481 1482 if ( bShowGlobal && typeof oSettings.aanFeatures.f != 'undefined' ) 1483 { 1484 var n = oSettings.aanFeatures.f; 1485 for ( var i=0, iLen=n.length ; i<iLen ; i++ ) 1486 { 1487 $('input', n[i]).val( sInput ); 1488 } 1489 } 1490 } 1491 else 1492 { 1493 /* Single column filter */ 1494 oSettings.aoPreSearchCols[ iColumn ].sSearch = sInput; 1495 oSettings.aoPreSearchCols[ iColumn ].bRegex = bRegex; 1496 oSettings.aoPreSearchCols[ iColumn ].bSmart = bSmart; 1497 _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 ); 1498 } 1499 }; 1500 1501 /* 1502 * Function: fnSettings 1503 * Purpose: Get the settings for a particular table for extern. manipulation 1504 * Returns: - 1505 * Inputs: - 1506 */ 1507 this.fnSettings = function( nNode ) 1508 { 1509 return _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1510 }; 1511 1512 /* 1513 * Function: fnVersionCheck 1514 * Notes: The function is the same as the 'static' function provided in the ext variable 1515 */ 1516 this.fnVersionCheck = _oExt.fnVersionCheck; 1517 1518 /* 1519 * Function: fnSort 1520 * Purpose: Sort the table by a particular row 1521 * Returns: - 1522 * Inputs: int:iCol - the data index to sort on. Note that this will 1523 * not match the 'display index' if you have hidden data entries 1524 */ 1525 this.fnSort = function( aaSort ) 1526 { 1527 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1528 oSettings.aaSorting = aaSort; 1529 _fnSort( oSettings ); 1530 }; 1531 1532 /* 1533 * Function: fnSortListener 1534 * Purpose: Attach a sort listener to an element for a given column 1535 * Returns: - 1536 * Inputs: node:nNode - the element to attach the sort listener to 1537 * int:iColumn - the column that a click on this node will sort on 1538 * function:fnCallback - callback function when sort is run - optional 1539 */ 1540 this.fnSortListener = function( nNode, iColumn, fnCallback ) 1541 { 1542 _fnSortAttachListener( _fnSettingsFromNode( this[_oExt.iApiIndex] ), nNode, iColumn, 1543 fnCallback ); 1544 }; 1545
1546 /* 1547 * Function: fnAddData 1548 * Purpose: Add new row(s) into the table 1549 * Returns: array int: array of indexes (aoData) which have been added (zero length on error) 1550 * Inputs: array:mData - the data to be added. The length must match 1551 * the original data from the DOM 1552 * or 1553 * array array:mData - 2D array of data to be added 1554 * bool:bRedraw - redraw the table or not - default true 1555 * Notes: Warning - the refilter here will cause the table to redraw 1556 * starting at zero 1557 * Notes: Thanks to Yekimov Denis for contributing the basis for this function! 1558 */ 1559 this.fnAddData = function( mData, bRedraw ) 1560 { 1561 if ( mData.length === 0 ) 1562 { 1563 return []; 1564 } 1565 1566 var aiReturn = []; 1567 var iTest; 1568 1569 /* Find settings from table node */ 1570 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1571 1572 /* Check if we want to add multiple rows or not */ 1573 if ( typeof mData[0] == "object" ) 1574 { 1575 for ( var i=0 ; i<mData.length ; i++ ) 1576 { 1577 iTest = _fnAddData( oSettings, mData[i] ); 1578 if ( iTest == -1 ) 1579 { 1580 return aiReturn; 1581 } 1582 aiReturn.push( iTest ); 1583 } 1584 } 1585 else 1586 { 1587 iTest = _fnAddData( oSettings, mData ); 1588 if ( iTest == -1 ) 1589 { 1590 return aiReturn; 1591 } 1592 aiReturn.push( iTest ); 1593 } 1594 1595 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 1596 1597 if ( typeof bRedraw == 'undefined' || bRedraw ) 1598 { 1599 _fnReDraw( oSettings ); 1600 } 1601 return aiReturn; 1602 }; 1603 1604 /* 1605 * Function: fnDeleteRow 1606 * Purpose: Remove a row for the table 1607 * Returns: array:aReturn - the row that was deleted 1608 * Inputs: mixed:mTarget - 1609 * int: - index of aoData to be deleted, or 1610 * node(TR): - TR element you want to delete 1611 * function:fnCallBack - callback function - default null 1612 * bool:bRedraw - redraw the table or not - default true 1613 */ 1614 this.fnDeleteRow = function( mTarget, fnCallBack, bRedraw ) 1615 { 1616 /* Find settings from table node */ 1617 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1618 var i, iAODataIndex; 1619 1620 iAODataIndex = (typeof mTarget == 'object') ? 1621 _fnNodeToDataIndex(oSettings, mTarget) : mTarget; 1622 1623 /* Return the data array from this row */ 1624 var oData = oSettings.aoData.splice( iAODataIndex, 1 ); 1625 1626 /* Remove the target row from the search array */ 1627 var iDisplayIndex = $.inArray( iAODataIndex, oSettings.aiDisplay ); 1628 oSettings.asDataSearch.splice( iDisplayIndex, 1 ); 1629 1630 /* Delete from the display arrays */ 1631 _fnDeleteIndex( oSettings.aiDisplayMaster, iAODataIndex ); 1632 _fnDeleteIndex( oSettings.aiDisplay, iAODataIndex ); 1633 1634 /* If there is a user callback function - call it */ 1635 if ( typeof fnCallBack == "function" ) 1636 { 1637 fnCallBack.call( this, oSettings, oData ); 1638 } 1639 1640 /* Check for an 'overflow' they case for dislaying the table */ 1641 if ( oSettings._iDisplayStart >= oSettings.aiDisplay.length ) 1642 { 1643 oSettings._iDisplayStart -= oSettings._iDisplayLength; 1644 if ( oSettings._iDisplayStart < 0 ) 1645 { 1646 oSettings._iDisplayStart = 0; 1647 } 1648 } 1649 1650 if ( typeof bRedraw == 'undefined' || bRedraw ) 1651 { 1652 _fnCalculateEnd( oSettings ); 1653 _fnDraw( oSettings ); 1654 } 1655 1656 return oData; 1657 }; 1658 1659 /* 1660 * Function: fnClearTable 1661 * Purpose: Quickly and simply clear a table 1662 * Returns: - 1663 * Inputs: bool:bRedraw - redraw the table or not - default true 1664 * Notes: Thanks to Yekimov Denis for contributing the basis for this function! 1665 */ 1666 this.fnClearTable = function( bRedraw ) 1667 { 1668 /* Find settings from table node */ 1669 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1670 _fnClearTable( oSettings ); 1671 1672 if ( typeof bRedraw == 'undefined' || bRedraw ) 1673 { 1674 _fnDraw( oSettings ); 1675 } 1676 }; 1677 1678 /* 1679 * Function: fnOpen 1680 * Purpose: Open a display row (append a row after the row in question) 1681 * Returns: node:nNewRow - the row opened 1682 * Inputs: node:nTr - the table row to 'open' 1683 * string:sHtml - the HTML to put into the row
1684 * string:sClass - class to give the new TD cell 1685 */ 1686 this.fnOpen = function( nTr, sHtml, sClass ) 1687 { 1688 /* Find settings from table node */ 1689 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1690 1691 /* the old open one if there is one */ 1692 this.fnClose( nTr ); 1693 1694 var nNewRow = document.createElement("tr"); 1695 var nNewCell = document.createElement("td"); 1696 nNewRow.appendChild( nNewCell ); 1697 nNewCell.className = sClass; 1698 nNewCell.colSpan = _fnVisbleColumns( oSettings ); 1699 nNewCell.innerHTML = sHtml; 1700 1701 /* If the nTr isn't on the page at the moment - then we don't insert at the moment */ 1702 var nTrs = $('tr', oSettings.nTBody); 1703 if ( $.inArray(nTr, nTrs) != -1 ) 1704 { 1705 $(nNewRow).insertAfter(nTr); 1706 } 1707 1708 oSettings.aoOpenRows.push( { 1709 "nTr": nNewRow, 1710 "nParent": nTr 1711 } ); 1712 1713 return nNewRow; 1714 }; 1715 1716 /* 1717 * Function: fnClose 1718 * Purpose: Close a display row 1719 * Returns: int: 0 (success) or 1 (failed) 1720 * Inputs: node:nTr - the table row to 'close' 1721 */ 1722 this.fnClose = function( nTr ) 1723 { 1724 /* Find settings from table node */ 1725 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1726 1727 for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ ) 1728 { 1729 if ( oSettings.aoOpenRows[i].nParent == nTr ) 1730 { 1731 var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode; 1732 if ( nTrParent ) 1733 { 1734 /* Remove it if it is currently on display */ 1735 nTrParent.removeChild( oSettings.aoOpenRows[i].nTr ); 1736 } 1737 oSettings.aoOpenRows.splice( i, 1 ); 1738 return 0; 1739 } 1740 } 1741 return 1; 1742 }; 1743 1744 /* 1745 * Function: fnGetData 1746 * Purpose: Return an array with the data which is used to make up the table 1747 * Returns: array array string: 2d data array ([row][column]) or array string: 1d data array 1748 * or 1749 * array string (if iRow specified) 1750 * Inputs: mixed:mRow - optional - if not present, then the full 2D array for the table 1751 * if given then: 1752 * int: - return 1D array for aoData entry of this index 1753 * node(TR): - return 1D array for this TR element 1754 * Inputs: int:iRow - optional - if present then the array returned will be the data for 1755 * the row with the index 'iRow' 1756 */ 1757 this.fnGetData = function( mRow ) 1758 { 1759 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1760 1761 if ( typeof mRow != 'undefined' ) 1762 { 1763 var iRow = (typeof mRow == 'object') ? 1764 _fnNodeToDataIndex(oSettings, mRow) : mRow; 1765 return ( (aRowData = oSettings.aoData[iRow]) ? aRowData._aData : null); 1766 } 1767 return _fnGetDataMaster( oSettings ); 1768 }; 1769 1770 /* 1771 * Function: fnGetNodes 1772 * Purpose: Return an array with the TR nodes used for drawing the table 1773 * Returns: array node: TR elements 1774 * or 1775 * node (if iRow specified) 1776 * Inputs: int:iRow - optional - if present then the array returned will be the node for 1777 * the row with the index 'iRow' 1778 */ 1779 this.fnGetNodes = function( iRow ) 1780 { 1781 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1782 1783 if ( typeof iRow != 'undefined' ) 1784 { 1785 return ( (aRowData = oSettings.aoData[iRow]) ? aRowData.nTr : null ); 1786 } 1787 return _fnGetTrNodes( oSettings ); 1788 }; 1789 1790 /* 1791 * Function: fnGetPosition 1792 * Purpose: Get the array indexes of a particular cell from it's DOM element 1793 * Returns: int: - row index, or array[ int, int, int ]: - row index, column index (visible) 1794 * and column index including hidden columns 1795 * Inputs: node:nNode - this can either be a TR or a TD in the table, the return is 1796 * dependent on this input 1797 */ 1798 this.fnGetPosition = function( nNode ) 1799 { 1800 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1801 var i; 1802 1803 if ( nNode.nodeName.toUpperCase() == "TR" ) 1804 { 1805 return _fnNodeToDataIndex(oSettings, nNode); 1806 } 1807 else if ( nNode.nodeName.toUpperCase() == "TD" ) 1808 { 1809 var iDataIndex = _fnNodeToDataIndex(oSettings, nNode.parentNode); 1810 var iCorrector = 0; 1811 for ( var j=0 ; j<oSettings.aoColumns.length ; j++ ) 1812 { 1813 if ( oSettings.aoColumns[j].bVisible ) 1814 { 1815 if ( oSettings.aoData[iDataIndex].nTr.getElementsByTagName('td')[j-iCorrector] == nNode ) 1816 { 1817 return [ iDataIndex, j-iCorrector, j ]; 1818 } 1819 } 1820 else 1821 { 1822 iCorrector++; 1823 } 1824 } 1825 } 1826 return null; 1827 }; 1828
1829 /* 1830 * Function: fnUpdate 1831 * Purpose: Update a table cell or row 1832 * Returns: int: 0 okay, 1 error 1833 * Inputs: array string 'or' string:mData - data to update the cell/row with 1834 * mixed:mRow - 1835 * int: - index of aoData to be updated, or 1836 * node(TR): - TR element you want to update 1837 * int:iColumn - the column to update - optional (not used of mData is 2D) 1838 * bool:bRedraw - redraw the table or not - default true 1839 * bool:bAction - perform predraw actions or not (you will want this as 'true' if 1840 * you have bRedraw as true) - default true 1841 */ 1842 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction ) 1843 { 1844 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1845 var iVisibleColumn; 1846 var sDisplay; 1847 var iRow = (typeof mRow == 'object') ? 1848 _fnNodeToDataIndex(oSettings, mRow) : mRow; 1849 1850 if ( typeof mData != 'object' ) 1851 { 1852 sDisplay = mData; 1853 oSettings.aoData[iRow]._aData[iColumn] = sDisplay; 1854 1855 if ( oSettings.aoColumns[iColumn].fnRender !== null ) 1856 { 1857 sDisplay = oSettings.aoColumns[iColumn].fnRender( { 1858 "iDataRow": iRow, 1859 "iDataColumn": iColumn, 1860 "aData": oSettings.aoData[iRow]._aData, 1861 "oSettings": oSettings 1862 } ); 1863 1864 if ( oSettings.aoColumns[iColumn].bUseRendered ) 1865 { 1866 oSettings.aoData[iRow]._aData[iColumn] = sDisplay; 1867 } 1868 } 1869 1870 iVisibleColumn = _fnColumnIndexToVisible( oSettings, iColumn ); 1871 if ( iVisibleColumn !== null ) 1872 { 1873 oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML = 1874 sDisplay; 1875 } 1876 else 1877 { 1878 oSettings.aoData[iRow]._anHidden[iColumn].innerHTML = sDisplay; 1879 } 1880 } 1881 else 1882 { 1883 if ( mData.length != oSettings.aoColumns.length ) 1884 { 1885 _fnLog( oSettings, 0, 'An array passed to fnUpdate must have the same number of '+ 1886 'columns as the table in question - in this case '+oSettings.aoColumns.length ); 1887 return 1; 1888 } 1889 1890 for ( var i=0 ; i<mData.length ; i++ ) 1891 { 1892 sDisplay = mData[i]; 1893 oSettings.aoData[iRow]._aData[i] = sDisplay; 1894 1895 if ( oSettings.aoColumns[i].fnRender !== null ) 1896 { 1897 sDisplay = oSettings.aoColumns[i].fnRender( { 1898 "iDataRow": iRow, 1899 "iDataColumn": i, 1900 "aData": oSettings.aoData[iRow]._aData, 1901 "oSettings": oSettings 1902 } ); 1903
1904 if ( oSettings.aoColumns[i].bUseRendered ) 1905 { 1906 oSettings.aoData[iRow]._aData[i] = sDisplay; 1907 } 1908 } 1909 1910 iVisibleColumn = _fnColumnIndexToVisible( oSettings, i ); 1911 if ( iVisibleColumn !== null ) 1912 { 1913 oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML = 1914 sDisplay; 1915 } 1916 else 1917 { 1918 oSettings.aoData[iRow]._anHidden[i].innerHTML = sDisplay; 1919 } 1920 } 1921 } 1922 1923 /* Modify the search index for this row (strictly this is likely not needed, since fnReDraw 1924 * will rebuild the search array - however, the redraw might be disabled by the user) 1925 */ 1926 var iDisplayIndex = $.inArray( iRow, oSettings.aiDisplay ); 1927 oSettings.asDataSearch[iDisplayIndex] = _fnBuildSearchRow( oSettings, 1928 oSettings.aoData[iRow]._aData ); 1929 1930 /* Perform pre-draw actions */ 1931 if ( typeof bAction == 'undefined' || bAction ) 1932 { 1933 _fnAjustColumnSizing( oSettings ); 1934 } 1935 1936 /* Redraw the table */ 1937 if ( typeof bRedraw == 'undefined' || bRedraw ) 1938 { 1939 _fnReDraw( oSettings ); 1940 } 1941 return 0; 1942 }; 1943 1944 1945 /* 1946 * Function: fnShowColoumn 1947 * Purpose: Show a particular column 1948 * Returns: - 1949 * Inputs: int:iCol - the column whose display should be changed 1950 * bool:bShow - show (true) or hide (false) the column 1951 * bool:bRedraw - redraw the table or not - default true 1952 */ 1953 this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) 1954 { 1955 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 1956 var i, iLen; 1957 var iColumns = oSettings.aoColumns.length; 1958 var nTd, anTds, nCell, anTrs, jqChildren; 1959 1960 /* No point in doing anything if we are requesting what is already true */ 1961 if ( oSettings.aoColumns[iCol].bVisible == bShow ) 1962 { 1963 return; 1964 } 1965 1966 var nTrHead = $('>tr', oSettings.nTHead)[0]; 1967 var nTrFoot = $('>tr', oSettings.nTFoot)[0]; 1968 var anTheadTh = []; 1969 var anTfootTh = []; 1970 for ( i=0 ; i<iColumns ; i++ ) 1971 { 1972 anTheadTh.push( oSettings.aoColumns[i].nTh ); 1973 anTfootTh.push( oSettings.aoColumns[i].nTf ); 1974 } 1975 1976 /* Show the column */ 1977 if ( bShow ) 1978 { 1979 var iInsert = 0; 1980 for ( i=0 ; i<iCol ; i++ ) 1981 { 1982 if ( oSettings.aoColumns[i].bVisible ) 1983 { 1984 iInsert++; 1985 } 1986 } 1987 1988 /* Need to decide if we should use appendChild or insertBefore */ 1989 if ( iInsert >= _fnVisbleColumns( oSettings ) ) 1990 { 1991 nTrHead.appendChild( anTheadTh[iCol] ); 1992 anTrs = $('>tr', oSettings.nTHead); 1993 for ( i=1, iLen=anTrs.length ; i<iLen ; i++ ) 1994 { 1995 anTrs[i].appendChild( oSettings.aoColumns[iCol].anThExtra[i-1] ); 1996 } 1997 1998 if ( nTrFoot ) 1999 { 2000 nTrFoot.appendChild( anTfootTh[iCol] ); 2001 anTrs = $('>tr', oSettings.nTFoot); 2002 for ( i=1, iLen=anTrs.length ; i<iLen ; i++ ) 2003 { 2004 anTrs[i].appendChild( oSettings.aoColumns[iCol].anTfExtra[i-1] ); 2005 } 2006 } 2007 2008 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) 2009 { 2010 nTd = oSettings.aoData[i]._anHidden[iCol]; 2011 oSettings.aoData[i].nTr.appendChild( nTd ); 2012 } 2013 } 2014 else 2015 { 2016 /* Which coloumn should we be inserting before? */ 2017 var iBefore; 2018 for ( i=iCol ; i<iColumns ; i++ ) 2019 { 2020 iBefore = _fnColumnIndexToVisible( oSettings, i ); 2021 if ( iBefore !== null ) 2022 { 2023 break; 2024 } 2025 } 2026 2027 nTrHead.insertBefore( anTheadTh[iCol], nTrHead.getElementsByTagName('th')[iBefore] ); 2028 anTrs = $('>tr', oSettings.nTHead); 2029 for ( i=1, iLen=anTrs.length ; i<iLen ; i++ ) 2030 { 2031 jqChildren = $(anTrs[i]).children(); 2032 anTrs[i].insertBefore( oSettings.aoColumns[iCol].anThExtra[i-1], jqChildren[iBefore] ); 2033 } 2034 2035 if ( nTrFoot ) 2036 { 2037 nTrFoot.insertBefore( anTfootTh[iCol], nTrFoot.getElementsByTagName('th')[iBefore] ); 2038 anTrs = $('>tr', oSettings.nTFoot); 2039 for ( i=1, iLen=anTrs.length ; i<iLen ; i++ ) 2040 { 2041 jqChildren = $(anTrs[i]).children(); 2042 anTrs[i].insertBefore( oSettings.aoColumns[iCol].anTfExtra[i-1], jqChildren[iBefore] ); 2043 } 2044 } 2045 2046 anTds = _fnGetTdNodes( oSettings );
2047 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) 2048 { 2049 nTd = oSettings.aoData[i]._anHidden[iCol]; 2050 oSettings.aoData[i].nTr.insertBefore( nTd, $('>td:eq('+iBefore+')', 2051 oSettings.aoData[i].nTr)[0] ); 2052 } 2053 } 2054 2055 oSettings.aoColumns[iCol].bVisible = true; 2056 } 2057 else 2058 { 2059 /* Remove a column from display */ 2060 nTrHead.removeChild( anTheadTh[iCol] ); 2061 for ( i=0, iLen=oSettings.aoColumns[iCol].anThExtra.length ; i<iLen ; i++ ) 2062 { 2063 nCell = oSettings.aoColumns[iCol].anThExtra[i]; 2064 nCell.parentNode.removeChild( nCell ); 2065 } 2066 2067 if ( nTrFoot ) 2068 { 2069 nTrFoot.removeChild( anTfootTh[iCol] ); 2070 for ( i=0, iLen=oSettings.aoColumns[iCol].anTfExtra.length ; i<iLen ; i++ ) 2071 { 2072 nCell = oSettings.aoColumns[iCol].anTfExtra[i]; 2073 nCell.parentNode.removeChild( nCell ); 2074 } 2075 } 2076 2077 anTds = _fnGetTdNodes( oSettings ); 2078 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) 2079 { 2080 nTd = anTds[ ( i*oSettings.aoColumns.length) + (iCol*1) ]; 2081 oSettings.aoData[i]._anHidden[iCol] = nTd; 2082 nTd.parentNode.removeChild( nTd ); 2083 } 2084 2085 oSettings.aoColumns[iCol].bVisible = false; 2086 } 2087 2088 /* If there are any 'open' rows, then we need to alter the colspan for this col change */ 2089 for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ ) 2090 { 2091 oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings ); 2092 } 2093 2094 /* Do a redraw incase anything depending on the table columns needs it 2095 * (built-in: scrolling) 2096 */ 2097 if ( typeof bRedraw == 'undefined' || bRedraw ) 2098 { 2099 _fnAjustColumnSizing( oSettings ); 2100 _fnDraw( oSettings ); 2101 } 2102 2103 _fnSaveState( oSettings ); 2104 }; 2105 2106 /* 2107 * Function: fnPageChange 2108 * Purpose: Change the pagination 2109 * Returns: - 2110 * Inputs: string:sAction - paging action to take: "first", "previous", "next" or "last" 2111 * bool:bRedraw - redraw the table or not - optional - default true 2112 */ 2113 this.fnPageChange = function ( sAction, bRedraw ) 2114 { 2115 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 2116 _fnPageChange( oSettings, sAction ); 2117 _fnCalculateEnd( oSettings ); 2118 2119 if ( typeof bRedraw == 'undefined' || bRedraw ) 2120 { 2121 _fnDraw( oSettings ); 2122 } 2123 }; 2124 2125 /* 2126 * Function: fnDestroy 2127 * Purpose: Destructor for the DataTable 2128 * Returns: - 2129 * Inputs: - 2130 */ 2131 this.fnDestroy = function ( ) 2132 { 2133 var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); 2134 var nOrig = oSettings.nTableWrapper.parentNode; 2135 var nBody = oSettings.nTBody; 2136 var i, iLen; 2137 2138 /* Flag to note that the table is currently being destoryed - no action should be taken */ 2139 oSettings.bDestroying = true; 2140 2141 /* Blitz all DT events */ 2142 $(oSettings.nTableWrapper).find('*').andSelf().unbind('.DT'); 2143 2144 /* Restore hidden columns */ 2145 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 2146 { 2147 if ( oSettings.aoColumns[i].bVisible === false ) 2148 { 2149 this.fnSetColumnVis( i, true ); 2150 } 2151 } 2152 2153 /* If there is an 'empty' indicator row, remove it */ 2154 $('tbody>tr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove(); 2155 2156 /* When scrolling we had to break the table up - restore it */ 2157 if ( oSettings.nTable != oSettings.nTHead.parentNode ) 2158 { 2159 $('>thead', oSettings.nTable).remove(); 2160 oSettings.nTable.appendChild( oSettings.nTHead ); 2161 } 2162 2163 if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode ) 2164 { 2165 $('>tfoot', oSettings.nTable).remove(); 2166 oSettings.nTable.appendChild( oSettings.nTFoot ); 2167 } 2168 2169 /* Remove the DataTables generated nodes, events and classes */ 2170 oSettings.nTable.parentNode.removeChild( oSettings.nTable ); 2171 $(oSettings.nTableWrapper).remove(); 2172 2173 oSettings.aaSorting = []; 2174 oSettings.aaSortingFixed = []; 2175 _fnSortingClasses( oSettings ); 2176 2177 $(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripClasses.join(' ') ); 2178 2179 if ( !oSettings.bJUI ) 2180 { 2181 $('th', oSettings.nTHead).removeClass( [ _oExt.oStdClasses.sSortable, 2182 _oExt.oStdClasses.sSortableAsc, 2183 _oExt.oStdClasses.sSortableDesc, 2184 _oExt.oStdClasses.sSortableNone ].join(' ') 2185 ); 2186 } 2187 else 2188 {
2189 $('th', oSettings.nTHead).removeClass( [ _oExt.oStdClasses.sSortable, 2190 _oExt.oJUIClasses.sSortableAsc, 2191 _oExt.oJUIClasses.sSortableDesc, 2192 _oExt.oJUIClasses.sSortableNone ].join(' ') 2193 ); 2194 $('th span', oSettings.nTHead).remove(); 2195 } 2196 2197 /* Add the TR elements back into the table in their original order */ 2198 nOrig.appendChild( oSettings.nTable ); 2199 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) 2200 { 2201 nBody.appendChild( oSettings.aoData[i].nTr ); 2202 } 2203 2204 /* Restore the width of the original table */ 2205 oSettings.nTable.style.width = _fnStringToCss(oSettings.sDestroyWidth); 2206 2207 /* If the were originally odd/even type classes - then we add them back here. Note 2208 * this is not fool proof (for example if not all rows as odd/even classes - but 2209 * it's a good effort without getting carried away 2210 */ 2211 $('>tr:even', nBody).addClass( oSettings.asDestoryStrips[0] ); 2212 $('>tr:odd', nBody).addClass( oSettings.asDestoryStrips[1] ); 2213 2214 /* Remove the settings object from the settings array */ 2215 for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ ) 2216 { 2217 if ( _aoSettings[i] == oSettings ) 2218 { 2219 _aoSettings.splice( i, 1 ); 2220 } 2221 } 2222 2223 /* End it all */ 2224 oSettings = null; 2225 }; 2226 2227 /* 2228 * Function: fnAjustColumnSizing 2229 * Purpose: Update tale sizing based on content. This would most likely be used for scrolling 2230 * and will typically need a redraw after it. 2231 * Returns: - 2232 * Inputs: bool:bRedraw - redraw the table or not, you will typically want to - default true 2233 */ 2234 this.fnAdjustColumnSizing = function ( bRedraw ) 2235 { 2236 var oSettings = _fnSettingsFromNode(this[_oExt.iApiIndex]); 2237 _fnAjustColumnSizing( oSettings ); 2238 2239 if ( typeof bRedraw == 'undefined' || bRedraw ) 2240 { 2241 this.fnDraw( false ); 2242 } 2243 else if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" ) 2244 { 2245 /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */ 2246 this.oApi._fnScrollDraw(oSettings); 2247 } 2248 }; 2249 2250 /* 2251 * Plugin API functions 2252 * 2253 * This call will add the functions which are defined in _oExt.oApi to the 2254 * DataTables object, providing a rather nice way to allow plug-in API functions. Note that 2255 * this is done here, so that API function can actually override the built in API functions if 2256 * required for a particular purpose. 2257 */ 2258 2259 /* 2260 * Function: _fnExternApiFunc 2261 * Purpose: Create a wrapper function for exporting an internal func to an external API func 2262 * Returns: function: - wrapped function 2263 * Inputs: string:sFunc - API function name 2264 */ 2265 function _fnExternApiFunc (sFunc) 2266 { 2267 return function() { 2268 var aArgs = [_fnSettingsFromNode(this[_oExt.iApiIndex])].concat( 2269 Array.prototype.slice.call(arguments) ); 2270 return _oExt.oApi[sFunc].apply( this, aArgs ); 2271 }; 2272 } 2273 2274 for ( var sFunc in _oExt.oApi ) 2275 { 2276 if ( sFunc ) 2277 { 2278 /* 2279 * Function: anon 2280 * Purpose: Wrap the plug-in API functions in order to provide the settings as 1st arg 2281 * and execute in this scope 2282 * Returns: - 2283 * Inputs: - 2284 */ 2285 this[sFunc] = _fnExternApiFunc(sFunc); 2286 } 2287 } 2288 2289 2290 2291 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2292 * Section - Local functions 2293 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 2294 2295 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2296 * Section - Initalisation 2297 */ 2298 2299 /* 2300 * Function: _fnInitalise 2301 * Purpose: Draw the table for the first time, adding all required features 2302 * Returns: - 2303 * Inputs: object:oSettings - dataTables settings object 2304 */ 2305 function _fnInitalise ( oSettings ) 2306 { 2307 var i, iLen; 2308 2309 /* Ensure that the table data is fully initialised */ 2310 if ( oSettings.bInitialised === false ) 2311 { 2312 setTimeout( function(){ _fnInitalise( oSettings ); }, 200 ); 2313 return; 2314 } 2315 2316 /* Show the display HTML options */ 2317 _fnAddOptionsHtml( oSettings ); 2318 2319 /* Draw the headers for the table */ 2320 _fnDrawHead( oSettings ); 2321 2322 /* Okay to show that something is going on now */ 2323 _fnProcessingDisplay( oSettings, true ); 2324 2325 /* Calculate sizes for columns */ 2326 if ( oSettings.oFeatures.bAutoWidth ) 2327 { 2328 _fnCalculateColumnWidths( oSettings ); 2329 } 2330
2331 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 2332 { 2333 if ( oSettings.aoColumns[i].sWidth !== null ) 2334 { 2335 oSettings.aoColumns[i].nTh.style.width = _fnStringToCss( oSettings.aoColumns[i].sWidth ); 2336 } 2337 } 2338 2339 /* If there is default sorting required - let's do it. The sort function will do the 2340 * drawing for us. Otherwise we draw the table regardless of the Ajax source - this allows 2341 * the table to look initialised for Ajax sourcing data (show 'loading' message possibly) 2342 */ 2343 if ( oSettings.oFeatures.bSort ) 2344 { 2345 _fnSort( oSettings ); 2346 } 2347 else 2348 { 2349 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 2350 _fnCalculateEnd( oSettings ); 2351 _fnDraw( oSettings ); 2352 } 2353 2354 /* if there is an ajax source load the data */ 2355 if ( oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide ) 2356 { 2357 oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, [], function(json) { 2358 /* Got the data - add it to the table */ 2359 for ( i=0 ; i<json.aaData.length ; i++ ) 2360 { 2361 _fnAddData( oSettings, json.aaData[i] ); 2362 } 2363 2364 /* Reset the init display for cookie saving. We've already done a filter, and 2365 * therefore cleared it before. So we need to make it appear 'fresh' 2366 */ 2367 oSettings.iInitDisplayStart = oSettings._iDisplayStart; 2368 2369 if ( oSettings.oFeatures.bSort ) 2370 { 2371 _fnSort( oSettings ); 2372 } 2373 else 2374 { 2375 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 2376 _fnCalculateEnd( oSettings ); 2377 _fnDraw( oSettings ); 2378 } 2379 2380 _fnProcessingDisplay( oSettings, false ); 2381 _fnInitComplete( oSettings, json ); 2382 } ); 2383 return; 2384 } 2385 2386 /* Server-side processing initialisation complete is done at the end of _fnDraw */ 2387 if ( !oSettings.oFeatures.bServerSide ) 2388 { 2389 _fnProcessingDisplay( oSettings, false ); 2390 _fnInitComplete( oSettings ); 2391 } 2392 } 2393 2394 /* 2395 * Function: _fnInitalise 2396 * Purpose: Draw the table for the first time, adding all required features 2397 * Returns: - 2398 * Inputs: object:oSettings - dataTables settings object 2399 */ 2400 function _fnInitComplete ( oSettings, json ) 2401 { 2402 oSettings._bInitComplete = true; 2403 if ( typeof oSettings.fnInitComplete == 'function' ) 2404 { 2405 if ( typeof json != 'undefined' ) 2406 { 2407 oSettings.fnInitComplete.call( oSettings.oInstance, oSettings, json ); 2408 } 2409 else 2410 { 2411 oSettings.fnInitComplete.call( oSettings.oInstance, oSettings ); 2412 } 2413 } 2414 } 2415 2416 /* 2417 * Function: _fnLanguageProcess 2418 * Purpose: Copy language variables from remote object to a local one 2419 * Returns: - 2420 * Inputs: object:oSettings - dataTables settings object 2421 * object:oLanguage - Language information 2422 * bool:bInit - init once complete 2423 */ 2424 function _fnLanguageProcess( oSettings, oLanguage, bInit ) 2425 { 2426 _fnMap( oSettings.oLanguage, oLanguage, 'sProcessing' ); 2427 _fnMap( oSettings.oLanguage, oLanguage, 'sLengthMenu' ); 2428 _fnMap( oSettings.oLanguage, oLanguage, 'sEmptyTable' ); 2429 _fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords' ); 2430 _fnMap( oSettings.oLanguage, oLanguage, 'sInfo' ); 2431 _fnMap( oSettings.oLanguage, oLanguage, 'sInfoEmpty' ); 2432 _fnMap( oSettings.oLanguage, oLanguage, 'sInfoFiltered' ); 2433 _fnMap( oSettings.oLanguage, oLanguage, 'sInfoPostFix' ); 2434 _fnMap( oSettings.oLanguage, oLanguage, 'sSearch' ); 2435 2436 if ( typeof oLanguage.oPaginate != 'undefined' ) 2437 { 2438 _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sFirst' ); 2439 _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sPrevious' ); 2440 _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sNext' ); 2441 _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sLast' ); 2442 } 2443 2444 /* Backwards compatibility - if there is no sEmptyTable given, then use the same as 2445 * sZeroRecords - assuming that is given. 2446 */ 2447 if ( typeof oLanguage.sEmptyTable == 'undefined' && 2448 typeof oLanguage.sZeroRecords != 'undefined' ) 2449 { 2450 _fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' ); 2451 } 2452 2453 if ( bInit ) 2454 { 2455 _fnInitalise( oSettings ); 2456 } 2457 } 2458 2459 /* 2460 * Function: _fnAddColumn 2461 * Purpose: Add a column to the list used for the table with default values 2462 * Returns: - 2463 * Inputs: object:oSettings - dataTables settings object 2464 * node:nTh - the th element for this column 2465 */ 2466 function _fnAddColumn( oSettings, nTh ) 2467 { 2468 oSettings.aoColumns[ oSettings.aoColumns.length++ ] = { 2469 "sType": null, 2470 "_bAutoType": true, 2471 "bVisible": true, 2472 "bSearchable": true, 2473 "bSortable": true, 2474 "asSorting": [ 'asc', 'desc' ], 2475 "sSortingClass": oSettings.oClasses.sSortable, 2476 "sSortingClassJUI": oSettings.oClasses.sSortJUI, 2477 "sTitle": nTh ? nTh.innerHTML : '', 2478 "sName": '', 2479 "sWidth": null, 2480 "sWidthOrig": null, 2481 "sClass": null, 2482 "fnRender": null, 2483 "bUseRendered": true, 2484 "iDataSort": oSettings.aoColumns.length-1, 2485 "sSortDataType": 'std', 2486 "nTh": nTh ? nTh : document.createElement('th'), 2487 "nTf": null, 2488 "anThExtra": [], 2489 "anTfExtra": [] 2490 }; 2491 2492 var iCol = oSettings.aoColumns.length-1; 2493 var oCol = oSettings.aoColumns[ iCol ]; 2494 2495 /* Add a column specific filter */ 2496 if ( typeof oSettings.aoPreSearchCols[ iCol ] == 'undefined' || 2497 oSettings.aoPreSearchCols[ iCol ] === null ) 2498 { 2499 oSettings.aoPreSearchCols[ iCol ] = { 2500 "sSearch": "", 2501 "bRegex": false, 2502 "bSmart": true 2503 }; 2504 } 2505 else 2506 {
2507 /* Don't require that the user must specify bRegex and / or bSmart */ 2508 if ( typeof oSettings.aoPreSearchCols[ iCol ].bRegex == 'undefined' ) 2509 { 2510 oSettings.aoPreSearchCols[ iCol ].bRegex = true; 2511 } 2512 2513 if ( typeof oSettings.aoPreSearchCols[ iCol ].bSmart == 'undefined' ) 2514 { 2515 oSettings.aoPreSearchCols[ iCol ].bSmart = true; 2516 } 2517 } 2518 2519 /* Use the column options function to initialise classes etc */ 2520 _fnColumnOptions( oSettings, iCol, null ); 2521 } 2522 2523 /* 2524 * Function: _fnColumnOptions 2525 * Purpose: Apply options for a column 2526 * Returns: - 2527 * Inputs: object:oSettings - dataTables settings object 2528 * int:iCol - column index to consider 2529 * object:oOptions - object with sType, bVisible and bSearchable 2530 */ 2531 function _fnColumnOptions( oSettings, iCol, oOptions ) 2532 { 2533 var oCol = oSettings.aoColumns[ iCol ]; 2534 2535 /* User specified column options */ 2536 if ( typeof oOptions != 'undefined' && oOptions !== null ) 2537 { 2538 if ( typeof oOptions.sType != 'undefined' ) 2539 { 2540 oCol.sType = oOptions.sType; 2541 oCol._bAutoType = false;
2542 } 2543 2544 _fnMap( oCol, oOptions, "bVisible" ); 2545 _fnMap( oCol, oOptions, "bSearchable" ); 2546 _fnMap( oCol, oOptions, "bSortable" ); 2547 _fnMap( oCol, oOptions, "sTitle" ); 2548 _fnMap( oCol, oOptions, "sName" ); 2549 _fnMap( oCol, oOptions, "sWidth" ); 2550 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); 2551 _fnMap( oCol, oOptions, "sClass" ); 2552 _fnMap( oCol, oOptions, "fnRender" ); 2553 _fnMap( oCol, oOptions, "bUseRendered" ); 2554 _fnMap( oCol, oOptions, "iDataSort" ); 2555 _fnMap( oCol, oOptions, "asSorting" ); 2556 _fnMap( oCol, oOptions, "sSortDataType" ); 2557 } 2558 2559 /* Feature sorting overrides column specific when off */ 2560 if ( !oSettings.oFeatures.bSort ) 2561 { 2562 oCol.bSortable = false; 2563 } 2564 2565 /* Check that the class assignment is correct for sorting */ 2566 if ( !oCol.bSortable || 2567 ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) ) 2568 { 2569 oCol.sSortingClass = oSettings.oClasses.sSortableNone; 2570 oCol.sSortingClassJUI = ""; 2571 } 2572 else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 ) 2573 { 2574 oCol.sSortingClass = oSettings.oClasses.sSortableAsc; 2575 oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed; 2576 } 2577 else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 ) 2578 { 2579 oCol.sSortingClass = oSettings.oClasses.sSortableDesc; 2580 oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed; 2581 } 2582 } 2583 2584 /* 2585 * Function: _fnAddData 2586 * Purpose: Add a data array to the table, creating DOM node etc 2587 * Returns: int: - >=0 if successful (index of new aoData entry), -1 if failed 2588 * Inputs: object:oSettings - dataTables settings object 2589 * array:aData - data array to be added 2590 * Notes: There are two basic methods for DataTables to get data to display - a JS array 2591 * (which is dealt with by this function), and the DOM, which has it's own optimised 2592 * function (_fnGatherData). Be careful to make the same changes here as there and vice-versa 2593 */ 2594 function _fnAddData ( oSettings, aDataSupplied ) 2595 { 2596 /* Sanity check the length of the new array */ 2597 if ( aDataSupplied.length != oSettings.aoColumns.length && 2598 oSettings.iDrawError != oSettings.iDraw ) 2599 { 2600 _fnLog( oSettings, 0, "Added data (size "+aDataSupplied.length+") does not match known "+ 2601 "number of columns ("+oSettings.aoColumns.length+")" ); 2602 oSettings.iDrawError = oSettings.iDraw; 2603 return -1; 2604 } 2605 2606 2607 /* Create the object for storing information about this new row */ 2608 var aData = aDataSupplied.slice(); 2609 var iThisIndex = oSettings.aoData.length; 2610 oSettings.aoData.push( { 2611 "nTr": document.createElement('tr'), 2612 "_iId": oSettings.iNextId++, 2613 "_aData": aData, 2614 "_anHidden": [], 2615 "_sRowStripe": '' 2616 } ); 2617 2618 /* Create the cells */ 2619 var nTd, sThisType; 2620 for ( var i=0 ; i<aData.length ; i++ ) 2621 { 2622 nTd = document.createElement('td'); 2623 2624 /* Allow null data (from a data array) - simply deal with it as a blank string */ 2625 if ( aData[i] === null ) 2626 { 2627 aData[i] = ''; 2628 } 2629 2630 if ( typeof oSettings.aoColumns[i].fnRender == 'function' ) 2631 { 2632 var sRendered = oSettings.aoColumns[i].fnRender( { 2633 "iDataRow": iThisIndex, 2634 "iDataColumn": i, 2635 "aData": aData, 2636 "oSettings": oSettings 2637 } ); 2638 nTd.innerHTML = sRendered;
2639 if ( oSettings.aoColumns[i].bUseRendered ) 2640 { 2641 /* Use the rendered data for filtering/sorting */ 2642 oSettings.aoData[iThisIndex]._aData[i] = sRendered; 2643 } 2644 } 2645 else 2646 { 2647 nTd.innerHTML = aData[i]; 2648 } 2649 2650 /* Cast everything as a string - so we can treat everything equally when sorting */ 2651 if ( typeof aData[i] != 'string' ) 2652 { 2653 aData[i] += ""; 2654 } 2655 aData[i] = $.trim(aData[i]); 2656 2657 /* Add user defined class */ 2658 if ( oSettings.aoColumns[i].sClass !== null ) 2659 { 2660 nTd.className = oSettings.aoColumns[i].sClass; 2661 } 2662 2663 /* See if we should auto-detect the column type */ 2664 if ( oSettings.aoColumns[i]._bAutoType && oSettings.aoColumns[i].sType != 'string' ) 2665 { 2666 /* Attempt to auto detect the type - same as _fnGatherData() */ 2667 sThisType = _fnDetectType( oSettings.aoData[iThisIndex]._aData[i] ); 2668 if ( oSettings.aoColumns[i].sType === null ) 2669 { 2670 oSettings.aoColumns[i].sType = sThisType; 2671 } 2672 else if ( oSettings.aoColumns[i].sType != sThisType ) 2673 { 2674 /* String is always the 'fallback' option */ 2675 oSettings.aoColumns[i].sType = 'string'; 2676 } 2677 } 2678 2679 if ( oSettings.aoColumns[i].bVisible ) 2680 { 2681 oSettings.aoData[iThisIndex].nTr.appendChild( nTd ); 2682 oSettings.aoData[iThisIndex]._anHidden[i] = null; 2683 } 2684 else 2685 { 2686 oSettings.aoData[iThisIndex]._anHidden[i] = nTd; 2687 } 2688 } 2689 2690 /* Add to the display array */ 2691 oSettings.aiDisplayMaster.push( iThisIndex ); 2692 return iThisIndex; 2693 } 2694 2695 /* 2696 * Function: _fnGatherData 2697 * Purpose: Read in the data from the target table from the DOM 2698 * Returns: - 2699 * Inputs: object:oSettings - dataTables settings object 2700 * Notes: This is a optimised version of _fnAddData (more or less) for reading information 2701 * from the DOM. The basic actions must be identical in the two functions. 2702 */ 2703 function _fnGatherData( oSettings ) 2704 { 2705 var iLoop, i, iLen, j, jLen, jInner, 2706 nTds, nTrs, nTd, aLocalData, iThisIndex, 2707 iRow, iRows, iColumn, iColumns; 2708 2709 /* 2710 * Process by row first 2711 * Add the data object for the whole table - storing the tr node. Note - no point in getting 2712 * DOM based data if we are going to go and replace it with Ajax source data. 2713 */ 2714 if ( oSettings.sAjaxSource === null ) 2715 { 2716 nTrs = oSettings.nTBody.childNodes; 2717 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 2718 { 2719 if ( nTrs[i].nodeName.toUpperCase() == "TR" ) 2720 { 2721 iThisIndex = oSettings.aoData.length; 2722 oSettings.aoData.push( { 2723 "nTr": nTrs[i], 2724 "_iId": oSettings.iNextId++, 2725 "_aData": [], 2726 "_anHidden": [], 2727 "_sRowStripe": '' 2728 } ); 2729 2730 oSettings.aiDisplayMaster.push( iThisIndex ); 2731 2732 aLocalData = oSettings.aoData[iThisIndex]._aData; 2733 nTds = nTrs[i].childNodes; 2734 jInner = 0; 2735 2736 for ( j=0, jLen=nTds.length ; j<jLen ; j++ ) 2737 { 2738 if ( nTds[j].nodeName.toUpperCase() == "TD" ) 2739 { 2740 aLocalData[jInner] = $.trim(nTds[j].innerHTML); 2741 jInner++; 2742 } 2743 } 2744 } 2745 } 2746 } 2747 2748 /* Gather in the TD elements of the Table - note that this is basically the same as 2749 * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet 2750 * setup! 2751 */ 2752 nTrs = _fnGetTrNodes( oSettings ); 2753 nTds = []; 2754 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 2755 { 2756 for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ ) 2757 { 2758 nTd = nTrs[i].childNodes[j]; 2759 if ( nTd.nodeName.toUpperCase() == "TD" ) 2760 { 2761 nTds.push( nTd ); 2762 } 2763 } 2764 } 2765 2766 /* Sanity check */ 2767 if ( nTds.length != nTrs.length * oSettings.aoColumns.length ) 2768 { 2769 _fnLog( oSettings, 1, "Unexpected number of TD elements. Expected "+ 2770 (nTrs.length * oSettings.aoColumns.length)+" and got "+nTds.length+". DataTables does "+ 2771 "not support rowspan / colspan in the table body, and there must be one cell for each "+ 2772 "row/column combination." ); 2773 } 2774 2775 /* Now process by column */ 2776 for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ ) 2777 { 2778 /* Get the title of the column - unless there is a user set one */ 2779 if ( oSettings.aoColumns[iColumn].sTitle === null ) 2780 { 2781 oSettings.aoColumns[iColumn].sTitle = oSettings.aoColumns[iColumn].nTh.innerHTML; 2782 } 2783 2784 var 2785 bAutoType = oSettings.aoColumns[iColumn]._bAutoType, 2786 bRender = typeof oSettings.aoColumns[iColumn].fnRender == 'function', 2787 bClass = oSettings.aoColumns[iColumn].sClass !== null, 2788 bVisible = oSettings.aoColumns[iColumn].bVisible, 2789 nCell, sThisType, sRendered; 2790 2791 /* A single loop to rule them all (and be more efficient) */ 2792 if ( bAutoType || bRender || bClass || !bVisible ) 2793 { 2794 for ( iRow=0, iRows=oSettings.aoData.length ; iRow<iRows ; iRow++ ) 2795 { 2796 nCell = nTds[ (iRow*iColumns) + iColumn ]; 2797 2798 /* Type detection */ 2799 if ( bAutoType ) 2800 { 2801 if ( oSettings.aoColumns[iColumn].sType != 'string' ) 2802 { 2803 sThisType = _fnDetectType( oSettings.aoData[iRow]._aData[iColumn] ); 2804 if ( oSettings.aoColumns[iColumn].sType === null ) 2805 { 2806 oSettings.aoColumns[iColumn].sType = sThisType; 2807 } 2808 else if ( oSettings.aoColumns[iColumn].sType != sThisType ) 2809 { 2810 /* String is always the 'fallback' option */ 2811 oSettings.aoColumns[iColumn].sType = 'string'; 2812 } 2813 } 2814 } 2815 2816 /* Rendering */ 2817 if ( bRender ) 2818 { 2819 sRendered = oSettings.aoColumns[iColumn].fnRender( { 2820 "iDataRow": iRow, 2821 "iDataColumn": iColumn, 2822 "aData": oSettings.aoData[iRow]._aData, 2823 "oSettings": oSettings 2824 } ); 2825 nCell.innerHTML = sRendered;
2826 if ( oSettings.aoColumns[iColumn].bUseRendered ) 2827 { 2828 /* Use the rendered data for filtering/sorting */ 2829 oSettings.aoData[iRow]._aData[iColumn] = sRendered; 2830 } 2831 } 2832 2833 /* Classes */ 2834 if ( bClass ) 2835 { 2836 nCell.className += ' '+oSettings.aoColumns[iColumn].sClass; 2837 } 2838 2839 2840 /* Column visability */ 2841 if ( !bVisible ) 2842 { 2843 oSettings.aoData[iRow]._anHidden[iColumn] = nCell; 2844 nCell.parentNode.removeChild( nCell ); 2845 } 2846 else 2847 { 2848 oSettings.aoData[iRow]._anHidden[iColumn] = null; 2849 } 2850 } 2851 } 2852 } 2853 } 2854 2855 2856 2857 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2858 * Section - Drawing functions 2859 */ 2860 2861 /* 2862 * Function: _fnDrawHead 2863 * Purpose: Create the HTML header for the table 2864 * Returns: - 2865 * Inputs: object:oSettings - dataTables settings object 2866 */ 2867 function _fnDrawHead( oSettings ) 2868 { 2869 var i, nTh, iLen, j, jLen; 2870 var anTr = oSettings.nTHead.getElementsByTagName('tr'); 2871 var iThs = oSettings.nTHead.getElementsByTagName('th').length; 2872 var iCorrector = 0; 2873 var jqChildren; 2874 2875 /* If there is a header in place - then use it - otherwise it's going to get nuked... */ 2876 if ( iThs !== 0 ) 2877 { 2878 /* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */ 2879 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 2880 { 2881 nTh = oSettings.aoColumns[i].nTh; 2882 2883 if ( oSettings.aoColumns[i].sClass !== null ) 2884 { 2885 $(nTh).addClass( oSettings.aoColumns[i].sClass ); 2886 } 2887 2888 /* Cache and remove (if needed) any extra elements for this column in the header */ 2889 for ( j=1, jLen=anTr.length ; j<jLen ; j++ ) 2890 { 2891 jqChildren = $(anTr[j]).children(); 2892 oSettings.aoColumns[i].anThExtra.push( jqChildren[i-iCorrector] ); 2893 if ( !oSettings.aoColumns[i].bVisible ) 2894 { 2895 anTr[j].removeChild( jqChildren[i-iCorrector] ); 2896 } 2897 } 2898 2899 if ( oSettings.aoColumns[i].bVisible ) 2900 { 2901 /* Set the title of the column if it is user defined (not what was auto detected) */ 2902 if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML ) 2903 { 2904 nTh.innerHTML = oSettings.aoColumns[i].sTitle; 2905 } 2906 } 2907 else 2908 { 2909 nTh.parentNode.removeChild( nTh ); 2910 iCorrector++; 2911 } 2912 } 2913 } 2914 else 2915 { 2916 /* We don't have a header in the DOM - so we are going to have to create one */ 2917 var nTr = document.createElement( "tr" ); 2918 2919 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 2920 { 2921 nTh = oSettings.aoColumns[i].nTh; 2922 nTh.innerHTML = oSettings.aoColumns[i].sTitle; 2923 2924 if ( oSettings.aoColumns[i].sClass !== null ) 2925 { 2926 $(nTh).addClass( oSettings.aoColumns[i].sClass ); 2927 } 2928 2929 if ( oSettings.aoColumns[i].bVisible ) 2930 { 2931 nTr.appendChild( nTh ); 2932 } 2933 } 2934 $(oSettings.nTHead).html( '' )[0].appendChild( nTr ); 2935 } 2936 2937 /* Add the extra markup needed by jQuery UI's themes */ 2938 if ( oSettings.bJUI ) 2939 { 2940 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 2941 { 2942 nTh = oSettings.aoColumns[i].nTh; 2943 2944 var nDiv = document.createElement('div'); 2945 nDiv.className = oSettings.oClasses.sSortJUIWrapper; 2946 $(nTh).contents().appendTo(nDiv); 2947 2948 nDiv.appendChild( document.createElement('span') ); 2949 nTh.appendChild( nDiv ); 2950 } 2951 } 2952 2953 /* Add sort listener */ 2954 var fnNoSelect = function (e) { 2955 this.onselectstart = function() { return false; }; 2956 return false; 2957 }; 2958 2959 if ( oSettings.oFeatures.bSort ) 2960 { 2961 for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) 2962 { 2963 if ( oSettings.aoColumns[i].bSortable !== false ) 2964 { 2965 _fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i ); 2966 2967 /* Take the brutal approach to cancelling text selection in header */ 2968 $(oSettings.aoColumns[i].nTh).bind( 'mousedown.DT', fnNoSelect ); 2969 } 2970 else 2971 { 2972 $(oSettings.aoColumns[i].nTh).addClass( oSettings.oClasses.sSortableNone ); 2973 } 2974 } 2975 } 2976 2977 /* Cache the footer elements */ 2978 if ( oSettings.nTFoot !== null ) 2979 { 2980 iCorrector = 0; 2981 anTr = oSettings.nTFoot.getElementsByTagName('tr'); 2982 var nTfs = anTr[0].getElementsByTagName('th'); 2983 2984 for ( i=0, iLen=nTfs.length ; i<iLen ; i++ ) 2985 { 2986 if ( typeof oSettings.aoColumns[i] != 'undefined' ) 2987 { 2988 oSettings.aoColumns[i].nTf = nTfs[i-iCorrector]; 2989 2990 if ( oSettings.oClasses.sFooterTH !== "" ) 2991 { 2992 oSettings.aoColumns[i].nTf.className += " "+oSettings.oClasses.sFooterTH; 2993 } 2994 2995 /* Deal with any extra elements for this column from the footer */ 2996 for ( j=1, jLen=anTr.length ; j<jLen ; j++ ) 2997 { 2998 jqChildren = $(anTr[j]).children(); 2999 oSettings.aoColumns[i].anTfExtra.push( jqChildren[i-iCorrector] ); 3000 if ( !oSettings.aoColumns[i].bVisible ) 3001 { 3002 anTr[j].removeChild( jqChildren[i-iCorrector] ); 3003 } 3004 } 3005 3006 if ( !oSettings.aoColumns[i].bVisible ) 3007 { 3008 nTfs[i-iCorrector].parentNode.removeChild( nTfs[i-iCorrector] ); 3009 iCorrector++; 3010 } 3011 } 3012 } 3013 } 3014 } 3015 3016 /* 3017 * Function: _fnDraw 3018 * Purpose: Insert the required TR nodes into the table for display 3019 * Returns: - 3020 * Inputs: object:oSettings - dataTables settings object 3021 */ 3022 function _fnDraw( oSettings ) 3023 { 3024 var i, iLen; 3025 var anRows = []; 3026 var iRowCount = 0; 3027 var bRowError = false;
3028 var iStrips = oSettings.asStripClasses.length; 3029 var iOpenRows = oSettings.aoOpenRows.length; 3030 3031 oSettings.bDrawing = true; 3032 3033 /* Check and see if we have an initial draw position from state saving */ 3034 if ( typeof oSettings.iInitDisplayStart != 'undefined' && oSettings.iInitDisplayStart != -1 ) 3035 { 3036 if ( oSettings.oFeatures.bServerSide ) 3037 { 3038 oSettings._iDisplayStart = oSettings.iInitDisplayStart; 3039 } 3040 else 3041 { 3042 oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ? 3043 0 : oSettings.iInitDisplayStart; 3044 } 3045 oSettings.iInitDisplayStart = -1; 3046 _fnCalculateEnd( oSettings ); 3047 } 3048 3049 /* If we are dealing with Ajax - do it here */ 3050 if ( !oSettings.bDestroying && oSettings.oFeatures.bServerSide && 3051 !_fnAjaxUpdate( oSettings ) ) 3052 { 3053 return; 3054 } 3055 else if ( !oSettings.oFeatures.bServerSide ) 3056 { 3057 oSettings.iDraw++; 3058 } 3059 3060 if ( oSettings.aiDisplay.length !== 0 ) 3061 { 3062 var iStart = oSettings._iDisplayStart; 3063 var iEnd = oSettings._iDisplayEnd; 3064 3065 if ( oSettings.oFeatures.bServerSide ) 3066 { 3067 iStart = 0; 3068 iEnd = oSettings.aoData.length; 3069 } 3070 3071 for ( var j=iStart ; j<iEnd ; j++ ) 3072 { 3073 var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ]; 3074 var nRow = aoData.nTr; 3075 3076 /* Remove the old stripping classes and then add the new one */ 3077 if ( iStrips !== 0 ) 3078 { 3079 var sStrip = oSettings.asStripClasses[ iRowCount % iStrips ]; 3080 if ( aoData._sRowStripe != sStrip ) 3081 { 3082 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStrip ); 3083 aoData._sRowStripe = sStrip; 3084 } 3085 } 3086 3087 /* Custom row callback function - might want to manipule the row */ 3088 if ( typeof oSettings.fnRowCallback == "function" ) 3089 { 3090 nRow = oSettings.fnRowCallback.call( oSettings.oInstance, nRow, 3091 oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j ); 3092 if ( !nRow && !bRowError ) 3093 { 3094 _fnLog( oSettings, 0, "A node was not returned by fnRowCallback" ); 3095 bRowError = true; 3096 } 3097 } 3098 3099 anRows.push( nRow ); 3100 iRowCount++; 3101 3102 /* If there is an open row - and it is attached to this parent - attach it on redraw */ 3103 if ( iOpenRows !== 0 ) 3104 { 3105 for ( var k=0 ; k<iOpenRows ; k++ ) 3106 { 3107 if ( nRow == oSettings.aoOpenRows[k].nParent ) 3108 { 3109 anRows.push( oSettings.aoOpenRows[k].nTr ); 3110 } 3111 } 3112 } 3113 } 3114 } 3115 else 3116 { 3117 /* Table is empty - create a row with an empty message in it */ 3118 anRows[ 0 ] = document.createElement( 'tr' ); 3119 3120 if ( typeof oSettings.asStripClasses[0] != 'undefined' ) 3121 { 3122 anRows[ 0 ].className = oSettings.asStripClasses[0]; 3123 } 3124 3125 var nTd = document.createElement( 'td' ); 3126 nTd.setAttribute( 'valign', "top" ); 3127 nTd.colSpan = _fnVisbleColumns( oSettings ); 3128 nTd.className = oSettings.oClasses.sRowEmpty; 3129 if ( typeof oSettings.oLanguage.sEmptyTable != 'undefined' && 3130 oSettings.fnRecordsTotal() === 0 ) 3131 { 3132 nTd.innerHTML = oSettings.oLanguage.sEmptyTable; 3133 } 3134 else 3135 { 3136 nTd.innerHTML = oSettings.oLanguage.sZeroRecords.replace( 3137 '_MAX_', oSettings.fnFormatNumber(oSettings.fnRecordsTotal()) ); 3138 } 3139 3140 anRows[ iRowCount ].appendChild( nTd ); 3141 } 3142 3143 /* Callback the header and footer custom funcation if there is one */ 3144 if ( typeof oSettings.fnHeaderCallback == 'function' ) 3145 { 3146 oSettings.fnHeaderCallback.call( oSettings.oInstance, $('>tr', oSettings.nTHead)[0], 3147 _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), 3148 oSettings.aiDisplay ); 3149 } 3150 3151 if ( typeof oSettings.fnFooterCallback == 'function' ) 3152 { 3153 oSettings.fnFooterCallback.call( oSettings.oInstance, $('>tr', oSettings.nTFoot)[0], 3154 _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), 3155 oSettings.aiDisplay ); 3156 } 3157 3158 /* 3159 * Need to remove any old row from the display - note we can't just empty the tbody using 3160 * $().html('') since this will unbind the jQuery event handlers (even although the node 3161 * still exists!) - equally we can't use innerHTML, since IE throws an exception. 3162 */ 3163 var 3164 nAddFrag = document.createDocumentFragment(), 3165 nRemoveFrag = document.createDocumentFragment(), 3166 nBodyPar, nTrs; 3167 3168 if ( oSettings.nTBody ) 3169 { 3170 nBodyPar = oSettings.nTBody.parentNode; 3171 nRemoveFrag.appendChild( oSettings.nTBody ); 3172 3173 /* When doing infinite scrolling, only remove child rows when sorting, filtering or start 3174 * up. When not infinite scroll, always do it. 3175 */ 3176 if ( !oSettings.oScroll.bInfinite || !oSettings._bInitComplete || 3177 oSettings.bSorted || oSettings.bFiltered ) 3178 { 3179 nTrs = oSettings.nTBody.childNodes; 3180 for ( i=nTrs.length-1 ; i>=0 ; i-- ) 3181 { 3182 nTrs[i].parentNode.removeChild( nTrs[i] ); 3183 } 3184 } 3185 3186 /* Put the draw table into the dom */ 3187 for ( i=0, iLen=anRows.length ; i<iLen ; i++ ) 3188 { 3189 nAddFrag.appendChild( anRows[i] ); 3190 } 3191 3192 oSettings.nTBody.appendChild( nAddFrag ); 3193 if ( nBodyPar !== null ) 3194 { 3195 nBodyPar.appendChild( oSettings.nTBody ); 3196 } 3197 } 3198 3199 /* Call all required callback functions for the end of a draw */
3200 for ( i=oSettings.aoDrawCallback.length-1 ; i>=0 ; i-- ) 3201 { 3202 oSettings.aoDrawCallback[i].fn.call( oSettings.oInstance, oSettings ); 3203 } 3204 3205 /* Draw is complete, sorting and filtering must be as well */ 3206 oSettings.bSorted = false; 3207 oSettings.bFiltered = false; 3208 oSettings.bDrawing = false; 3209 3210 if ( oSettings.oFeatures.bServerSide ) 3211 { 3212 _fnProcessingDisplay( oSettings, false ); 3213 if ( typeof oSettings._bInitComplete == 'undefined' ) 3214 { 3215 _fnInitComplete( oSettings ); 3216 } 3217 } 3218 } 3219 3220 /* 3221 * Function: _fnReDraw 3222 * Purpose: Redraw the table - taking account of the various features which are enabled 3223 * Returns: - 3224 * Inputs: object:oSettings - dataTables settings object 3225 */ 3226 function _fnReDraw( oSettings ) 3227 { 3228 if ( oSettings.oFeatures.bSort ) 3229 { 3230 /* Sorting will refilter and draw for us */ 3231 _fnSort( oSettings, oSettings.oPreviousSearch ); 3232 } 3233 else if ( oSettings.oFeatures.bFilter ) 3234 { 3235 /* Filtering will redraw for us */ 3236 _fnFilterComplete( oSettings, oSettings.oPreviousSearch ); 3237 } 3238 else 3239 { 3240 _fnCalculateEnd( oSettings ); 3241 _fnDraw( oSettings ); 3242 } 3243 } 3244 3245 /* 3246 * Function: _fnAjaxUpdate 3247 * Purpose: Update the table using an Ajax call 3248 * Returns: bool: block the table drawing or not 3249 * Inputs: object:oSettings - dataTables settings object 3250 */ 3251 function _fnAjaxUpdate( oSettings ) 3252 { 3253 if ( oSettings.bAjaxDataGet ) 3254 { 3255 _fnProcessingDisplay( oSettings, true ); 3256 var iColumns = oSettings.aoColumns.length; 3257 var aoData = []; 3258 var i; 3259 3260 /* Paging and general */ 3261 oSettings.iDraw++; 3262 aoData.push( { "name": "sEcho", "value": oSettings.iDraw } ); 3263 aoData.push( { "name": "iColumns", "value": iColumns } ); 3264 aoData.push( { "name": "sColumns", "value": _fnColumnOrdering(oSettings) } ); 3265 aoData.push( { "name": "iDisplayStart", "value": oSettings._iDisplayStart } ); 3266 aoData.push( { "name": "iDisplayLength", "value": oSettings.oFeatures.bPaginate !== false ? 3267 oSettings._iDisplayLength : -1 } ); 3268 3269 /* Filtering */ 3270 if ( oSettings.oFeatures.bFilter !== false ) 3271 { 3272 aoData.push( { "name": "sSearch", "value": oSettings.oPreviousSearch.sSearch } ); 3273 aoData.push( { "name": "bRegex", "value": oSettings.oPreviousSearch.bRegex } ); 3274 for ( i=0 ; i<iColumns ; i++ ) 3275 { 3276 aoData.push( { "name": "sSearch_"+i, "value": oSettings.aoPreSearchCols[i].sSearch } ); 3277 aoData.push( { "name": "bRegex_"+i, "value": oSettings.aoPreSearchCols[i].bRegex } ); 3278 aoData.push( { "name": "bSearchable_"+i, "value": oSettings.aoColumns[i].bSearchable } ); 3279 } 3280 } 3281 3282 /* Sorting */ 3283 if ( oSettings.oFeatures.bSort !== false ) 3284 { 3285 var iFixed = oSettings.aaSortingFixed !== null ? oSettings.aaSortingFixed.length : 0; 3286 var iUser = oSettings.aaSorting.length; 3287 aoData.push( { "name": "iSortingCols", "value": iFixed+iUser } ); 3288 for ( i=0 ; i<iFixed ; i++ ) 3289 { 3290 aoData.push( { "name": "iSortCol_"+i, "value": oSettings.aaSortingFixed[i][0] } ); 3291 aoData.push( { "name": "sSortDir_"+i, "value": oSettings.aaSortingFixed[i][1] } ); 3292 } 3293 3294 for ( i=0 ; i<iUser ; i++ ) 3295 { 3296 aoData.push( { "name": "iSortCol_"+(i+iFixed), "value": oSettings.aaSorting[i][0] } ); 3297 aoData.push( { "name": "sSortDir_"+(i+iFixed), "value": oSettings.aaSorting[i][1] } ); 3298 } 3299 3300 for ( i=0 ; i<iColumns ; i++ ) 3301 { 3302 aoData.push( { "name": "bSortable_"+i, "value": oSettings.aoColumns[i].bSortable } ); 3303 } 3304 } 3305 3306 oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData, 3307 function(json) { 3308 _fnAjaxUpdateDraw( oSettings, json ); 3309 } ); 3310 return false; 3311 } 3312 else 3313 { 3314 return true; 3315 } 3316 } 3317 3318 /* 3319 * Function: _fnAjaxUpdateDraw 3320 * Purpose: Data the data from the server (nuking the old) and redraw the table 3321 * Returns: - 3322 * Inputs: object:oSettings - dataTables settings object 3323 * object:json - json data return from the server. 3324 * The following must be defined: 3325 * iTotalRecords, iTotalDisplayRecords, aaData 3326 * The following may be defined: 3327 * sColumns 3328 */ 3329 function _fnAjaxUpdateDraw ( oSettings, json ) 3330 { 3331 if ( typeof json.sEcho != 'undefined' ) 3332 { 3333 /* Protect against old returns over-writing a new one. Possible when you get 3334 * very fast interaction, and later queires are completed much faster 3335 */ 3336 if ( json.sEcho*1 < oSettings.iDraw ) 3337 { 3338 return; 3339 } 3340 else 3341 { 3342 oSettings.iDraw = json.sEcho * 1; 3343 } 3344 } 3345 3346 if ( !oSettings.oScroll.bInfinite || 3347 (oSettings.oScroll.bInfinite && (oSettings.bSorted || oSettings.bFiltered)) ) 3348 { 3349 _fnClearTable( oSettings ); 3350 } 3351 oSettings._iRecordsTotal = json.iTotalRecords; 3352 oSettings._iRecordsDisplay = json.iTotalDisplayRecords; 3353 3354 /* Determine if reordering is required */ 3355 var sOrdering = _fnColumnOrdering(oSettings); 3356 var bReOrder = (typeof json.sColumns != 'undefined' && sOrdering !== "" && json.sColumns != sOrdering ); 3357 if ( bReOrder ) 3358 { 3359 var aiIndex = _fnReOrderIndex( oSettings, json.sColumns ); 3360 } 3361 3362 for ( var i=0, iLen=json.aaData.length ; i<iLen ; i++ ) 3363 { 3364 if ( bReOrder ) 3365 { 3366 /* If we need to re-order, then create a new array with the correct order and add it */ 3367 var aData = [];
3368 for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ ) 3369 { 3370 aData.push( json.aaData[i][ aiIndex[j] ] ); 3371 } 3372 _fnAddData( oSettings, aData ); 3373 } 3374 else 3375 { 3376 /* No re-order required, sever got it "right" - just straight add */ 3377 _fnAddData( oSettings, json.aaData[i] ); 3378 } 3379 } 3380 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 3381 3382 oSettings.bAjaxDataGet = false; 3383 _fnDraw( oSettings ); 3384 oSettings.bAjaxDataGet = true; 3385 _fnProcessingDisplay( oSettings, false ); 3386 } 3387 3388 3389 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 3390 * Section - Options (features) HTML 3391 */ 3392 3393 /* 3394 * Function: _fnAddOptionsHtml 3395 * Purpose: Add the options to the page HTML for the table 3396 * Returns: - 3397 * Inputs: object:oSettings - dataTables settings object 3398 */ 3399 function _fnAddOptionsHtml ( oSettings ) 3400 { 3401 /* 3402 * Create a temporary, empty, div which we can later on replace with what we have generated 3403 * we do it this way to rendering the 'options' html offline - speed :-) 3404 */ 3405 var nHolding = document.createElement( 'div' ); 3406 oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable ); 3407 3408 /* 3409 * All DataTables are wrapped in a div - this is not currently optional - backwards 3410 * compatability. It can be removed if you don't want it. 3411 */ 3412 oSettings.nTableWrapper = document.createElement( 'div' ); 3413 oSettings.nTableWrapper.className = oSettings.oClasses.sWrapper; 3414 if ( oSettings.sTableId !== '' ) 3415 { 3416 oSettings.nTableWrapper.setAttribute( 'id', oSettings.sTableId+'_wrapper' ); 3417 } 3418 3419 /* Track where we want to insert the option */ 3420 var nInsertNode = oSettings.nTableWrapper; 3421 3422 /* Loop over the user set positioning and place the elements as needed */ 3423 var aDom = oSettings.sDom.split(''); 3424 var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j; 3425 for ( var i=0 ; i<aDom.length ; i++ ) 3426 { 3427 iPushFeature = 0; 3428 cOption = aDom[i]; 3429 3430 if ( cOption == '<' ) 3431 { 3432 /* New container div */ 3433 nNewNode = document.createElement( 'div' ); 3434 3435 /* Check to see if we should append an id and/or a class name to the container */ 3436 cNext = aDom[i+1]; 3437 if ( cNext == "'" || cNext == '"' ) 3438 { 3439 sAttr = ""; 3440 j = 2; 3441 while ( aDom[i+j] != cNext ) 3442 { 3443 sAttr += aDom[i+j]; 3444 j++; 3445 } 3446 3447 /* Replace jQuery UI constants */ 3448 if ( sAttr == "H" ) 3449 { 3450 sAttr = "fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix"; 3451 } 3452 else if ( sAttr == "F" ) 3453 { 3454 sAttr = "fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix"; 3455 } 3456 3457 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic 3458 * breaks the string into parts and applies them as needed 3459 */ 3460 if ( sAttr.indexOf('.') != -1 ) 3461 { 3462 var aSplit = sAttr.split('.'); 3463 nNewNode.setAttribute('id', aSplit[0].substr(1, aSplit[0].length-1) ); 3464 nNewNode.className = aSplit[1]; 3465 } 3466 else if ( sAttr.charAt(0) == "#" ) 3467 { 3468 nNewNode.setAttribute('id', sAttr.substr(1, sAttr.length-1) ); 3469 } 3470 else 3471 { 3472 nNewNode.className = sAttr; 3473 } 3474 3475 i += j; /* Move along the position array */ 3476 } 3477 3478 nInsertNode.appendChild( nNewNode ); 3479 nInsertNode = nNewNode; 3480 } 3481 else if ( cOption == '>' ) 3482 { 3483 /* End container div */
3484 nInsertNode = nInsertNode.parentNode; 3485 } 3486 else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange ) 3487 { 3488 /* Length */ 3489 nTmp = _fnFeatureHtmlLength( oSettings ); 3490 iPushFeature = 1; 3491 } 3492 else if ( cOption == 'f' && oSettings.oFeatures.bFilter ) 3493 { 3494 /* Filter */ 3495 nTmp = _fnFeatureHtmlFilter( oSettings ); 3496 iPushFeature = 1; 3497 } 3498 else if ( cOption == 'r' && oSettings.oFeatures.bProcessing ) 3499 { 3500 /* pRocessing */ 3501 nTmp = _fnFeatureHtmlProcessing( oSettings ); 3502 iPushFeature = 1; 3503 } 3504 else if ( cOption == 't' ) 3505 { 3506 /* Table */ 3507 nTmp = _fnFeatureHtmlTable( oSettings ); 3508 iPushFeature = 1; 3509 } 3510 else if ( cOption == 'i' && oSettings.oFeatures.bInfo ) 3511 { 3512 /* Info */ 3513 nTmp = _fnFeatureHtmlInfo( oSettings ); 3514 iPushFeature = 1; 3515 } 3516 else if ( cOption == 'p' && oSettings.oFeatures.bPaginate ) 3517 { 3518 /* Pagination */ 3519 nTmp = _fnFeatureHtmlPaginate( oSettings ); 3520 iPushFeature = 1; 3521 } 3522 else if ( _oExt.aoFeatures.length !== 0 ) 3523 { 3524 /* Plug-in features */ 3525 var aoFeatures = _oExt.aoFeatures; 3526 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ ) 3527 { 3528 if ( cOption == aoFeatures[k].cFeature ) 3529 { 3530 nTmp = aoFeatures[k].fnInit( oSettings ); 3531 if ( nTmp ) 3532 { 3533 iPushFeature = 1; 3534 } 3535 break; 3536 } 3537 } 3538 } 3539 3540 /* Add to the 2D features array */ 3541 if ( iPushFeature == 1 && nTmp !== null ) 3542 { 3543 if ( typeof oSettings.aanFeatures[cOption] != 'object' ) 3544 { 3545 oSettings.aanFeatures[cOption] = []; 3546 } 3547 oSettings.aanFeatures[cOption].push( nTmp ); 3548 nInsertNode.appendChild( nTmp ); 3549 } 3550 } 3551 3552 /* Built our DOM structure - replace the holding div with what we want */ 3553 nHolding.parentNode.replaceChild( oSettings.nTableWrapper, nHolding ); 3554 } 3555 3556 3557 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 3558 * Section - Feature: Filtering 3559 */ 3560 3561 /* 3562 * Function: _fnFeatureHtmlTable 3563 * Purpose: Add any control elements for the table - specifically scrolling 3564 * Returns: node: - Node to add to the DOM 3565 * Inputs: object:oSettings - dataTables settings object 3566 */ 3567 function _fnFeatureHtmlTable ( oSettings ) 3568 { 3569 /* Chack if scrolling is enabled or not - if not then leave the DOM unaltered */ 3570 if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" ) 3571 { 3572 return oSettings.nTable; 3573 } 3574 3575 /* 3576 * The HTML structure that we want to generate in this function is: 3577 * div - nScroller 3578 * div - nScrollHead 3579 * div - nScrollHeadInner 3580 * table - nScrollHeadTable 3581 * thead - nThead 3582 * div - nScrollBody 3583 * table - oSettings.nTable 3584 * thead - nTheadSize 3585 * tbody - nTbody 3586 * div - nScrollFoot 3587 * div - nScrollFootInner 3588 * table - nScrollFootTable 3589 * tfoot - nTfoot 3590 */ 3591 var 3592 nScroller = document.createElement('div'), 3593 nScrollHead = document.createElement('div'), 3594 nScrollHeadInner = document.createElement('div'), 3595 nScrollBody = document.createElement('div'), 3596 nScrollFoot = document.createElement('div'), 3597 nScrollFootInner = document.createElement('div'), 3598 nScrollHeadTable = oSettings.nTable.cloneNode(false), 3599 nScrollFootTable = oSettings.nTable.cloneNode(false), 3600 nThead = oSettings.nTable.getElementsByTagName('thead')[0], 3601 nTfoot = oSettings.nTable.getElementsByTagName('tfoot').length === 0 ? null : 3602 oSettings.nTable.getElementsByTagName('tfoot')[0], 3603 oClasses = (typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI) ? 3604 _oExt.oJUIClasses : _oExt.oStdClasses; 3605 3606 nScrollHead.appendChild( nScrollHeadInner ); 3607 nScrollFoot.appendChild( nScrollFootInner ); 3608 nScrollBody.appendChild( oSettings.nTable ); 3609 nScroller.appendChild( nScrollHead ); 3610 nScroller.appendChild( nScrollBody ); 3611 nScrollHeadInner.appendChild( nScrollHeadTable ); 3612 nScrollHeadTable.appendChild( nThead ); 3613 if ( nTfoot !== null ) 3614 { 3615 nScroller.appendChild( nScrollFoot ); 3616 nScrollFootInner.appendChild( nScrollFootTable ); 3617 nScrollFootTable.appendChild( nTfoot ); 3618 } 3619
3620 nScroller.className = oClasses.sScrollWrapper; 3621 nScrollHead.className = oClasses.sScrollHead; 3622 nScrollHeadInner.className = oClasses.sScrollHeadInner; 3623 nScrollBody.className = oClasses.sScrollBody; 3624 nScrollFoot.className = oClasses.sScrollFoot; 3625 nScrollFootInner.className = oClasses.sScrollFootInner; 3626 3627 if ( oSettings.oScroll.bAutoCss ) 3628 { 3629 nScrollHead.style.overflow = "hidden"; 3630 nScrollHead.style.position = "relative"; 3631 nScrollFoot.style.overflow = "hidden"; 3632 nScrollBody.style.overflow = "auto"; 3633 } 3634 3635 nScrollHead.style.border = "0"; 3636 nScrollHead.style.width = "100%"; 3637 nScrollFoot.style.border = "0"; 3638 nScrollHeadInner.style.width = "150%"; /* will be overwritten */ 3639 3640 /* Modify attributes to respect the clones */ 3641 nScrollHeadTable.removeAttribute('id'); 3642 nScrollHeadTable.style.marginLeft = "0"; 3643 oSettings.nTable.style.marginLeft = "0"; 3644 if ( nTfoot !== null ) 3645 { 3646 nScrollFootTable.removeAttribute('id'); 3647 nScrollFootTable.style.marginLeft = "0"; 3648 } 3649 3650 /* Move any caption elements from the body to the header */ 3651 var nCaptions = $('>caption', oSettings.nTable); 3652 for ( var i=0, iLen=nCaptions.length ; i<iLen ; i++ ) 3653 { 3654 nScrollHeadTable.appendChild( nCaptions[i] ); 3655 } 3656 3657 /* 3658 * Sizing 3659 */ 3660 /* When xscrolling add the width and a scroller to move the header with the body */ 3661 if ( oSettings.oScroll.sX !== "" ) 3662 { 3663 nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX ); 3664 nScrollBody.style.width = _fnStringToCss( oSettings.oScroll.sX ); 3665 3666 if ( nTfoot !== null ) 3667 { 3668 nScrollFoot.style.width = _fnStringToCss( oSettings.oScroll.sX ); 3669 } 3670 3671 /* When the body is scrolled, then we also want to scroll the headers */ 3672 $(nScrollBody).scroll( function (e) { 3673 nScrollHead.scrollLeft = this.scrollLeft; 3674 3675 if ( nTfoot !== null ) 3676 { 3677 nScrollFoot.scrollLeft = this.scrollLeft; 3678 } 3679 } ); 3680 } 3681 3682 /* When yscrolling, add the height */ 3683 if ( oSettings.oScroll.sY !== "" ) 3684 { 3685 nScrollBody.style.height = _fnStringToCss( oSettings.oScroll.sY ); 3686 } 3687 3688 /* Redraw - align columns across the tables */ 3689 oSettings.aoDrawCallback.push( { 3690 "fn": _fnScrollDraw, 3691 "sName": "scrolling" 3692 } ); 3693 3694 /* Infinite scrolling event handlers */ 3695 if ( oSettings.oScroll.bInfinite ) 3696 { 3697 $(nScrollBody).scroll( function() { 3698 /* Use a blocker to stop scrolling from loading more data while other data is still loading */ 3699 if ( !oSettings.bDrawing ) 3700 { 3701 /* Check if we should load the next data set */ 3702 if ( $(this).scrollTop() + $(this).height() > 3703 $(oSettings.nTable).height() - oSettings.oScroll.iLoadGap ) 3704 { 3705 /* Only do the redraw if we have to - we might be at the end of the data */ 3706 if ( oSettings.fnDisplayEnd() < oSettings.fnRecordsDisplay() ) 3707 { 3708 _fnPageChange( oSettings, 'next' ); 3709 _fnCalculateEnd( oSettings ); 3710 _fnDraw( oSettings ); 3711 } 3712 } 3713 } 3714 } ); 3715 } 3716 3717 oSettings.nScrollHead = nScrollHead; 3718 oSettings.nScrollFoot = nScrollFoot; 3719 3720 return nScroller; 3721 } 3722 3723 /* 3724 * Function: _fnScrollDraw 3725 * Purpose: Update the various tables for resizing 3726 * Returns: node: - Node to add to the DOM 3727 * Inputs: object:o - dataTables settings object 3728 * Notes: It's a bit of a pig this function, but basically the idea to: 3729 * 1. Re-create the table inside the scrolling div 3730 * 2. Take live measurements from the DOM 3731 * 3. Apply the measurements 3732 * 4. Clean up 3733 */ 3734 function _fnScrollDraw ( o ) 3735 { 3736 var 3737 nScrollHeadInner = o.nScrollHead.getElementsByTagName('div')[0], 3738 nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0], 3739 nScrollBody = o.nTable.parentNode, 3740 i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis, 3741 iWidth, aApplied=[], iSanityWidth; 3742 3743 /* 3744 * 1. Re-create the table inside the scrolling div 3745 */ 3746 3747 /* Remove the old minimised thead and tfoot elements in the inner table */ 3748 var nTheadSize = o.nTable.getElementsByTagName('thead'); 3749 if ( nTheadSize.length > 0 ) 3750 { 3751 o.nTable.removeChild( nTheadSize[0] ); 3752 } 3753 3754 if ( o.nTFoot !== null ) 3755 { 3756 /* Remove the old minimised footer element in the cloned header */ 3757 var nTfootSize = o.nTable.getElementsByTagName('tfoot'); 3758 if ( nTfootSize.length > 0 ) 3759 { 3760 o.nTable.removeChild( nTfootSize[0] ); 3761 } 3762 } 3763 3764 /* Clone the current header and footer elements and then place it into the inner table */ 3765 nTheadSize = o.nTHead.cloneNode(true); 3766 o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] ); 3767 3768 if ( o.nTFoot !== null ) 3769 { 3770 nTfootSize = o.nTFoot.cloneNode(true); 3771 o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] ); 3772 } 3773 3774 /* 3775 * 2. Take live measurements from the DOM - do not alter the DOM itself! 3776 */ 3777 3778 /* Remove old sizing and apply the calculated column widths 3779 * Get the unique column headers in the newly created (cloned) header. We want to apply the 3780 * calclated sizes to this header 3781 */ 3782 var nThs = _fnGetUniqueThs( nTheadSize ); 3783 for ( i=0, iLen=nThs.length ; i<iLen ; i++ ) 3784 { 3785 iVis = _fnVisibleToColumnIndex( o, i ); 3786 nThs[i].style.width = o.aoColumns[iVis].sWidth; 3787 } 3788 3789 if ( o.nTFoot !== null ) 3790 { 3791 _fnApplyToChildren( function(n) { 3792 n.style.width = ""; 3793 }, nTfootSize.getElementsByTagName('tr') ); 3794 } 3795 3796 /* Size the table as a whole */ 3797 iSanityWidth = $(o.nTable).outerWidth(); 3798 if ( o.oScroll.sX === "" ) 3799 { 3800 /* No x scrolling */ 3801 o.nTable.style.width = "100%"; 3802 3803 /* I know this is rubbish - but IE7 will make the width of the table when 100% include
3804 * the scrollbar - which is shouldn't. This needs feature detection in future - to do 3805 */ 3806 if ( $.browser.msie && $.browser.version <= 7 ) 3807 { 3808 o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth()-o.oScroll.iBarWidth ); 3809 } 3810 } 3811 else 3812 { 3813 if ( o.oScroll.sXInner !== "" ) 3814 { 3815 /* x scroll inner has been given - use it */ 3816 o.nTable.style.width = _fnStringToCss(o.oScroll.sXInner); 3817 } 3818 else if ( iSanityWidth == $(nScrollBody).width() && 3819 $(nScrollBody).height() < $(o.nTable).height() ) 3820 { 3821 /* There is y-scrolling - try to take account of the y scroll bar */ 3822 o.nTable.style.width = _fnStringToCss( iSanityWidth-o.oScroll.iBarWidth ); 3823 if ( $(o.nTable).outerWidth() > iSanityWidth-o.oScroll.iBarWidth ) 3824 { 3825 /* Not possible to take account of it */ 3826 o.nTable.style.width = _fnStringToCss( iSanityWidth ); 3827 } 3828 } 3829 else 3830 { 3831 /* All else fails */ 3832 o.nTable.style.width = _fnStringToCss( iSanityWidth ); 3833 } 3834 } 3835 3836 /* Recalculate the sanity width - now that we've applied the required width, before it was 3837 * a temporary variable. This is required because the column width calculation is done 3838 * before this table DOM is created. 3839 */ 3840 iSanityWidth = $(o.nTable).outerWidth(); 3841 3842 /* We want the hidden header to have zero height, so remove padding and borders. Then 3843 * set the width based on the real headers 3844 */ 3845 anHeadToSize = o.nTHead.getElementsByTagName('tr'); 3846 anHeadSizers = nTheadSize.getElementsByTagName('tr'); 3847 3848 _fnApplyToChildren( function(nSizer, nToSize) { 3849 oStyle = nSizer.style; 3850 oStyle.paddingTop = "0"; 3851 oStyle.paddingBottom = "0"; 3852 oStyle.borderTopWidth = "0"; 3853 oStyle.borderBottomWidth = "0"; 3854 oStyle.height = 0; 3855 3856 iWidth = $(nSizer).width(); 3857 nToSize.style.width = _fnStringToCss( iWidth ); 3858 aApplied.push( iWidth ); 3859 }, anHeadSizers, anHeadToSize ); 3860 $(anHeadSizers).height(0); 3861 3862 if ( o.nTFoot !== null ) 3863 { 3864 /* Clone the current footer and then place it into the body table as a "hidden header" */ 3865 anFootSizers = nTfootSize.getElementsByTagName('tr'); 3866 anFootToSize = o.nTFoot.getElementsByTagName('tr'); 3867 3868 _fnApplyToChildren( function(nSizer, nToSize) { 3869 oStyle = nSizer.style; 3870 oStyle.paddingTop = "0"; 3871 oStyle.paddingBottom = "0"; 3872 oStyle.borderTopWidth = "0"; 3873 oStyle.borderBottomWidth = "0"; 3874 oStyle.height = 0; 3875 3876 iWidth = $(nSizer).width(); 3877 nToSize.style.width = _fnStringToCss( iWidth ); 3878 aApplied.push( iWidth ); 3879 }, anFootSizers, anFootToSize ); 3880 $(anFootSizers).height(0); 3881 } 3882 3883 /* 3884 * 3. Apply the measurements 3885 */ 3886 3887 /* "Hide" the header and footer that we used for the sizing. We want to also fix their width 3888 * to what they currently are 3889 */ 3890 _fnApplyToChildren( function(nSizer) { 3891 nSizer.innerHTML = ""; 3892 nSizer.style.width = _fnStringToCss( aApplied.shift() ); 3893 }, anHeadSizers ); 3894 3895 if ( o.nTFoot !== null ) 3896 { 3897 _fnApplyToChildren( function(nSizer) { 3898 nSizer.innerHTML = ""; 3899 nSizer.style.width = _fnStringToCss( aApplied.shift() ); 3900 }, anFootSizers ); 3901 } 3902 3903 /* Sanity check that the table is of a sensible width. If not then we are going to get 3904 * misalignment 3905 */ 3906 if ( $(o.nTable).outerWidth() < iSanityWidth ) 3907 { 3908 if ( o.oScroll.sX === "" ) 3909 { 3910 _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+ 3911 " misalignment. It is suggested that you enable x-scrolling or increase the width"+ 3912 " the table has in which to be drawn" ); 3913 } 3914 else if ( o.oScroll.sXInner !== "" ) 3915 { 3916 _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+ 3917 " misalignment. It is suggested that you increase the sScrollXInner property to"+ 3918 " allow it to draw in a larger area, or simply remove that parameter to allow"+ 3919 " automatic calculation" ); 3920 } 3921 } 3922 3923 3924 /* 3925 * 4. Clean up 3926 */ 3927 3928 if ( o.oScroll.sY === "" ) 3929 { 3930 /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting 3931 * the scrollbar height from the visible display, rather than adding it on. We need to 3932 * set the height in order to sort this. Don't want to do it in any other browsers. 3933 */ 3934 if ( $.browser.msie && $.browser.version <= 7 ) 3935 { 3936 nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+o.oScroll.iBarWidth ); 3937 } 3938 } 3939 3940 if ( o.oScroll.sY !== "" && o.oScroll.bCollapse ) 3941 { 3942 nScrollBody.style.height = _fnStringToCss( o.oScroll.sY ); 3943 3944 var iExtra = (o.oScroll.sX !== "" && o.nTable.offsetWidth > nScrollBody.offsetWidth) ? 3945 o.oScroll.iBarWidth : 0; 3946 if ( o.nTable.offsetHeight < nScrollBody.offsetHeight ) 3947 { 3948 nScrollBody.style.height = _fnStringToCss( $(o.nTable).height()+iExtra ); 3949 } 3950 } 3951 3952 /* Finally set the width's of the header and footer tables */ 3953 var iOuterWidth = $(o.nTable).outerWidth(); 3954 nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth ); 3955 nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth+o.oScroll.iBarWidth ); 3956 3957 if ( o.nTFoot !== null ) 3958 { 3959 var 3960 nScrollFootInner = o.nScrollFoot.getElementsByTagName('div')[0], 3961 nScrollFootTable = nScrollFootInner.getElementsByTagName('table')[0]; 3962 3963 nScrollFootInner.style.width = _fnStringToCss( o.nTable.offsetWidth+o.oScroll.iBarWidth ); 3964 nScrollFootTable.style.width = _fnStringToCss( o.nTable.offsetWidth ); 3965 } 3966 3967 /* If sorting or filtering has occured, jump the scrolling back to the top */ 3968 if ( o.bSorted || o.bFiltered ) 3969 { 3970 nScrollBody.scrollTop = 0; 3971 } 3972 } 3973 3974 /* 3975 * Function: _fnAjustColumnSizing 3976 * Purpose: Ajust the table column widths for new data 3977 * Returns: - 3978 * Inputs: object:oSettings - dataTables settings object 3979 * Notes: You would probably want to do a redraw after calling this function! 3980 */ 3981 function _fnAjustColumnSizing ( oSettings ) 3982 { 3983 /* Not interested in doing column width calculation if autowidth is disabled */ 3984 if ( oSettings.oFeatures.bAutoWidth === false ) 3985 { 3986 return false;
3987 } 3988 3989 _fnCalculateColumnWidths( oSettings ); 3990 for ( var i=0 , iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 3991 { 3992 oSettings.aoColumns[i].nTh.style.width = oSettings.aoColumns[i].sWidth; 3993 } 3994 } 3995 3996 3997 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 3998 * Section - Feature: Filtering 3999 */ 4000 4001 /* 4002 * Function: _fnFeatureHtmlFilter 4003 * Purpose: Generate the node required for filtering text 4004 * Returns: node 4005 * Inputs: object:oSettings - dataTables settings object 4006 */ 4007 function _fnFeatureHtmlFilter ( oSettings ) 4008 { 4009 var nFilter = document.createElement( 'div' ); 4010 if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.f == "undefined" ) 4011 { 4012 nFilter.setAttribute( 'id', oSettings.sTableId+'_filter' ); 4013 } 4014 nFilter.className = oSettings.oClasses.sFilter; 4015 var sSpace = oSettings.oLanguage.sSearch==="" ? "" : " "; 4016 nFilter.innerHTML = oSettings.oLanguage.sSearch+sSpace+'<input type="text" />'; 4017 4018 var jqFilter = $("input", nFilter); 4019 jqFilter.val( oSettings.oPreviousSearch.sSearch.replace('"','"') ); 4020 jqFilter.bind( 'keyup.DT', function(e) { 4021 /* Update all other filter input elements for the new display */ 4022 var n = oSettings.aanFeatures.f; 4023 for ( var i=0, iLen=n.length ; i<iLen ; i++ ) 4024 { 4025 if ( n[i] != this.parentNode ) 4026 { 4027 $('input', n[i]).val( this.value ); 4028 } 4029 } 4030 4031 /* Now do the filter */ 4032 if ( this.value != oSettings.oPreviousSearch.sSearch ) 4033 { 4034 _fnFilterComplete( oSettings, { 4035 "sSearch": this.value, 4036 "bRegex": oSettings.oPreviousSearch.bRegex, 4037 "bSmart": oSettings.oPreviousSearch.bSmart 4038 } ); 4039 } 4040 } ); 4041 4042 jqFilter.bind( 'keypress.DT', function(e) { 4043 /* Prevent default */ 4044 if ( e.keyCode == 13 ) 4045 { 4046 return false; 4047 } 4048 } ); 4049 4050 return nFilter; 4051 } 4052 4053 /* 4054 * Function: _fnFilterComplete 4055 * Purpose: Filter the table using both the global filter and column based filtering 4056 * Returns: - 4057 * Inputs: object:oSettings - dataTables settings object 4058 * object:oSearch: search information 4059 * int:iForce - optional - force a research of the master array (1) or not (undefined or 0) 4060 */ 4061 function _fnFilterComplete ( oSettings, oInput, iForce ) 4062 { 4063 /* Filter on everything */ 4064 _fnFilter( oSettings, oInput.sSearch, iForce, oInput.bRegex, oInput.bSmart ); 4065 4066 /* Now do the individual column filter */ 4067 for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ ) 4068 { 4069 _fnFilterColumn( oSettings, oSettings.aoPreSearchCols[i].sSearch, i, 4070 oSettings.aoPreSearchCols[i].bRegex, oSettings.aoPreSearchCols[i].bSmart ); 4071 } 4072 4073 /* Custom filtering */ 4074 if ( _oExt.afnFiltering.length !== 0 ) 4075 { 4076 _fnFilterCustom( oSettings ); 4077 } 4078 4079 /* Tell the draw function we have been filtering */ 4080 oSettings.bFiltered = true; 4081 4082 /* Redraw the table */ 4083 oSettings._iDisplayStart = 0; 4084 _fnCalculateEnd( oSettings ); 4085 _fnDraw( oSettings ); 4086 4087 /* Rebuild search array 'offline' */ 4088 _fnBuildSearchArray( oSettings, 0 ); 4089 } 4090 4091 /* 4092 * Function: _fnFilterCustom 4093 * Purpose: Apply custom filtering functions 4094 * Returns: - 4095 * Inputs: object:oSettings - dataTables settings object 4096 */ 4097 function _fnFilterCustom( oSettings ) 4098 { 4099 var afnFilters = _oExt.afnFiltering; 4100 for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ ) 4101 { 4102 var iCorrector = 0; 4103 for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ ) 4104 { 4105 var iDisIndex = oSettings.aiDisplay[j-iCorrector]; 4106 4107 /* Check if we should use this row based on the filtering function */ 4108 if ( !afnFilters[i]( oSettings, oSettings.aoData[iDisIndex]._aData, iDisIndex ) ) 4109 { 4110 oSettings.aiDisplay.splice( j-iCorrector, 1 ); 4111 iCorrector++; 4112 } 4113 } 4114 } 4115 } 4116 4117 /* 4118 * Function: _fnFilterColumn 4119 * Purpose: Filter the table on a per-column basis 4120 * Returns: - 4121 * Inputs: object:oSettings - dataTables settings object 4122 * string:sInput - string to filter on 4123 * int:iColumn - column to filter 4124 * bool:bRegex - treat search string as a regular expression or not 4125 * bool:bSmart - use smart filtering or not 4126 */ 4127 function _fnFilterColumn ( oSettings, sInput, iColumn, bRegex, bSmart ) 4128 { 4129 if ( sInput === "" ) 4130 { 4131 return; 4132 } 4133 4134 var iIndexCorrector = 0; 4135 var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart ); 4136 4137 for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- ) 4138 { 4139 var sData = _fnDataToSearch( oSettings.aoData[ oSettings.aiDisplay[i] ]._aData[iColumn], 4140 oSettings.aoColumns[iColumn].sType ); 4141 if ( ! rpSearch.test( sData ) ) 4142 { 4143 oSettings.aiDisplay.splice( i, 1 ); 4144 iIndexCorrector++; 4145 } 4146 } 4147 } 4148 4149 /* 4150 * Function: _fnFilter 4151 * Purpose: Filter the data table based on user input and draw the table 4152 * Returns: - 4153 * Inputs: object:oSettings - dataTables settings object
4154 * string:sInput - string to filter on 4155 * int:iForce - optional - force a research of the master array (1) or not (undefined or 0) 4156 * bool:bRegex - treat as a regular expression or not 4157 * bool:bSmart - perform smart filtering or not 4158 */ 4159 function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart ) 4160 { 4161 var i; 4162 var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart ); 4163 4164 /* Check if we are forcing or not - optional parameter */ 4165 if ( typeof iForce == 'undefined' || iForce === null ) 4166 { 4167 iForce = 0; 4168 } 4169 4170 /* Need to take account of custom filtering functions - always filter */ 4171 if ( _oExt.afnFiltering.length !== 0 ) 4172 { 4173 iForce = 1; 4174 } 4175 4176 /* 4177 * If the input is blank - we want the full data set 4178 */ 4179 if ( sInput.length <= 0 ) 4180 { 4181 oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); 4182 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 4183 } 4184 else 4185 { 4186 /* 4187 * We are starting a new search or the new search string is smaller 4188 * then the old one (i.e. delete). Search from the master array 4189 */ 4190 if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length || 4191 oSettings.oPreviousSearch.sSearch.length > sInput.length || iForce == 1 || 4192 sInput.indexOf(oSettings.oPreviousSearch.sSearch) !== 0 ) 4193 { 4194 /* Nuke the old display array - we are going to rebuild it */ 4195 oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length); 4196 4197 /* Force a rebuild of the search array */ 4198 _fnBuildSearchArray( oSettings, 1 ); 4199 4200 /* Search through all records to populate the search array 4201 * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1 4202 * mapping 4203 */ 4204 for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ ) 4205 { 4206 if ( rpSearch.test(oSettings.asDataSearch[i]) ) 4207 { 4208 oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] ); 4209 } 4210 } 4211 } 4212 else 4213 { 4214 /* Using old search array - refine it - do it this way for speed 4215 * Don't have to search the whole master array again 4216 */ 4217 var iIndexCorrector = 0; 4218 4219 /* Search the current results */ 4220 for ( i=0 ; i<oSettings.asDataSearch.length ; i++ ) 4221 { 4222 if ( ! rpSearch.test(oSettings.asDataSearch[i]) ) 4223 { 4224 oSettings.aiDisplay.splice( i-iIndexCorrector, 1 ); 4225 iIndexCorrector++; 4226 } 4227 } 4228 } 4229 } 4230 oSettings.oPreviousSearch.sSearch = sInput; 4231 oSettings.oPreviousSearch.bRegex = bRegex; 4232 oSettings.oPreviousSearch.bSmart = bSmart; 4233 } 4234 4235 /* 4236 * Function: _fnBuildSearchArray 4237 * Purpose: Create an array which can be quickly search through 4238 * Returns: - 4239 * Inputs: object:oSettings - dataTables settings object 4240 * int:iMaster - use the master data array - optional 4241 */ 4242 function _fnBuildSearchArray ( oSettings, iMaster ) 4243 { 4244 /* Clear out the old data */ 4245 oSettings.asDataSearch.splice( 0, oSettings.asDataSearch.length ); 4246 4247 var aArray = (typeof iMaster != 'undefined' && iMaster == 1) ? 4248 oSettings.aiDisplayMaster : oSettings.aiDisplay; 4249 4250 for ( var i=0, iLen=aArray.length ; i<iLen ; i++ ) 4251 { 4252 oSettings.asDataSearch[i] = _fnBuildSearchRow( oSettings, 4253 oSettings.aoData[ aArray[i] ]._aData ); 4254 } 4255 } 4256 4257 /* 4258 * Function: _fnBuildSearchRow 4259 * Purpose: Create a searchable string from a single data row 4260 * Returns: - 4261 * Inputs: object:oSettings - dataTables settings object 4262 * array:aData - aoData[]._aData array to use for the data to search 4263 */ 4264 function _fnBuildSearchRow( oSettings, aData ) 4265 { 4266 var sSearch = ''; 4267 var nTmp = document.createElement('div'); 4268 4269 for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ ) 4270 { 4271 if ( oSettings.aoColumns[j].bSearchable ) 4272 { 4273 var sData = aData[j]; 4274 sSearch += _fnDataToSearch( sData, oSettings.aoColumns[j].sType )+' '; 4275 } 4276 } 4277 4278 /* If it looks like there is an HTML entity in the string, attempt to decode it */ 4279 if ( sSearch.indexOf('&') !== -1 ) 4280 { 4281 nTmp.innerHTML = sSearch; 4282 sSearch = nTmp.textContent ? nTmp.textContent : nTmp.innerText; 4283 4284 /* IE and Opera appear to put an newline where there is a <br> tag - remove it */ 4285 sSearch = sSearch.replace(/\n/g," ").replace(/\r/g,""); 4286 } 4287 4288 return sSearch; 4289 } 4290 4291 /* 4292 * Function: _fnFilterCreateSearch 4293 * Purpose: Build a regular expression object suitable for searching a table 4294 * Returns: RegExp: - constructed object 4295 * Inputs: string:sSearch - string to search for 4296 * bool:bRegex - treat as a regular expression or not 4297 * bool:bSmart - perform smart filtering or not 4298 */ 4299 function _fnFilterCreateSearch( sSearch, bRegex, bSmart ) 4300 { 4301 var asSearch, sRegExpString; 4302 4303 if ( bSmart ) 4304 { 4305 /* Generate the regular expression to use. Something along the lines of: 4306 * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$ 4307 */ 4308 asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' ); 4309 sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$'; 4310 return new RegExp( sRegExpString, "i" ); 4311 } 4312 else 4313 {
4314 sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch ); 4315 return new RegExp( sSearch, "i" ); 4316 } 4317 } 4318 4319 /* 4320 * Function: _fnDataToSearch 4321 * Purpose: Convert raw data into something that the user can search on 4322 * Returns: string: - search string 4323 * Inputs: string:sData - data to be modified 4324 * string:sType - data type 4325 */ 4326 function _fnDataToSearch ( sData, sType ) 4327 { 4328 if ( typeof _oExt.ofnSearch[sType] == "function" ) 4329 { 4330 return _oExt.ofnSearch[sType]( sData ); 4331 } 4332 else if ( sType == "html" ) 4333 { 4334 return sData.replace(/\n/g," ").replace( /<.*?>/g, "" ); 4335 } 4336 else if ( typeof sData == "string" ) 4337 { 4338 return sData.replace(/\n/g," "); 4339 } 4340 return sData; 4341 } 4342 4343 4344 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 4345 * Section - Feature: Sorting 4346 */ 4347 4348 /* 4349 * Function: _fnSort 4350 * Purpose: Change the order of the table 4351 * Returns: - 4352 * Inputs: object:oSettings - dataTables settings object 4353 * bool:bApplyClasses - optional - should we apply classes or not 4354 * Notes: We always sort the master array and then apply a filter again 4355 * if it is needed. This probably isn't optimal - but atm I can't think 4356 * of any other way which is (each has disadvantages). we want to sort aiDisplayMaster - 4357 * but according to aoData[]._aData 4358 */ 4359 function _fnSort ( oSettings, bApplyClasses ) 4360 { 4361 var 4362 iDataSort, iDataType, 4363 i, iLen, j, jLen, 4364 aaSort = [], 4365 aiOrig = [], 4366 oSort = _oExt.oSort, 4367 aoData = oSettings.aoData, 4368 aoColumns = oSettings.aoColumns; 4369 4370 /* No sorting required if server-side or no sorting array */ 4371 if ( !oSettings.oFeatures.bServerSide && 4372 (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) ) 4373 { 4374 if ( oSettings.aaSortingFixed !== null ) 4375 { 4376 aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ); 4377 } 4378 else 4379 { 4380 aaSort = oSettings.aaSorting.slice(); 4381 } 4382 4383 /* If there is a sorting data type, and a fuction belonging to it, then we need to 4384 * get the data from the developer's function and apply it for this column 4385 */ 4386 for ( i=0 ; i<aaSort.length ; i++ ) 4387 { 4388 var iColumn = aaSort[i][0]; 4389 var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn ); 4390 var sDataType = oSettings.aoColumns[ iColumn ].sSortDataType; 4391 if ( typeof _oExt.afnSortData[sDataType] != 'undefined' ) 4392 { 4393 var aData = _oExt.afnSortData[sDataType]( oSettings, iColumn, iVisColumn ); 4394 for ( j=0, jLen=aoData.length ; j<jLen ; j++ ) 4395 { 4396 aoData[j]._aData[iColumn] = aData[j]; 4397 } 4398 } 4399 } 4400 4401 /* Create a value - key array of the current row positions such that we can use their 4402 * current position during the sort, if values match, in order to perform stable sorting 4403 */ 4404 for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ ) 4405 { 4406 aiOrig[ oSettings.aiDisplayMaster[i] ] = i; 4407 } 4408 4409 /* Do the sort - here we want multi-column sorting based on a given data source (column) 4410 * and sorting function (from oSort) in a certain direction. It's reasonably complex to 4411 * follow on it's own, but this is what we want (example two column sorting): 4412 * fnLocalSorting = function(a,b){ 4413 * var iTest; 4414 * iTest = oSort['string-asc']('data11', 'data12'); 4415 * if (iTest !== 0) 4416 * return iTest; 4417 * iTest = oSort['numeric-desc']('data21', 'data22'); 4418 * if (iTest !== 0) 4419 * return iTest; 4420 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); 4421 * } 4422 * Basically we have a test for each sorting column, if the data in that column is equal, 4423 * test the next column. If all columns match, then we use a numeric sort on the row 4424 * positions in the original data array to provide a stable sort. 4425 */ 4426 var iSortLen = aaSort.length; 4427 oSettings.aiDisplayMaster.sort( function ( a, b ) { 4428 var iTest; 4429 for ( i=0 ; i<iSortLen ; i++ ) 4430 { 4431 iDataSort = aoColumns[ aaSort[i][0] ].iDataSort; 4432 iDataType = aoColumns[ iDataSort ].sType;
4433 iTest = oSort[ iDataType+"-"+aaSort[i][1] ]( 4434 aoData[a]._aData[iDataSort], 4435 aoData[b]._aData[iDataSort] 4436 ); 4437 4438 if ( iTest !== 0 ) 4439 { 4440 return iTest; 4441 } 4442 } 4443 4444 return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); 4445 } ); 4446 } 4447 4448 /* Alter the sorting classes to take account of the changes */ 4449 if ( typeof bApplyClasses == 'undefined' || bApplyClasses ) 4450 { 4451 _fnSortingClasses( oSettings ); 4452 } 4453 4454 /* Tell the draw function that we have sorted the data */ 4455 oSettings.bSorted = true; 4456 4457 /* Copy the master data into the draw array and re-draw */ 4458 if ( oSettings.oFeatures.bFilter ) 4459 { 4460 /* _fnFilter() will redraw the table for us */ 4461 _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 ); 4462 } 4463 else 4464 { 4465 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 4466 oSettings._iDisplayStart = 0; /* reset display back to page 0 */ 4467 _fnCalculateEnd( oSettings ); 4468 _fnDraw( oSettings ); 4469 } 4470 } 4471 4472 /* 4473 * Function: _fnSortAttachListener 4474 * Purpose: Attach a sort handler (click) to a node 4475 * Returns: - 4476 * Inputs: object:oSettings - dataTables settings object 4477 * node:nNode - node to attach the handler to 4478 * int:iDataIndex - column sorting index 4479 * function:fnCallback - callback function - optional 4480 */ 4481 function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback ) 4482 { 4483 $(nNode).bind( 'click.DT', function (e) { 4484 /* If the column is not sortable - don't to anything */ 4485 if ( oSettings.aoColumns[iDataIndex].bSortable === false ) 4486 { 4487 return; 4488 } 4489 4490 /* 4491 * This is a little bit odd I admit... I declare a temporary function inside the scope of 4492 * _fnDrawHead and the click handler in order that the code presented here can be used 4493 * twice - once for when bProcessing is enabled, and another time for when it is 4494 * disabled, as we need to perform slightly different actions. 4495 * Basically the issue here is that the Javascript engine in modern browsers don't 4496 * appear to allow the rendering engine to update the display while it is still excuting 4497 * it's thread (well - it does but only after long intervals). This means that the 4498 * 'processing' display doesn't appear for a table sort. To break the js thread up a bit 4499 * I force an execution break by using setTimeout - but this breaks the expected 4500 * thread continuation for the end-developer's point of view (their code would execute 4501 * too early), so we on;y do it when we absolutely have to. 4502 */ 4503 var fnInnerSorting = function () { 4504 var iColumn, iNextSort; 4505 4506 /* If the shift key is pressed then we are multipe column sorting */ 4507 if ( e.shiftKey ) 4508 { 4509 /* Are we already doing some kind of sort on this column? */ 4510 var bFound = false; 4511 for ( var i=0 ; i<oSettings.aaSorting.length ; i++ ) 4512 { 4513 if ( oSettings.aaSorting[i][0] == iDataIndex ) 4514 { 4515 bFound = true; 4516 iColumn = oSettings.aaSorting[i][0]; 4517 iNextSort = oSettings.aaSorting[i][2]+1; 4518 4519 if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' ) 4520 { 4521 /* Reached the end of the sorting options, remove from multi-col sort */ 4522 oSettings.aaSorting.splice( i, 1 ); 4523 } 4524 else 4525 { 4526 /* Move onto next sorting direction */ 4527 oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort]; 4528 oSettings.aaSorting[i][2] = iNextSort; 4529 } 4530 break; 4531 } 4532 } 4533 4534 /* No sort yet - add it in */ 4535 if ( bFound === false ) 4536 { 4537 oSettings.aaSorting.push( [ iDataIndex, 4538 oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] ); 4539 } 4540 } 4541 else 4542 { 4543 /* If no shift key then single column sort */ 4544 if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex ) 4545 { 4546 iColumn = oSettings.aaSorting[0][0]; 4547 iNextSort = oSettings.aaSorting[0][2]+1; 4548 if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' ) 4549 { 4550 iNextSort = 0; 4551 } 4552 oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort]; 4553 oSettings.aaSorting[0][2] = iNextSort; 4554 } 4555 else 4556 { 4557 oSettings.aaSorting.splice( 0, oSettings.aaSorting.length ); 4558 oSettings.aaSorting.push( [ iDataIndex, 4559 oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] ); 4560 } 4561 } 4562 4563 /* Run the sort */ 4564 _fnSort( oSettings ); 4565 }; /* /fnInnerSorting */ 4566 4567 if ( !oSettings.oFeatures.bProcessing ) 4568 { 4569 fnInnerSorting(); 4570 } 4571 else 4572 { 4573 _fnProcessingDisplay( oSettings, true ); 4574 setTimeout( function() { 4575 fnInnerSorting(); 4576 if ( !oSettings.oFeatures.bServerSide ) 4577 { 4578 _fnProcessingDisplay( oSettings, false ); 4579 } 4580 }, 0 ); 4581 } 4582 4583 /* Call the user specified callback function - used for async user interaction */ 4584 if ( typeof fnCallback == 'function' ) 4585 { 4586 fnCallback( oSettings ); 4587 } 4588 } ); 4589 } 4590 4591 /* 4592 * Function: _fnSortingClasses 4593 * Purpose: Set the sortting classes on the header 4594 * Returns: - 4595 * Inputs: object:oSettings - dataTables settings object 4596 * Notes: It is safe to call this function when bSort and bSortClasses are false 4597 */ 4598 function _fnSortingClasses( oSettings ) 4599 { 4600 var i, iLen, j, jLen, iFound; 4601 var aaSort, sClass; 4602 var iColumns = oSettings.aoColumns.length; 4603 var oClasses = oSettings.oClasses; 4604 4605 for ( i=0 ; i<iColumns ; i++ ) 4606 { 4607 if ( oSettings.aoColumns[i].bSortable ) 4608 {
4609 $(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc + 4610 " "+ oSettings.aoColumns[i].sSortingClass ); 4611 } 4612 } 4613 4614 if ( oSettings.aaSortingFixed !== null ) 4615 { 4616 aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting ); 4617 } 4618 else 4619 { 4620 aaSort = oSettings.aaSorting.slice(); 4621 } 4622 4623 /* Apply the required classes to the header */ 4624 for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) 4625 { 4626 if ( oSettings.aoColumns[i].bSortable ) 4627 { 4628 sClass = oSettings.aoColumns[i].sSortingClass; 4629 iFound = -1; 4630 for ( j=0 ; j<aaSort.length ; j++ ) 4631 { 4632 if ( aaSort[j][0] == i ) 4633 { 4634 sClass = ( aaSort[j][1] == "asc" ) ? 4635 oClasses.sSortAsc : oClasses.sSortDesc; 4636 iFound = j; 4637 break; 4638 } 4639 } 4640 $(oSettings.aoColumns[i].nTh).addClass( sClass ); 4641 4642 if ( oSettings.bJUI ) 4643 { 4644 /* jQuery UI uses extra markup */ 4645 var jqSpan = $("span", oSettings.aoColumns[i].nTh); 4646 jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+ 4647 oClasses.sSortJUI +" "+ oClasses.sSortJUIAscAllowed +" "+ oClasses.sSortJUIDescAllowed ); 4648 4649 var sSpanClass; 4650 if ( iFound == -1 ) 4651 { 4652 sSpanClass = oSettings.aoColumns[i].sSortingClassJUI; 4653 } 4654 else if ( aaSort[iFound][1] == "asc" ) 4655 { 4656 sSpanClass = oClasses.sSortJUIAsc; 4657 } 4658 else 4659 { 4660 sSpanClass = oClasses.sSortJUIDesc; 4661 } 4662 4663 jqSpan.addClass( sSpanClass ); 4664 } 4665 } 4666 else 4667 { 4668 /* No sorting on this column, so add the base class. This will have been assigned by 4669 * _fnAddColumn 4670 */ 4671 $(oSettings.aoColumns[i].nTh).addClass( oSettings.aoColumns[i].sSortingClass ); 4672 } 4673 } 4674 4675 /* 4676 * Apply the required classes to the table body 4677 * Note that this is given as a feature switch since it can significantly slow down a sort 4678 * on large data sets (adding and removing of classes is always slow at the best of times..) 4679 * Further to this, note that this code is admitadly fairly ugly. It could be made a lot 4680 * simpiler using jQuery selectors and add/removeClass, but that is significantly slower 4681 * (on the order of 5 times slower) - hence the direct DOM manipulation here. 4682 */ 4683 sClass = oClasses.sSortColumn; 4684 4685 if ( oSettings.oFeatures.bSort && oSettings.oFeatures.bSortClasses ) 4686 { 4687 var nTds = _fnGetTdNodes( oSettings ); 4688 4689 /* Remove the old classes */ 4690 if ( nTds.length >= iColumns ) 4691 { 4692 for ( i=0 ; i<iColumns ; i++ ) 4693 { 4694 if ( nTds[i].className.indexOf(sClass+"1") != -1 ) 4695 { 4696 for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ ) 4697 { 4698 nTds[(iColumns*j)+i].className = 4699 $.trim( nTds[(iColumns*j)+i].className.replace( sClass+"1", "" ) ); 4700 } 4701 } 4702 else if ( nTds[i].className.indexOf(sClass+"2") != -1 ) 4703 { 4704 for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ ) 4705 { 4706 nTds[(iColumns*j)+i].className = 4707 $.trim( nTds[(iColumns*j)+i].className.replace( sClass+"2", "" ) ); 4708 } 4709 } 4710 else if ( nTds[i].className.indexOf(sClass+"3") != -1 ) 4711 { 4712 for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ ) 4713 { 4714 nTds[(iColumns*j)+i].className = 4715 $.trim( nTds[(iColumns*j)+i].className.replace( " "+sClass+"3", "" ) ); 4716 } 4717 } 4718 } 4719 } 4720 4721 /* Add the new classes to the table */ 4722 var iClass = 1, iTargetCol; 4723 for ( i=0 ; i<aaSort.length ; i++ ) 4724 { 4725 iTargetCol = parseInt( aaSort[i][0], 10 ); 4726 for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ ) 4727 { 4728 nTds[(iColumns*j)+iTargetCol].className += " "+sClass+iClass; 4729 } 4730 4731 if ( iClass < 3 ) 4732 { 4733 iClass++; 4734 } 4735 } 4736 } 4737 } 4738 4739 4740 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 4741 * Section - Feature: Pagination. Note that most of the paging logic is done in 4742 * _oExt.oPagination 4743 */ 4744 4745 /* 4746 * Function: _fnFeatureHtmlPaginate 4747 * Purpose: Generate the node required for default pagination 4748 * Returns: node 4749 * Inputs: object:oSettings - dataTables settings object 4750 */ 4751 function _fnFeatureHtmlPaginate ( oSettings ) 4752 { 4753 if ( oSettings.oScroll.bInfinite ) 4754 { 4755 return null; 4756 } 4757 4758 var nPaginate = document.createElement( 'div' ); 4759 nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType; 4760 4761 _oExt.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate, 4762 function( oSettings ) { 4763 _fnCalculateEnd( oSettings ); 4764 _fnDraw( oSettings ); 4765 } 4766 ); 4767 4768 /* Add a draw callback for the pagination on first instance, to update the paging display */ 4769 if ( typeof oSettings.aanFeatures.p == "undefined" ) 4770 { 4771 oSettings.aoDrawCallback.push( { 4772 "fn": function( oSettings ) { 4773 _oExt.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) { 4774 _fnCalculateEnd( oSettings ); 4775 _fnDraw( oSettings ); 4776 } ); 4777 }, 4778 "sName": "pagination" 4779 } ); 4780 } 4781 return nPaginate; 4782 } 4783 4784 /* 4785 * Function: _fnPageChange 4786 * Purpose: Alter the display settings to change the page 4787 * Returns: bool:true - page has changed, false - no change (no effect) eg 'first' on page 1 4788 * Inputs: object:oSettings - dataTables settings object
4789 * string:sAction - paging action to take: "first", "previous", "next" or "last" 4790 */ 4791 function _fnPageChange ( oSettings, sAction ) 4792 { 4793 var iOldStart = oSettings._iDisplayStart; 4794 4795 if ( sAction == "first" ) 4796 { 4797 oSettings._iDisplayStart = 0; 4798 } 4799 else if ( sAction == "previous" ) 4800 { 4801 oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ? 4802 oSettings._iDisplayStart - oSettings._iDisplayLength : 4803 0; 4804 4805 /* Correct for underrun */ 4806 if ( oSettings._iDisplayStart < 0 ) 4807 { 4808 oSettings._iDisplayStart = 0; 4809 } 4810 } 4811 else if ( sAction == "next" ) 4812 { 4813 if ( oSettings._iDisplayLength >= 0 ) 4814 { 4815 /* Make sure we are not over running the display array */ 4816 if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() ) 4817 { 4818 oSettings._iDisplayStart += oSettings._iDisplayLength; 4819 } 4820 } 4821 else 4822 { 4823 oSettings._iDisplayStart = 0; 4824 } 4825 } 4826 else if ( sAction == "last" ) 4827 { 4828 if ( oSettings._iDisplayLength >= 0 ) 4829 { 4830 var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1; 4831 oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength; 4832 } 4833 else 4834 { 4835 oSettings._iDisplayStart = 0; 4836 } 4837 } 4838 else 4839 { 4840 _fnLog( oSettings, 0, "Unknown paging action: "+sAction ); 4841 } 4842 4843 return iOldStart != oSettings._iDisplayStart; 4844 } 4845 4846 4847 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 4848 * Section - Feature: HTML info 4849 */ 4850 4851 /* 4852 * Function: _fnFeatureHtmlInfo 4853 * Purpose: Generate the node required for the info display 4854 * Returns: node 4855 * Inputs: object:oSettings - dataTables settings object 4856 */ 4857 function _fnFeatureHtmlInfo ( oSettings ) 4858 { 4859 var nInfo = document.createElement( 'div' ); 4860 nInfo.className = oSettings.oClasses.sInfo; 4861 4862 /* Actions that are to be taken once only for this feature */ 4863 if ( typeof oSettings.aanFeatures.i == "undefined" ) 4864 { 4865 /* Add draw callback */ 4866 oSettings.aoDrawCallback.push( { 4867 "fn": _fnUpdateInfo, 4868 "sName": "information" 4869 } ); 4870 4871 /* Add id */ 4872 if ( oSettings.sTableId !== '' ) 4873 { 4874 nInfo.setAttribute( 'id', oSettings.sTableId+'_info' ); 4875 } 4876 } 4877 4878 return nInfo; 4879 } 4880 4881 /* 4882 * Function: _fnUpdateInfo 4883 * Purpose: Update the information elements in the display 4884 * Returns: - 4885 * Inputs: object:oSettings - dataTables settings object 4886 */ 4887 function _fnUpdateInfo ( oSettings ) 4888 { 4889 /* Show information about the table */ 4890 if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 ) 4891 { 4892 return; 4893 } 4894 4895 var 4896 iStart = oSettings._iDisplayStart+1, iEnd = oSettings.fnDisplayEnd(), 4897 iMax = oSettings.fnRecordsTotal(), iTotal = oSettings.fnRecordsDisplay(), 4898 sStart = oSettings.fnFormatNumber( iStart ), sEnd = oSettings.fnFormatNumber( iEnd ), 4899 sMax = oSettings.fnFormatNumber( iMax ), sTotal = oSettings.fnFormatNumber( iTotal ), 4900 sOut; 4901 4902 /* When infinite scrolling, we are always starting at 1. _iDisplayStart is used only 4903 * internally 4904 */ 4905 if ( oSettings.oScroll.bInfinite ) 4906 { 4907 sStart = oSettings.fnFormatNumber( 1 ); 4908 } 4909 4910 if ( oSettings.fnRecordsDisplay() === 0 && 4911 oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() ) 4912 { 4913 /* Empty record set */ 4914 sOut = oSettings.oLanguage.sInfoEmpty+ oSettings.oLanguage.sInfoPostFix; 4915 } 4916 else if ( oSettings.fnRecordsDisplay() === 0 ) 4917 { 4918 /* Rmpty record set after filtering */ 4919 sOut = oSettings.oLanguage.sInfoEmpty +' '+ 4920 oSettings.oLanguage.sInfoFiltered.replace('_MAX_', sMax)+ 4921 oSettings.oLanguage.sInfoPostFix; 4922 } 4923 else if ( oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() ) 4924 { 4925 /* Normal record set */ 4926 sOut = oSettings.oLanguage.sInfo. 4927 replace('_START_', sStart). 4928 replace('_END_', sEnd). 4929 replace('_TOTAL_', sTotal)+ 4930 oSettings.oLanguage.sInfoPostFix; 4931 } 4932 else 4933 { 4934 /* Record set after filtering */ 4935 sOut = oSettings.oLanguage.sInfo. 4936 replace('_START_', sStart). 4937 replace('_END_', sEnd). 4938 replace('_TOTAL_', sTotal) +' '+ 4939 oSettings.oLanguage.sInfoFiltered.replace('_MAX_', 4940 oSettings.fnFormatNumber(oSettings.fnRecordsTotal()))+ 4941 oSettings.oLanguage.sInfoPostFix; 4942 } 4943 4944 if ( oSettings.oLanguage.fnInfoCallback !== null ) 4945 { 4946 sOut = oSettings.oLanguage.fnInfoCallback( oSettings, iStart, iEnd, iMax, iTotal, sOut ); 4947 } 4948 4949 var n = oSettings.aanFeatures.i; 4950 for ( var i=0, iLen=n.length ; i<iLen ; i++ ) 4951 { 4952 $(n[i]).html( sOut ); 4953 } 4954 } 4955 4956 4957 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 4958 * Section - Feature: Length change 4959 */ 4960 4961 /* 4962 * Function: _fnFeatureHtmlLength 4963 * Purpose: Generate the node required for user display length changing 4964 * Returns: node 4965 * Inputs: object:oSettings - dataTables settings object 4966 */ 4967 function _fnFeatureHtmlLength ( oSettings ) 4968 { 4969 if ( oSettings.oScroll.bInfinite ) 4970 { 4971 return null; 4972 } 4973 4974 /* This can be overruled by not using the _MENU_ var/macro in the language variable */ 4975 var sName = (oSettings.sTableId === "") ? "" : 'name="'+oSettings.sTableId+'_length"'; 4976 var sStdMenu = '<select size="1" '+sName+'>'; 4977 var i, iLen; 4978 4979 if ( oSettings.aLengthMenu.length == 2 && typeof oSettings.aLengthMenu[0] == 'object' && 4980 typeof oSettings.aLengthMenu[1] == 'object' ) 4981 {
4982 for ( i=0, iLen=oSettings.aLengthMenu[0].length ; i<iLen ; i++ ) 4983 { 4984 sStdMenu += '<option value="'+oSettings.aLengthMenu[0][i]+'">'+ 4985 oSettings.aLengthMenu[1][i]+'</option>'; 4986 } 4987 } 4988 else 4989 { 4990 for ( i=0, iLen=oSettings.aLengthMenu.length ; i<iLen ; i++ ) 4991 { 4992 sStdMenu += '<option value="'+oSettings.aLengthMenu[i]+'">'+ 4993 oSettings.aLengthMenu[i]+'</option>'; 4994 } 4995 } 4996 sStdMenu += '</select>'; 4997 4998 var nLength = document.createElement( 'div' ); 4999 if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.l == "undefined" ) 5000 { 5001 nLength.setAttribute( 'id', oSettings.sTableId+'_length' ); 5002 } 5003 nLength.className = oSettings.oClasses.sLength; 5004 nLength.innerHTML = oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu ); 5005 5006 /* 5007 * Set the length to the current display length - thanks to Andrea Pavlovic for this fix, 5008 * and Stefan Skopnik for fixing the fix! 5009 */ 5010 $('select option[value="'+oSettings._iDisplayLength+'"]',nLength).attr("selected",true); 5011 5012 $('select', nLength).bind( 'change.DT', function(e) { 5013 var iVal = $(this).val(); 5014 5015 /* Update all other length options for the new display */ 5016 var n = oSettings.aanFeatures.l; 5017 for ( i=0, iLen=n.length ; i<iLen ; i++ ) 5018 { 5019 if ( n[i] != this.parentNode ) 5020 { 5021 $('select', n[i]).val( iVal ); 5022 } 5023 } 5024 5025 /* Redraw the table */ 5026 oSettings._iDisplayLength = parseInt(iVal, 10); 5027 _fnCalculateEnd( oSettings ); 5028 5029 /* If we have space to show extra rows (backing up from the end point - then do so */ 5030 if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) 5031 { 5032 oSettings._iDisplayStart = oSettings.fnDisplayEnd() - oSettings._iDisplayLength; 5033 if ( oSettings._iDisplayStart < 0 ) 5034 { 5035 oSettings._iDisplayStart = 0; 5036 } 5037 } 5038 5039 if ( oSettings._iDisplayLength == -1 ) 5040 { 5041 oSettings._iDisplayStart = 0; 5042 } 5043 5044 _fnDraw( oSettings ); 5045 } ); 5046 5047 return nLength; 5048 } 5049 5050 5051 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5052 * Section - Feature: Processing incidator 5053 */ 5054 5055 /* 5056 * Function: _fnFeatureHtmlProcessing 5057 * Purpose: Generate the node required for the processing node 5058 * Returns: node 5059 * Inputs: object:oSettings - dataTables settings object 5060 */ 5061 function _fnFeatureHtmlProcessing ( oSettings ) 5062 { 5063 var nProcessing = document.createElement( 'div' ); 5064 5065 if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.r == "undefined" ) 5066 { 5067 nProcessing.setAttribute( 'id', oSettings.sTableId+'_processing' ); 5068 } 5069 nProcessing.innerHTML = oSettings.oLanguage.sProcessing; 5070 nProcessing.className = oSettings.oClasses.sProcessing; 5071 oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable ); 5072 5073 return nProcessing; 5074 } 5075 5076 /* 5077 * Function: _fnProcessingDisplay 5078 * Purpose: Display or hide the processing indicator 5079 * Returns: - 5080 * Inputs: object:oSettings - dataTables settings object 5081 * bool: 5082 * true - show the processing indicator 5083 * false - don't show 5084 */ 5085 function _fnProcessingDisplay ( oSettings, bShow ) 5086 { 5087 if ( oSettings.oFeatures.bProcessing ) 5088 { 5089 var an = oSettings.aanFeatures.r; 5090 for ( var i=0, iLen=an.length ; i<iLen ; i++ ) 5091 { 5092 an[i].style.visibility = bShow ? "visible" : "hidden"; 5093 } 5094 } 5095 } 5096 5097 5098 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 5099 * Section - Support functions 5100 */ 5101 5102 /* 5103 * Function: _fnVisibleToColumnIndex 5104 * Purpose: Covert the index of a visible column to the index in the data array (take account 5105 * of hidden columns) 5106 * Returns: int:i - the data index 5107 * Inputs: object:oSettings - dataTables settings object 5108 */ 5109 function _fnVisibleToColumnIndex( oSettings, iMatch ) 5110 { 5111 var iColumn = -1; 5112 5113 for ( var i=0 ; i<oSettings.aoColumns.length ; i++ ) 5114 { 5115 if ( oSettings.aoColumns[i].bVisible === true ) 5116 { 5117 iColumn++; 5118 } 5119 5120 if ( iColumn == iMatch ) 5121 { 5122 return i; 5123 } 5124 } 5125 5126 return null; 5127 } 5128 5129 /* 5130 * Function: _fnColumnIndexToVisible 5131 * Purpose: Covert the index of an index in the data array and convert it to the visible 5132 * column index (take account of hidden columns) 5133 * Returns: int:i - the data index 5134 * Inputs: object:oSettings - dataTables settings object 5135 */ 5136 function _fnColumnIndexToVisible( oSettings, iMatch ) 5137 { 5138 var iVisible = -1; 5139 for ( var i=0 ; i<oSettings.aoColumns.length ; i++ ) 5140 { 5141 if ( oSettings.aoColumns[i].bVisible === true ) 5142 { 5143 iVisible++; 5144 } 5145 5146 if ( i == iMatch ) 5147 { 5148 return oSettings.aoColumns[i].bVisible === true ? iVisible : null; 5149 } 5150 } 5151 5152 return null; 5153 } 5154 5155 5156 /* 5157 * Function: _fnNodeToDataIndex 5158 * Purpose: Take a TR element and convert it to an index in aoData 5159 * Returns: int:i - index if found, null if not 5160 * Inputs: object:s - dataTables settings object 5161 * node:n - the TR element to find 5162 */ 5163 function _fnNodeToDataIndex( s, n ) 5164 { 5165 var i, iLen; 5166 5167 /* Optimisation - see if the nodes which are currently visible match, since that is 5168 * the most likely node to be asked for (a selector or event for example) 5169 */ 5170 for ( i=s._iDisplayStart, iLen=s._iDisplayEnd ; i<iLen ; i++ ) 5171 { 5172 if ( s.aoData[ s.aiDisplay[i] ].nTr == n ) 5173 { 5174 return s.aiDisplay[i]; 5175 } 5176 } 5177 5178 /* Otherwise we are in for a slog through the whole data cache */ 5179 for ( i=0, iLen=s.aoData.length ; i<iLen ; i++ ) 5180 { 5181 if ( s.aoData[i].nTr == n ) 5182 { 5183 return i; 5184 } 5185 } 5186 return null; 5187 } 5188 5189 /* 5190 * Function: _fnVisbleColumns 5191 * Purpose: Get the number of visible columns 5192 * Returns: int:i - the number of visible columns 5193 * Inputs: object:oS - dataTables settings object 5194 */ 5195 function _fnVisbleColumns( oS ) 5196 { 5197 var iVis = 0; 5198 for ( var i=0 ; i<oS.aoColumns.length ; i++ ) 5199 { 5200 if ( oS.aoColumns[i].bVisible === true ) 5201 { 5202 iVis++; 5203 } 5204 } 5205 return iVis; 5206 } 5207 5208 /* 5209 * Function: _fnCalculateEnd 5210 * Purpose: Rcalculate the end point based on the start point 5211 * Returns: - 5212 * Inputs: object:oSettings - dataTables settings object 5213 */ 5214 function _fnCalculateEnd( oSettings ) 5215 { 5216 if ( oSettings.oFeatures.bPaginate === false ) 5217 { 5218 oSettings._iDisplayEnd = oSettings.aiDisplay.length; 5219 } 5220 else 5221 {
5222 /* Set the end point of the display - based on how many elements there are 5223 * still to display 5224 */ 5225 if ( oSettings._iDisplayStart + oSettings._iDisplayLength > oSettings.aiDisplay.length || 5226 oSettings._iDisplayLength == -1 ) 5227 { 5228 oSettings._iDisplayEnd = oSettings.aiDisplay.length; 5229 } 5230 else 5231 { 5232 oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength; 5233 } 5234 } 5235 } 5236 5237 /* 5238 * Function: _fnConvertToWidth 5239 * Purpose: Convert a CSS unit width to pixels (e.g. 2em) 5240 * Returns: int:iWidth - width in pixels 5241 * Inputs: string:sWidth - width to be converted 5242 * node:nParent - parent to get the with for (required for 5243 * relative widths) - optional 5244 */ 5245 function _fnConvertToWidth ( sWidth, nParent ) 5246 { 5247 if ( !sWidth || sWidth === null || sWidth === '' ) 5248 { 5249 return 0; 5250 } 5251 5252 if ( typeof nParent == "undefined" ) 5253 { 5254 nParent = document.getElementsByTagName('body')[0]; 5255 } 5256 5257 var iWidth; 5258 var nTmp = document.createElement( "div" ); 5259 nTmp.style.width = sWidth; 5260 5261 nParent.appendChild( nTmp ); 5262 iWidth = nTmp.offsetWidth; 5263 nParent.removeChild( nTmp ); 5264 5265 return ( iWidth ); 5266 } 5267 5268 /* 5269 * Function: _fnCalculateColumnWidths 5270 * Purpose: Calculate the width of columns for the table 5271 * Returns: - 5272 * Inputs: object:oSettings - dataTables settings object 5273 */ 5274 function _fnCalculateColumnWidths ( oSettings ) 5275 { 5276 var iTableWidth = oSettings.nTable.offsetWidth; 5277 var iUserInputs = 0; 5278 var iTmpWidth; 5279 var iVisibleColumns = 0; 5280 var iColums = oSettings.aoColumns.length; 5281 var i; 5282 var oHeaders = $('th', oSettings.nTHead); 5283 5284 /* Convert any user input sizes into pixel sizes */ 5285 for ( i=0 ; i<iColums ; i++ ) 5286 { 5287 if ( oSettings.aoColumns[i].bVisible ) 5288 { 5289 iVisibleColumns++; 5290 5291 if ( oSettings.aoColumns[i].sWidth !== null ) 5292 { 5293 iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig, 5294 oSettings.nTable.parentNode ); 5295 if ( iTmpWidth !== null ) 5296 { 5297 oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth ); 5298 } 5299 5300 iUserInputs++; 5301 } 5302 } 5303 } 5304 5305 /* If the number of columns in the DOM equals the number that we have to process in 5306 * DataTables, then we can use the offsets that are created by the web-browser. No custom 5307 * sizes can be set in order for this to happen, nor scrolling used 5308 */ 5309 if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums && 5310 oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" ) 5311 { 5312 for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) 5313 { 5314 iTmpWidth = $(oHeaders[i]).width(); 5315 if ( iTmpWidth !== null ) 5316 { 5317 oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth ); 5318 } 5319 } 5320 } 5321 else 5322 { 5323 /* Otherwise we are going to have to do some calculations to get the width of each column. 5324 * Construct a 1 row table with the widest node in the data, and any user defined widths, 5325 * then insert it into the DOM and allow the browser to do all the hard work of 5326 * calculating table widths. 5327 */ 5328 var 5329 nCalcTmp = oSettings.nTable.cloneNode( false ), 5330 nBody = document.createElement( 'tbody' ), 5331 nTr = document.createElement( 'tr' ), 5332 nDivSizing; 5333 5334 nCalcTmp.removeAttribute( "id" ); 5335 nCalcTmp.appendChild( oSettings.nTHead.cloneNode(true) ); 5336 if ( oSettings.nTFoot !== null ) 5337 { 5338 nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) ); 5339 _fnApplyToChildren( function(n) { 5340 n.style.width = ""; 5341 }, nCalcTmp.getElementsByTagName('tr') ); 5342 } 5343 5344 nCalcTmp.appendChild( nBody ); 5345 nBody.appendChild( nTr ); 5346 5347 /* Remove any sizing that was previously applied by the styles */ 5348 var jqColSizing = $('thead th', nCalcTmp); 5349 if ( jqColSizing.length === 0 ) 5350 { 5351 jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp); 5352 } 5353 jqColSizing.each( function (i) { 5354 this.style.width = ""; 5355 5356 var iIndex = _fnVisibleToColumnIndex( oSettings, i ); 5357 if ( iIndex !== null && oSettings.aoColumns[iIndex].sWidthOrig !== "" ) 5358 { 5359 this.style.width = oSettings.aoColumns[iIndex].sWidthOrig; 5360 } 5361 } ); 5362 5363 /* Find the biggest td for each column and put it into the table */ 5364 for ( i=0 ; i<iColums ; i++ ) 5365 { 5366 if ( oSettings.aoColumns[i].bVisible ) 5367 { 5368 var nTd = _fnGetWidestNode( oSettings, i ); 5369 if ( nTd !== null ) 5370 { 5371 nTd = nTd.cloneNode(true); 5372 nTr.appendChild( nTd ); 5373 } 5374 } 5375 } 5376 5377 /* Build the table and 'display' it */ 5378 var nWrapper = oSettings.nTable.parentNode; 5379 nWrapper.appendChild( nCalcTmp ); 5380 5381 /* When scrolling (X or Y) we want to set the width of the table as appropriate. However, 5382 * when not scrolling leave the table width as it is. This results in slightly different, 5383 * but I think correct behaviour 5384 */ 5385 if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" ) 5386 { 5387 nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner); 5388 } 5389 else if ( oSettings.oScroll.sX !== "" ) 5390 { 5391 nCalcTmp.style.width = ""; 5392 if ( $(nCalcTmp).width() < nWrapper.offsetWidth ) 5393 { 5394 nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth ); 5395 } 5396 } 5397 else if ( oSettings.oScroll.sY !== "" ) 5398 { 5399 nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth ); 5400 } 5401 nCalcTmp.style.visibility = "hidden"; 5402 5403 /* Scrolling considerations */ 5404 _fnScrollingWidthAdjust( oSettings, nCalcTmp ); 5405 5406 /* Read the width's calculated by the browser and store them for use by the caller. We 5407 * first of all try to use the elements in the body, but it is possible that there are 5408 * no elements there, under which circumstances we use the header elements 5409 */ 5410 var oNodes = $("tbody tr:eq(0)>td", nCalcTmp); 5411 if ( oNodes.length === 0 ) 5412 { 5413 oNodes = $("thead tr:eq(0)>th", nCalcTmp); 5414 } 5415 5416 var iIndex, iCorrector = 0, iWidth; 5417 for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) 5418 { 5419 if ( oSettings.aoColumns[i].bVisible ) 5420 { 5421 iWidth = $(oNodes[iCorrector]).outerWidth(); 5422 if ( iWidth !== null && iWidth > 0 ) 5423 { 5424 oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth ); 5425 } 5426 iCorrector++; 5427 } 5428 } 5429 5430 oSettings.nTable.style.width = _fnStringToCss( $(nCalcTmp).outerWidth() ); 5431 nCalcTmp.parentNode.removeChild( nCalcTmp ); 5432 } 5433 } 5434 5435 /* 5436 * Function: _fnScrollingWidthAdjust 5437 * Purpose: Adjust a table's width to take account of scrolling 5438 * Returns: - 5439 * Inputs: object:oSettings - dataTables settings object 5440 * node:n - table node 5441 */ 5442 function _fnScrollingWidthAdjust ( oSettings, n ) 5443 { 5444 if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" ) 5445 { 5446 /* When y-scrolling only, we want to remove the width of the scroll bar so the table 5447 * + scroll bar will fit into the area avaialble. 5448 */ 5449 var iOrigWidth = $(n).width();
5450 n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth ); 5451 } 5452 else if ( oSettings.oScroll.sX !== "" ) 5453 { 5454 /* When x-scrolling both ways, fix the table at it's current size, without adjusting */ 5455 n.style.width = _fnStringToCss( $(n).outerWidth() ); 5456 } 5457 } 5458 5459 /* 5460 * Function: _fnGetWidestNode 5461 * Purpose: Get the widest node 5462 * Returns: string: - max strlens for each column 5463 * Inputs: object:oSettings - dataTables settings object 5464 * int:iCol - column of interest 5465 * boolean:bFast - Should we use fast (but non-accurate) calculation - optional, 5466 * default true 5467 * Notes: This operation is _expensive_ (!!!). It requires a lot of DOM interaction, but 5468 * this is the only way to reliably get the widest string. For example 'mmm' would be wider 5469 * than 'iiii' so we can't just ocunt characters. If this can be optimised it would be good 5470 * to do so! 5471 */ 5472 function _fnGetWidestNode( oSettings, iCol, bFast ) 5473 { 5474 /* Use fast not non-accurate calculate based on the strlen */ 5475 if ( typeof bFast == 'undefined' || bFast ) 5476 { 5477 var iMaxLen = _fnGetMaxLenString( oSettings, iCol ); 5478 var iFastVis = _fnColumnIndexToVisible( oSettings, iCol); 5479 if ( iMaxLen < 0 ) 5480 { 5481 return null; 5482 } 5483 return oSettings.aoData[iMaxLen].nTr.getElementsByTagName('td')[iFastVis]; 5484 } 5485 5486 /* Use the slow approach, but get high quality answers - note that this code is not actually 5487 * used by DataTables by default. If you want to use it you can alter the call to 5488 * _fnGetWidestNode to pass 'false' as the third argument 5489 */ 5490 var
5491 iMax = -1, i, iLen, 5492 iMaxIndex = -1, 5493 n = document.createElement('div'); 5494 5495 n.style.visibility = "hidden"; 5496 n.style.position = "absolute"; 5497 document.body.appendChild( n ); 5498 5499 for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ ) 5500 { 5501 n.innerHTML = oSettings.aoData[i]._aData[iCol]; 5502 if ( n.offsetWidth > iMax ) 5503 { 5504 iMax = n.offsetWidth; 5505 iMaxIndex = i; 5506 } 5507 } 5508 document.body.removeChild( n ); 5509 5510 if ( iMaxIndex >= 0 ) 5511 { 5512 var iVis = _fnColumnIndexToVisible( oSettings, iCol); 5513 var nRet = oSettings.aoData[iMaxIndex].nTr.getElementsByTagName('td')[iVis]; 5514 if ( nRet ) 5515 { 5516 return nRet; 5517 } 5518 } 5519 return null; 5520 } 5521 5522 /* 5523 * Function: _fnGetMaxLenString 5524 * Purpose: Get the maximum strlen for each data column 5525 * Returns: string: - max strlens for each column 5526 * Inputs: object:oSettings - dataTables settings object 5527 * int:iCol - column of interest 5528 */ 5529 function _fnGetMaxLenString( oSettings, iCol ) 5530 { 5531 var iMax = -1; 5532 var iMaxIndex = -1; 5533 5534 for ( var i=0 ; i<oSettings.aoData.length ; i++ ) 5535 { 5536 var s = oSettings.aoData[i]._aData[iCol]; 5537 if ( s.length > iMax ) 5538 { 5539 iMax = s.length; 5540 iMaxIndex = i; 5541 } 5542 } 5543 5544 return iMaxIndex; 5545 } 5546 5547 /* 5548 * Function: _fnStringToCss 5549 * Purpose: Append a CSS unit (only if required) to a string 5550 * Returns: 0 if match, 1 if length is different, 2 if no match 5551 * Inputs: array:aArray1 - first array 5552 * array:aArray2 - second array 5553 */ 5554 function _fnStringToCss( s ) 5555 { 5556 if ( s === null ) 5557 { 5558 return "0px"; 5559 } 5560 5561 if ( typeof s == 'number' ) 5562 { 5563 if ( s < 0 ) 5564 { 5565 return "0px"; 5566 } 5567 return s+"px"; 5568 } 5569 5570 /* Check if the last character is not 0-9 */ 5571 var c = s.charCodeAt( s.length-1 ); 5572 if (c < 0x30 || c > 0x39) 5573 { 5574 return s; 5575 } 5576 return s+"px"; 5577 } 5578 5579 /* 5580 * Function: _fnArrayCmp 5581 * Purpose: Compare two arrays 5582 * Returns: 0 if match, 1 if length is different, 2 if no match 5583 * Inputs: array:aArray1 - first array 5584 * array:aArray2 - second array 5585 */ 5586 function _fnArrayCmp( aArray1, aArray2 ) 5587 { 5588 if ( aArray1.length != aArray2.length ) 5589 { 5590 return 1; 5591 } 5592 5593 for ( var i=0 ; i<aArray1.length ; i++ ) 5594 { 5595 if ( aArray1[i] != aArray2[i] ) 5596 { 5597 return 2; 5598 } 5599 } 5600 5601 return 0; 5602 } 5603 5604 /* 5605 * Function: _fnDetectType 5606 * Purpose: Get the sort type based on an input string 5607 * Returns: string: - type (defaults to 'string' if no type can be detected) 5608 * Inputs: string:sData - data we wish to know the type of 5609 * Notes: This function makes use of the DataTables plugin objct _oExt 5610 * (.aTypes) such that new types can easily be added. 5611 */ 5612 function _fnDetectType( sData ) 5613 { 5614 var aTypes = _oExt.aTypes; 5615 var iLen = aTypes.length; 5616 5617 for ( var i=0 ; i<iLen ; i++ ) 5618 { 5619 var sType = aTypes[i]( sData ); 5620 if ( sType !== null ) 5621 { 5622 return sType; 5623 } 5624 } 5625 5626 return 'string'; 5627 } 5628 5629 /* 5630 * Function: _fnSettingsFromNode 5631 * Purpose: Return the settings object for a particular table 5632 * Returns: object: Settings object - or null if not found 5633 * Inputs: node:nTable - table we are using as a dataTable 5634 */ 5635 function _fnSettingsFromNode ( nTable ) 5636 { 5637 for ( var i=0 ; i<_aoSettings.length ; i++ ) 5638 { 5639 if ( _aoSettings[i].nTable == nTable ) 5640 { 5641 return _aoSettings[i]; 5642 } 5643 } 5644 5645 return null; 5646 } 5647 5648 /* 5649 * Function: _fnGetDataMaster 5650 * Purpose: Return an array with the full table data 5651 * Returns: array array:aData - Master data array 5652 * Inputs: object:oSettings - dataTables settings object 5653 */ 5654 function _fnGetDataMaster ( oSettings ) 5655 { 5656 var aData = []; 5657 var iLen = oSettings.aoData.length; 5658 for ( var i=0 ; i<iLen; i++ ) 5659 { 5660 aData.push( oSettings.aoData[i]._aData ); 5661 } 5662 return aData; 5663 } 5664 5665 /* 5666 * Function: _fnGetTrNodes 5667 * Purpose: Return an array with the TR nodes for the table 5668 * Returns: array: - TR array 5669 * Inputs: object:oSettings - dataTables settings object 5670 */ 5671 function _fnGetTrNodes ( oSettings ) 5672 { 5673 var aNodes = []; 5674 var iLen = oSettings.aoData.length; 5675 for ( var i=0 ; i<iLen ; i++ ) 5676 { 5677 aNodes.push( oSettings.aoData[i].nTr ); 5678 } 5679 return aNodes; 5680 } 5681 5682 /* 5683 * Function: _fnGetTdNodes 5684 * Purpose: Return an array with the TD nodes for the table 5685 * Returns: array: - TD array 5686 * Inputs: object:oSettings - dataTables settings object 5687 */ 5688 function _fnGetTdNodes ( oSettings ) 5689 { 5690 var nTrs = _fnGetTrNodes( oSettings ); 5691 var nTds = [], nTd; 5692 var anReturn = []; 5693 var iCorrector; 5694 var iRow, iRows, iColumn, iColumns; 5695 5696 for ( iRow=0, iRows=nTrs.length ; iRow<iRows ; iRow++ ) 5697 { 5698 nTds = []; 5699 for ( iColumn=0, iColumns=nTrs[iRow].childNodes.length ; iColumn<iColumns ; iColumn++ ) 5700 { 5701 nTd = nTrs[iRow].childNodes[iColumn]; 5702 if ( nTd.nodeName.toUpperCase() == "TD" ) 5703 { 5704 nTds.push( nTd ); 5705 } 5706 } 5707 5708 iCorrector = 0; 5709 for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ ) 5710 { 5711 if ( oSettings.aoColumns[iColumn].bVisible ) 5712 { 5713 anReturn.push( nTds[iColumn-iCorrector] ); 5714 } 5715 else 5716 { 5717 anReturn.push( oSettings.aoData[iRow]._anHidden[iColumn] ); 5718 iCorrector++; 5719 } 5720 } 5721 } 5722 return anReturn; 5723 } 5724 5725 /* 5726 * Function: _fnEscapeRegex 5727 * Purpose: scape a string stuch that it can be used in a regular expression 5728 * Returns: string: - escaped string 5729 * Inputs: string:sVal - string to escape 5730 */ 5731 function _fnEscapeRegex ( sVal ) 5732 { 5733 var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^' ]; 5734 var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' ); 5735 return sVal.replace(reReplace, '\\$1'); 5736 } 5737 5738 /* 5739 * Function: _fnDeleteIndex 5740 * Purpose: Take an array of integers (index array) and remove a target integer (value - not 5741 * the key!) 5742 * Returns: - 5743 * Inputs: a:array int - Index array to target 5744 * int:iTarget - value to find 5745 */ 5746 function _fnDeleteIndex( a, iTarget ) 5747 { 5748 var iTargetIndex = -1; 5749 5750 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) 5751 { 5752 if ( a[i] == iTarget ) 5753 { 5754 iTargetIndex = i; 5755 } 5756 else if ( a[i] > iTarget ) 5757 { 5758 a[i]--; 5759 } 5760 } 5761 5762 if ( iTargetIndex != -1 ) 5763 { 5764 a.splice( iTargetIndex, 1 ); 5765 } 5766 } 5767 5768 /* 5769 * Function: _fnReOrderIndex 5770 * Purpose: Figure out how to reorder a display list 5771 * Returns: array int:aiReturn - index list for reordering 5772 * Inputs: object:oSettings - dataTables settings object 5773 */ 5774 function _fnReOrderIndex ( oSettings, sColumns ) 5775 { 5776 var aColumns = sColumns.split(','); 5777 var aiReturn = []; 5778
5779 for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 5780 { 5781 for ( var j=0 ; j<iLen ; j++ ) 5782 { 5783 if ( oSettings.aoColumns[i].sName == aColumns[j] ) 5784 { 5785 aiReturn.push( j ); 5786 break; 5787 } 5788 } 5789 } 5790 5791 return aiReturn; 5792 } 5793 5794 /* 5795 * Function: _fnColumnOrdering 5796 * Purpose: Get the column ordering that DataTables expects 5797 * Returns: string: - comma separated list of names 5798 * Inputs: object:oSettings - dataTables settings object 5799 */ 5800 function _fnColumnOrdering ( oSettings ) 5801 { 5802 var sNames = ''; 5803 for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 5804 { 5805 sNames += oSettings.aoColumns[i].sName+','; 5806 } 5807 if ( sNames.length == iLen ) 5808 { 5809 return ""; 5810 } 5811 return sNames.slice(0, -1); 5812 } 5813 5814 /* 5815 * Function: _fnLog 5816 * Purpose: Log an error message 5817 * Returns: - 5818 * Inputs: int:iLevel - log error messages, or display them to the user 5819 * string:sMesg - error message 5820 */ 5821 function _fnLog( oSettings, iLevel, sMesg ) 5822 { 5823 var sAlert = oSettings.sTableId === "" ? 5824 "DataTables warning: " +sMesg : 5825 "DataTables warning (table id = '"+oSettings.sTableId+"'): " +sMesg; 5826 5827 if ( iLevel === 0 ) 5828 { 5829 if ( _oExt.sErrMode == 'alert' ) 5830 { 5831 alert( sAlert ); 5832 } 5833 else 5834 { 5835 throw sAlert; 5836 } 5837 return; 5838 } 5839 else if ( typeof console != 'undefined' && typeof console.log != 'undefined' ) 5840 { 5841 console.log( sAlert ); 5842 } 5843 } 5844 5845 /* 5846 * Function: _fnClearTable 5847 * Purpose: Nuke the table 5848 * Returns: - 5849 * Inputs: object:oSettings - dataTables settings object 5850 */ 5851 function _fnClearTable( oSettings ) 5852 { 5853 oSettings.aoData.splice( 0, oSettings.aoData.length ); 5854 oSettings.aiDisplayMaster.splice( 0, oSettings.aiDisplayMaster.length ); 5855 oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length ); 5856 _fnCalculateEnd( oSettings ); 5857 } 5858 5859 /* 5860 * Function: _fnSaveState 5861 * Purpose: Save the state of a table in a cookie such that the page can be reloaded 5862 * Returns: - 5863 * Inputs: object:oSettings - dataTables settings object 5864 */ 5865 function _fnSaveState ( oSettings ) 5866 { 5867 if ( !oSettings.oFeatures.bStateSave || typeof oSettings.bDestroying != 'undefined' ) 5868 { 5869 return; 5870 } 5871 5872 /* Store the interesting variables */ 5873 var i, iLen, sTmp; 5874 var sValue = "{"; 5875 sValue += '"iCreate":'+ new Date().getTime()+','; 5876 sValue += '"iStart":'+ oSettings._iDisplayStart+','; 5877 sValue += '"iEnd":'+ oSettings._iDisplayEnd+','; 5878 sValue += '"iLength":'+ oSettings._iDisplayLength+','; 5879 sValue += '"sFilter":"'+ encodeURIComponent(oSettings.oPreviousSearch.sSearch)+'",'; 5880 sValue += '"sFilterEsc":'+ !oSettings.oPreviousSearch.bRegex+','; 5881 5882 sValue += '"aaSorting":[ '; 5883 for ( i=0 ; i<oSettings.aaSorting.length ; i++ ) 5884 { 5885 sValue += '['+oSettings.aaSorting[i][0]+',"'+oSettings.aaSorting[i][1]+'"],'; 5886 } 5887 sValue = sValue.substring(0, sValue.length-1); 5888 sValue += "],"; 5889 5890 sValue += '"aaSearchCols":[ '; 5891 for ( i=0 ; i<oSettings.aoPreSearchCols.length ; i++ ) 5892 { 5893 sValue += '["'+encodeURIComponent(oSettings.aoPreSearchCols[i].sSearch)+ 5894 '",'+!oSettings.aoPreSearchCols[i].bRegex+'],'; 5895 } 5896 sValue = sValue.substring(0, sValue.length-1); 5897 sValue += "],"; 5898 5899 sValue += '"abVisCols":[ '; 5900 for ( i=0 ; i<oSettings.aoColumns.length ; i++ ) 5901 { 5902 sValue += oSettings.aoColumns[i].bVisible+","; 5903 } 5904 sValue = sValue.substring(0, sValue.length-1); 5905 sValue += "]"; 5906 5907 /* Save state from any plug-ins */ 5908 for ( i=0, iLen=oSettings.aoStateSave.length ; i<iLen ; i++ ) 5909 { 5910 sTmp = oSettings.aoStateSave[i].fn( oSettings, sValue ); 5911 if ( sTmp !== "" ) 5912 { 5913 sValue = sTmp; 5914 } 5915 } 5916 5917 sValue += "}"; 5918 5919 _fnCreateCookie( oSettings.sCookiePrefix+oSettings.sInstance, sValue, 5920 oSettings.iCookieDuration, oSettings.sCookiePrefix, oSettings.fnCookieCallback ); 5921 } 5922 5923 /* 5924 * Function: _fnLoadState 5925 * Purpose: Attempt to load a saved table state from a cookie 5926 * Returns: - 5927 * Inputs: object:oSettings - dataTables settings object 5928 * object:oInit - DataTables init object so we can override settings 5929 */ 5930 function _fnLoadState ( oSettings, oInit ) 5931 { 5932 if ( !oSettings.oFeatures.bStateSave ) 5933 { 5934 return; 5935 } 5936 5937 var oData, i, iLen; 5938 var sData = _fnReadCookie( oSettings.sCookiePrefix+oSettings.sInstance );
5939 if ( sData !== null && sData !== '' ) 5940 { 5941 /* Try/catch the JSON eval - if it is bad then we ignore it - note that 1.7.0 and before 5942 * incorrectly used single quotes for some strings - hence the replace below 5943 */ 5944 try 5945 { 5946 oData = (typeof $.parseJSON == 'function') ? 5947 $.parseJSON( sData.replace(/'/g, '"') ) : eval( '('+sData+')' ); 5948 } 5949 catch( e ) 5950 { 5951 return; 5952 } 5953 5954 /* Allow custom and plug-in manipulation functions to alter the data set which was 5955 * saved, and also reject any saved state by returning false 5956 */ 5957 for ( i=0, iLen=oSettings.aoStateLoad.length ; i<iLen ; i++ ) 5958 { 5959 if ( !oSettings.aoStateLoad[i].fn( oSettings, oData ) ) 5960 { 5961 return; 5962 } 5963 } 5964 5965 /* Store the saved state so it might be accessed at any time (particualrly a plug-in */ 5966 oSettings.oLoadedState = $.extend( true, {}, oData ); 5967 5968 /* Restore key features */ 5969 oSettings._iDisplayStart = oData.iStart; 5970 oSettings.iInitDisplayStart = oData.iStart; 5971 oSettings._iDisplayEnd = oData.iEnd; 5972 oSettings._iDisplayLength = oData.iLength; 5973 oSettings.oPreviousSearch.sSearch = decodeURIComponent(oData.sFilter); 5974 oSettings.aaSorting = oData.aaSorting.slice(); 5975 oSettings.saved_aaSorting = oData.aaSorting.slice(); 5976 5977 /* 5978 * Search filtering - global reference added in 1.4.1 5979 * Note that we use a 'not' for the value of the regular expression indicator to maintain 5980 * compatibility with pre 1.7 versions, where this was basically inverted. Added in 1.7.0 5981 */ 5982 if ( typeof oData.sFilterEsc != 'undefined' ) 5983 { 5984 oSettings.oPreviousSearch.bRegex = !oData.sFilterEsc; 5985 } 5986 5987 /* Column filtering - added in 1.5.0 beta 6 */ 5988 if ( typeof oData.aaSearchCols != 'undefined' ) 5989 { 5990 for ( i=0 ; i<oData.aaSearchCols.length ; i++ ) 5991 { 5992 oSettings.aoPreSearchCols[i] = { 5993 "sSearch": decodeURIComponent(oData.aaSearchCols[i][0]), 5994 "bRegex": !oData.aaSearchCols[i][1] 5995 }; 5996 } 5997 } 5998 5999 /* Column visibility state - added in 1.5.0 beta 10 */ 6000 if ( typeof oData.abVisCols != 'undefined' ) 6001 { 6002 /* Pass back visibiliy settings to the init handler, but to do not here override 6003 * the init object that the user might have passed in 6004 */ 6005 oInit.saved_aoColumns = []; 6006 for ( i=0 ; i<oData.abVisCols.length ; i++ ) 6007 { 6008 oInit.saved_aoColumns[i] = {}; 6009 oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i]; 6010 } 6011 } 6012 } 6013 } 6014 6015 /* 6016 * Function: _fnCreateCookie 6017 * Purpose: Create a new cookie with a value to store the state of a table 6018 * Returns: - 6019 * Inputs: string:sName - name of the cookie to create 6020 * string:sValue - the value the cookie should take 6021 * int:iSecs - duration of the cookie 6022 * string:sBaseName - sName is made up of the base + file name - this is the base 6023 * function:fnCallback - User definable function to modify the cookie 6024 */ 6025 function _fnCreateCookie ( sName, sValue, iSecs, sBaseName, fnCallback ) 6026 { 6027 var date = new Date(); 6028 date.setTime( date.getTime()+(iSecs*1000) ); 6029 6030 /* 6031 * Shocking but true - it would appear IE has major issues with having the path not having 6032 * a trailing slash on it. We need the cookie to be available based on the path, so we 6033 * have to append the file name to the cookie name. Appalling. Thanks to vex for adding the 6034 * patch to use at least some of the path 6035 */ 6036 var aParts = window.location.pathname.split('/'); 6037 var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase(); 6038 var sFullCookie, oData; 6039 6040 if ( fnCallback !== null ) 6041 { 6042 oData = (typeof $.parseJSON == 'function') ? 6043 $.parseJSON( sValue ) : eval( '('+sValue+')' ); 6044 sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(), 6045 aParts.join('/')+"/" ); 6046 } 6047 else 6048 { 6049 sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) + 6050 "; expires=" + date.toGMTString() +"; path=" + aParts.join('/')+"/"; 6051 } 6052 6053 /* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies 6054 * belonging to DataTables. This is FAR from bullet proof 6055 */ 6056 var sOldName="", iOldTime=9999999999999; 6057 var iLength = _fnReadCookie( sNameFile )!==null ? document.cookie.length : 6058 sFullCookie.length + document.cookie.length; 6059 6060 if ( iLength+10 > 4096 ) /* Magic 10 for padding */ 6061 { 6062 var aCookies =document.cookie.split(';');
6063 for ( var i=0, iLen=aCookies.length ; i<iLen ; i++ ) 6064 { 6065 if ( aCookies[i].indexOf( sBaseName ) != -1 ) 6066 { 6067 /* It's a DataTables cookie, so eval it and check the time stamp */ 6068 var aSplitCookie = aCookies[i].split('='); 6069 try { oData = eval( '('+decodeURIComponent(aSplitCookie[1])+')' ); } 6070 catch( e ) { continue; } 6071 6072 if ( typeof oData.iCreate != 'undefined' && oData.iCreate < iOldTime ) 6073 { 6074 sOldName = aSplitCookie[0]; 6075 iOldTime = oData.iCreate; 6076 } 6077 } 6078 } 6079 6080 if ( sOldName !== "" ) 6081 { 6082 document.cookie = sOldName+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+ 6083 aParts.join('/') + "/"; 6084 } 6085 } 6086 6087 document.cookie = sFullCookie; 6088 } 6089 6090 /* 6091 * Function: _fnReadCookie 6092 * Purpose: Read an old cookie to get a cookie with an old table state 6093 * Returns: string: - contents of the cookie - or null if no cookie with that name found 6094 * Inputs: string:sName - name of the cookie to read 6095 */ 6096 function _fnReadCookie ( sName ) 6097 { 6098 var 6099 aParts = window.location.pathname.split('/'), 6100 sNameEQ = sName + '_' + aParts[aParts.length-1].replace(/[\/:]/g,"").toLowerCase() + '=', 6101 sCookieContents = document.cookie.split(';'); 6102 6103 for( var i=0 ; i<sCookieContents.length ; i++ ) 6104 { 6105 var c = sCookieContents[i]; 6106 6107 while (c.charAt(0)==' ') 6108 { 6109 c = c.substring(1,c.length); 6110 } 6111 6112 if (c.indexOf(sNameEQ) === 0) 6113 { 6114 return decodeURIComponent( c.substring(sNameEQ.length,c.length) ); 6115 } 6116 } 6117 return null; 6118 } 6119 6120 /* 6121 * Function: _fnGetUniqueThs 6122 * Purpose: Get an array of unique th elements, one for each column 6123 * Returns: array node:aReturn - list of unique ths 6124 * Inputs: node:nThead - The thead element for the table 6125 */ 6126 function _fnGetUniqueThs ( nThead ) 6127 { 6128 var nTrs = nThead.getElementsByTagName('tr'); 6129 6130 /* Nice simple case */ 6131 if ( nTrs.length == 1 ) 6132 { 6133 return nTrs[0].getElementsByTagName('th'); 6134 } 6135 6136 /* Otherwise we need to figure out the layout array to get the nodes */ 6137 var aLayout = [], aReturn = []; 6138 var ROWSPAN = 2, COLSPAN = 3, TDELEM = 4; 6139 var i, j, k, iLen, jLen, iColumnShifted; 6140 var fnShiftCol = function ( a, i, j ) { 6141 while ( typeof a[i][j] != 'undefined' ) { 6142 j++; 6143 } 6144 return j; 6145 }; 6146 var fnAddRow = function ( i ) { 6147 if ( typeof aLayout[i] == 'undefined' ) { 6148 aLayout[i] = []; 6149 } 6150 }; 6151 6152 /* Calculate a layout array */ 6153 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 6154 { 6155 fnAddRow( i ); 6156 var iColumn = 0; 6157 var nTds = []; 6158 6159 for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ ) 6160 { 6161 if ( nTrs[i].childNodes[j].nodeName.toUpperCase() == "TD" || 6162 nTrs[i].childNodes[j].nodeName.toUpperCase() == "TH" ) 6163 { 6164 nTds.push( nTrs[i].childNodes[j] ); 6165 } 6166 } 6167 6168 for ( j=0, jLen=nTds.length ; j<jLen ; j++ ) 6169 { 6170 var iColspan = nTds[j].getAttribute('colspan') * 1; 6171 var iRowspan = nTds[j].getAttribute('rowspan') * 1; 6172 6173 if ( !iColspan || iColspan===0 || iColspan===1 ) 6174 { 6175 iColumnShifted = fnShiftCol( aLayout, i, iColumn ); 6176 aLayout[i][iColumnShifted] = (nTds[j].nodeName.toUpperCase()=="TD") ? TDELEM : nTds[j]; 6177 if ( iRowspan || iRowspan===0 || iRowspan===1 ) 6178 { 6179 for ( k=1 ; k<iRowspan ; k++ ) 6180 { 6181 fnAddRow( i+k ); 6182 aLayout[i+k][iColumnShifted] = ROWSPAN; 6183 } 6184 } 6185 iColumn++; 6186 } 6187 else 6188 { 6189 iColumnShifted = fnShiftCol( aLayout, i, iColumn ); 6190 for ( k=0 ; k<iColspan ; k++ ) 6191 { 6192 aLayout[i][iColumnShifted+k] = COLSPAN; 6193 } 6194 iColumn += iColspan; 6195 } 6196 } 6197 } 6198 6199 /* Convert the layout array into a node array */ 6200 for ( i=0, iLen=aLayout.length ; i<iLen ; i++ ) 6201 { 6202 for ( j=0, jLen=aLayout[i].length ; j<jLen ; j++ ) 6203 { 6204 if ( typeof aLayout[i][j] == 'object' && typeof aReturn[j] == 'undefined' ) 6205 { 6206 aReturn[j] = aLayout[i][j]; 6207 } 6208 } 6209 } 6210 6211 return aReturn; 6212 } 6213 6214 /* 6215 * Function: _fnScrollBarWidth 6216 * Purpose: Get the width of a scroll bar in this browser being used 6217 * Returns: int: - width in pixels 6218 * Inputs: - 6219 * Notes: All credit for this function belongs to Alexandre Gomes. Thanks for sharing! 6220 * http://www.alexandre-gomes.com/?p=115 6221 */ 6222 function _fnScrollBarWidth () 6223 { 6224 var inner = document.createElement('p'); 6225 var style = inner.style; 6226 style.width = "100%"; 6227 style.height = "200px"; 6228 6229 var outer = document.createElement('div'); 6230 style = outer.style; 6231 style.position = "absolute"; 6232 style.top = "0px";
6233 style.left = "0px"; 6234 style.visibility = "hidden"; 6235 style.width = "200px"; 6236 style.height = "150px"; 6237 style.overflow = "hidden"; 6238 outer.appendChild(inner); 6239 6240 document.body.appendChild(outer); 6241 var w1 = inner.offsetWidth; 6242 outer.style.overflow = 'scroll'; 6243 var w2 = inner.offsetWidth; 6244 if ( w1 == w2 ) 6245 { 6246 w2 = outer.clientWidth; 6247 } 6248 6249 document.body.removeChild(outer); 6250 return (w1 - w2); 6251 } 6252 6253 /* 6254 * Function: _fnApplyToChildren 6255 * Purpose: Apply a given function to the display child nodes of an element array (typically 6256 * TD children of TR rows 6257 * Returns: - (done by reference) 6258 * Inputs: function:fn - Method to apply to the objects 6259 * array nodes:an1 - List of elements to look through for display children 6260 * array nodes:an2 - Another list (identical structure to the first) - optional 6261 */ 6262 function _fnApplyToChildren( fn, an1, an2 ) 6263 { 6264 for ( var i=0, iLen=an1.length ; i<iLen ; i++ ) 6265 { 6266 for ( var j=0, jLen=an1[i].childNodes.length ; j<jLen ; j++ ) 6267 { 6268 if ( an1[i].childNodes[j].nodeType == 1 ) 6269 { 6270 if ( typeof an2 != 'undefined' ) 6271 { 6272 fn( an1[i].childNodes[j], an2[i].childNodes[j] ); 6273 } 6274 else 6275 { 6276 fn( an1[i].childNodes[j] ); 6277 } 6278 } 6279 } 6280 } 6281 } 6282 6283 /* 6284 * Function: _fnMap 6285 * Purpose: See if a property is defined on one object, if so assign it to the other object 6286 * Returns: - (done by reference) 6287 * Inputs: object:oRet - target object
6288 * object:oSrc - source object 6289 * string:sName - property 6290 * string:sMappedName - name to map too - optional, sName used if not given 6291 */ 6292 function _fnMap( oRet, oSrc, sName, sMappedName ) 6293 { 6294 if ( typeof sMappedName == 'undefined' ) 6295 { 6296 sMappedName = sName; 6297 } 6298 if ( typeof oSrc[sName] != 'undefined' ) 6299 { 6300 oRet[sMappedName] = oSrc[sName]; 6301 } 6302 } 6303 6304 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 6305 * Section - API 6306 * 6307 * I'm not overly happy with this solution - I'd much rather that there was a way of getting 6308 * a list of all the private functions and do what we need to dynamically - but that doesn't 6309 * appear to be possible. Bonkers. A better solution would be to provide a 'bind' type object 6310 * To do - bind type method in DTs 2.x. 6311 */ 6312 this.oApi._fnExternApiFunc = _fnExternApiFunc; 6313 this.oApi._fnInitalise = _fnInitalise; 6314 this.oApi._fnLanguageProcess = _fnLanguageProcess; 6315 this.oApi._fnAddColumn = _fnAddColumn; 6316 this.oApi._fnColumnOptions = _fnColumnOptions; 6317 this.oApi._fnAddData = _fnAddData; 6318 this.oApi._fnGatherData = _fnGatherData; 6319 this.oApi._fnDrawHead = _fnDrawHead; 6320 this.oApi._fnDraw = _fnDraw; 6321 this.oApi._fnReDraw = _fnReDraw; 6322 this.oApi._fnAjaxUpdate = _fnAjaxUpdate; 6323 this.oApi._fnAjaxUpdateDraw = _fnAjaxUpdateDraw; 6324 this.oApi._fnAddOptionsHtml = _fnAddOptionsHtml; 6325 this.oApi._fnFeatureHtmlTable = _fnFeatureHtmlTable; 6326 this.oApi._fnScrollDraw = _fnScrollDraw; 6327 this.oApi._fnAjustColumnSizing = _fnAjustColumnSizing; 6328 this.oApi._fnFeatureHtmlFilter = _fnFeatureHtmlFilter; 6329 this.oApi._fnFilterComplete = _fnFilterComplete; 6330 this.oApi._fnFilterCustom = _fnFilterCustom; 6331 this.oApi._fnFilterColumn = _fnFilterColumn; 6332 this.oApi._fnFilter = _fnFilter; 6333 this.oApi._fnBuildSearchArray = _fnBuildSearchArray; 6334 this.oApi._fnBuildSearchRow = _fnBuildSearchRow; 6335 this.oApi._fnFilterCreateSearch = _fnFilterCreateSearch; 6336 this.oApi._fnDataToSearch = _fnDataToSearch; 6337 this.oApi._fnSort = _fnSort; 6338 this.oApi._fnSortAttachListener = _fnSortAttachListener; 6339 this.oApi._fnSortingClasses = _fnSortingClasses; 6340 this.oApi._fnFeatureHtmlPaginate = _fnFeatureHtmlPaginate; 6341 this.oApi._fnPageChange = _fnPageChange; 6342 this.oApi._fnFeatureHtmlInfo = _fnFeatureHtmlInfo; 6343 this.oApi._fnUpdateInfo = _fnUpdateInfo; 6344 this.oApi._fnFeatureHtmlLength = _fnFeatureHtmlLength; 6345 this.oApi._fnFeatureHtmlProcessing = _fnFeatureHtmlProcessing; 6346 this.oApi._fnProcessingDisplay = _fnProcessingDisplay; 6347 this.oApi._fnVisibleToColumnIndex = _fnVisibleToColumnIndex; 6348 this.oApi._fnColumnIndexToVisible = _fnColumnIndexToVisible; 6349 this.oApi._fnNodeToDataIndex = _fnNodeToDataIndex; 6350 this.oApi._fnVisbleColumns = _fnVisbleColumns; 6351 this.oApi._fnCalculateEnd = _fnCalculateEnd; 6352 this.oApi._fnConvertToWidth = _fnConvertToWidth; 6353 this.oApi._fnCalculateColumnWidths = _fnCalculateColumnWidths; 6354 this.oApi._fnScrollingWidthAdjust = _fnScrollingWidthAdjust; 6355 this.oApi._fnGetWidestNode = _fnGetWidestNode; 6356 this.oApi._fnGetMaxLenString = _fnGetMaxLenString; 6357 this.oApi._fnStringToCss = _fnStringToCss; 6358 this.oApi._fnArrayCmp = _fnArrayCmp; 6359 this.oApi._fnDetectType = _fnDetectType; 6360 this.oApi._fnSettingsFromNode = _fnSettingsFromNode; 6361 this.oApi._fnGetDataMaster = _fnGetDataMaster; 6362 this.oApi._fnGetTrNodes = _fnGetTrNodes; 6363 this.oApi._fnGetTdNodes = _fnGetTdNodes; 6364 this.oApi._fnEscapeRegex = _fnEscapeRegex; 6365 this.oApi._fnDeleteIndex = _fnDeleteIndex; 6366 this.oApi._fnReOrderIndex = _fnReOrderIndex; 6367 this.oApi._fnColumnOrdering = _fnColumnOrdering; 6368 this.oApi._fnLog = _fnLog; 6369 this.oApi._fnClearTable = _fnClearTable; 6370 this.oApi._fnSaveState = _fnSaveState; 6371 this.oApi._fnLoadState = _fnLoadState; 6372 this.oApi._fnCreateCookie = _fnCreateCookie; 6373 this.oApi._fnReadCookie = _fnReadCookie; 6374 this.oApi._fnGetUniqueThs = _fnGetUniqueThs; 6375 this.oApi._fnScrollBarWidth = _fnScrollBarWidth; 6376 this.oApi._fnApplyToChildren = _fnApplyToChildren; 6377 this.oApi._fnMap = _fnMap; 6378 6379 6380 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 6381 * Section - Constructor 6382 */ 6383 6384 /* Want to be able to reference "this" inside the this.each function */ 6385 var _that = this; 6386 return this.each(function() 6387 { 6388 var i=0, iLen, j, jLen, k, kLen; 6389 6390 /* Check to see if we are re-initalising a table */ 6391 for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ ) 6392 { 6393 /* Base check on table node */
6394 if ( _aoSettings[i].nTable == this ) 6395 { 6396 if ( typeof oInit == 'undefined' || 6397 ( typeof oInit.bRetrieve != 'undefined' && oInit.bRetrieve === true ) ) 6398 { 6399 return _aoSettings[i].oInstance; 6400 } 6401 else if ( typeof oInit.bDestroy != 'undefined' && oInit.bDestroy === true ) 6402 { 6403 _aoSettings[i].oInstance.fnDestroy(); 6404 break; 6405 } 6406 else 6407 { 6408 _fnLog( _aoSettings[i], 0, "Cannot reinitialise DataTable.\n\n"+ 6409 "To retrieve the DataTables object for this table, please pass either no arguments "+ 6410 "to the dataTable() function, or set bRetrieve to true. Alternatively, to destory "+ 6411 "the old table and create a new one, set bDestroy to true (note that a lot of "+ 6412 "changes to the configuration can be made through the API which is usually much "+ 6413 "faster)." ); 6414 return; 6415 } 6416 } 6417 6418 /* If the element we are initialising has the same ID as a table which was previously 6419 * initialised, but the table nodes don't match (from before) then we destory the old 6420 * instance by simply deleting it. This is under the assumption that the table has been 6421 * destroyed by other methods. Anyone using non-id selectors will need to do this manually 6422 */ 6423 if ( _aoSettings[i].sTableId !== "" && _aoSettings[i].sTableId == this.getAttribute('id') ) 6424 { 6425 _aoSettings.splice( i, 1 ); 6426 break; 6427 } 6428 } 6429 6430 /* Make a complete and independent copy of the settings object */ 6431 var oSettings = new classSettings(); 6432 _aoSettings.push( oSettings ); 6433 6434 var bInitHandedOff = false; 6435 var bUsePassedData = false; 6436 6437 /* Set the id */ 6438 var sId = this.getAttribute( 'id' ); 6439 if ( sId !== null ) 6440 { 6441 oSettings.sTableId = sId; 6442 oSettings.sInstance = sId; 6443 } 6444 else 6445 { 6446 oSettings.sInstance = _oExt._oExternConfig.iNextUnique ++; 6447 } 6448 6449 /* Sanity check */ 6450 if ( this.nodeName.toLowerCase() != 'table' ) 6451 { 6452 _fnLog( oSettings, 0, "Attempted to initialise DataTables on a node which is not a "+ 6453 "table: "+this.nodeName ); 6454 return; 6455 } 6456 6457 /* Set the table node */ 6458 oSettings.nTable = this; 6459 6460 /* Keep a reference to the 'this' instance for the table. Note that if this table is being 6461 * created with others, we retrieve a unique instance to ease API access. 6462 */ 6463 oSettings.oInstance = _that.length == 1 ? _that : $(this).dataTable(); 6464 6465 /* Bind the API functions to the settings, so we can perform actions whenever oSettings is 6466 * available 6467 */ 6468 oSettings.oApi = _that.oApi; 6469 6470 /* State the table's width for if a destroy is called at a later time */ 6471 oSettings.sDestroyWidth = $(this).width(); 6472 6473 /* Store the features that we have available */ 6474 if ( typeof oInit != 'undefined' && oInit !== null ) 6475 { 6476 oSettings.oInit = oInit; 6477 _fnMap( oSettings.oFeatures, oInit, "bPaginate" ); 6478 _fnMap( oSettings.oFeatures, oInit, "bLengthChange" ); 6479 _fnMap( oSettings.oFeatures, oInit, "bFilter" ); 6480 _fnMap( oSettings.oFeatures, oInit, "bSort" ); 6481 _fnMap( oSettings.oFeatures, oInit, "bInfo" ); 6482 _fnMap( oSettings.oFeatures, oInit, "bProcessing" ); 6483 _fnMap( oSettings.oFeatures, oInit, "bAutoWidth" ); 6484 _fnMap( oSettings.oFeatures, oInit, "bSortClasses" ); 6485 _fnMap( oSettings.oFeatures, oInit, "bServerSide" ); 6486 _fnMap( oSettings.oScroll, oInit, "sScrollX", "sX" ); 6487 _fnMap( oSettings.oScroll, oInit, "sScrollXInner", "sXInner" ); 6488 _fnMap( oSettings.oScroll, oInit, "sScrollY", "sY" ); 6489 _fnMap( oSettings.oScroll, oInit, "bScrollCollapse", "bCollapse" ); 6490 _fnMap( oSettings.oScroll, oInit, "bScrollInfinite", "bInfinite" ); 6491 _fnMap( oSettings.oScroll, oInit, "iScrollLoadGap", "iLoadGap" ); 6492 _fnMap( oSettings.oScroll, oInit, "bScrollAutoCss", "bAutoCss" ); 6493 _fnMap( oSettings, oInit, "asStripClasses" ); 6494 _fnMap( oSettings, oInit, "fnRowCallback" ); 6495 _fnMap( oSettings, oInit, "fnHeaderCallback" ); 6496 _fnMap( oSettings, oInit, "fnFooterCallback" ); 6497 _fnMap( oSettings, oInit, "fnCookieCallback" ); 6498 _fnMap( oSettings, oInit, "fnInitComplete" ); 6499 _fnMap( oSettings, oInit, "fnServerData" ); 6500 _fnMap( oSettings, oInit, "fnFormatNumber" ); 6501 _fnMap( oSettings, oInit, "aaSorting" ); 6502 _fnMap( oSettings, oInit, "aaSortingFixed" );
6503 _fnMap( oSettings, oInit, "aLengthMenu" ); 6504 _fnMap( oSettings, oInit, "sPaginationType" ); 6505 _fnMap( oSettings, oInit, "sAjaxSource" ); 6506 _fnMap( oSettings, oInit, "iCookieDuration" ); 6507 _fnMap( oSettings, oInit, "sCookiePrefix" ); 6508 _fnMap( oSettings, oInit, "sDom" ); 6509 _fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" ); 6510 _fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" ); 6511 _fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" ); 6512 _fnMap( oSettings, oInit, "bJQueryUI", "bJUI" ); 6513 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); 6514 6515 /* Callback functions which are array driven */ 6516 if ( typeof oInit.fnDrawCallback == 'function' ) 6517 { 6518 oSettings.aoDrawCallback.push( { 6519 "fn": oInit.fnDrawCallback, 6520 "sName": "user" 6521 } ); 6522 } 6523 6524 if ( typeof oInit.fnStateSaveCallback == 'function' ) 6525 { 6526 oSettings.aoStateSave.push( { 6527 "fn": oInit.fnStateSaveCallback, 6528 "sName": "user" 6529 } ); 6530 } 6531 6532 if ( typeof oInit.fnStateLoadCallback == 'function' ) 6533 { 6534 oSettings.aoStateLoad.push( { 6535 "fn": oInit.fnStateLoadCallback, 6536 "sName": "user" 6537 } ); 6538 } 6539 6540 if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort && 6541 oSettings.oFeatures.bSortClasses ) 6542 { 6543 /* Enable sort classes for server-side processing. Safe to do it here, since server-side 6544 * processing must be enabled by the developer 6545 */ 6546 oSettings.aoDrawCallback.push( { 6547 "fn": _fnSortingClasses, 6548 "sName": "server_side_sort_classes" 6549 } ); 6550 } 6551 6552 if ( typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI ) 6553 { 6554 /* Use the JUI classes object for display. You could clone the oStdClasses object if 6555 * you want to have multiple tables with multiple independent classes 6556 */ 6557 oSettings.oClasses = _oExt.oJUIClasses; 6558 6559 if ( typeof oInit.sDom == 'undefined' ) 6560 { 6561 /* Set the DOM to use a layout suitable for jQuery UI's theming */ 6562 oSettings.sDom = '<"H"lfr>t<"F"ip>'; 6563 } 6564 } 6565 6566 /* Calculate the scroll bar width and cache it for use later on */ 6567 if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" ) 6568 { 6569 oSettings.oScroll.iBarWidth = _fnScrollBarWidth(); 6570 } 6571 6572 if ( typeof oInit.iDisplayStart != 'undefined' && 6573 typeof oSettings.iInitDisplayStart == 'undefined' ) 6574 { 6575 /* Display start point, taking into account the save saving */ 6576 oSettings.iInitDisplayStart = oInit.iDisplayStart; 6577 oSettings._iDisplayStart = oInit.iDisplayStart; 6578 } 6579 6580 /* Must be done after everything which can be overridden by a cookie! */ 6581 if ( typeof oInit.bStateSave != 'undefined' ) 6582 { 6583 oSettings.oFeatures.bStateSave = oInit.bStateSave; 6584 _fnLoadState( oSettings, oInit ); 6585 oSettings.aoDrawCallback.push( { 6586 "fn": _fnSaveState, 6587 "sName": "state_save" 6588 } ); 6589 } 6590 6591 if ( typeof oInit.aaData != 'undefined' ) 6592 { 6593 bUsePassedData = true; 6594 } 6595 6596 /* Backwards compatability */ 6597 /* aoColumns / aoData - remove at some point... */ 6598 if ( typeof oInit != 'undefined' && typeof oInit.aoData != 'undefined' ) 6599 { 6600 oInit.aoColumns = oInit.aoData; 6601 } 6602 6603 /* Language definitions */ 6604 if ( typeof oInit.oLanguage != 'undefined' ) 6605 { 6606 if ( typeof oInit.oLanguage.sUrl != 'undefined' && oInit.oLanguage.sUrl !== "" ) 6607 { 6608 /* Get the language definitions from a file */ 6609 oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl; 6610 $.getJSON( oSettings.oLanguage.sUrl, null, function( json ) { 6611 _fnLanguageProcess( oSettings, json, true ); } ); 6612 bInitHandedOff = true; 6613 } 6614 else 6615 { 6616 _fnLanguageProcess( oSettings, oInit.oLanguage, false ); 6617 } 6618 } 6619 /* Warning: The _fnLanguageProcess function is async to the remainder of this function due 6620 * to the XHR. We use _bInitialised in _fnLanguageProcess() to check this the processing 6621 * below is complete. The reason for spliting it like this is optimisation - we can fire 6622 * off the XHR (if needed) and then continue processing the data. 6623 */ 6624 } 6625 else 6626 {
6627 /* Create a dummy object for quick manipulation later on. */ 6628 oInit = {}; 6629 } 6630 6631 /* 6632 * Stripes 6633 * Add the strip classes now that we know which classes to apply - unless overruled 6634 */ 6635 if ( typeof oInit.asStripClasses == 'undefined' ) 6636 { 6637 oSettings.asStripClasses.push( oSettings.oClasses.sStripOdd ); 6638 oSettings.asStripClasses.push( oSettings.oClasses.sStripEven ); 6639 } 6640 6641 /* Remove row stripe classes if they are already on the table row */ 6642 var bStripeRemove = false; 6643 var anRows = $('>tbody>tr', this); 6644 for ( i=0, iLen=oSettings.asStripClasses.length ; i<iLen ; i++ ) 6645 { 6646 if ( anRows.filter(":lt(2)").hasClass( oSettings.asStripClasses[i]) ) 6647 { 6648 bStripeRemove = true; 6649 break; 6650 } 6651 } 6652 6653 if ( bStripeRemove ) 6654 { 6655 /* Store the classes which we are about to remove so they can be readded on destory */ 6656 oSettings.asDestoryStrips = [ '', '' ]; 6657 if ( $(anRows[0]).hasClass(oSettings.oClasses.sStripOdd) ) 6658 { 6659 oSettings.asDestoryStrips[0] += oSettings.oClasses.sStripOdd+" "; 6660 } 6661 if ( $(anRows[0]).hasClass(oSettings.oClasses.sStripEven) ) 6662 { 6663 oSettings.asDestoryStrips[0] += oSettings.oClasses.sStripEven; 6664 } 6665 if ( $(anRows[1]).hasClass(oSettings.oClasses.sStripOdd) ) 6666 { 6667 oSettings.asDestoryStrips[1] += oSettings.oClasses.sStripOdd+" "; 6668 } 6669 if ( $(anRows[1]).hasClass(oSettings.oClasses.sStripEven) ) 6670 { 6671 oSettings.asDestoryStrips[1] += oSettings.oClasses.sStripEven; 6672 } 6673 6674 anRows.removeClass( oSettings.asStripClasses.join(' ') ); 6675 } 6676 6677 /* 6678 * Columns 6679 * See if we should load columns automatically or use defined ones 6680 */ 6681 var nThead = this.getElementsByTagName('thead'); 6682 var anThs = nThead.length===0 ? [] : _fnGetUniqueThs( nThead[0] ); 6683 var aoColumnsInit; 6684 6685 /* If not given a column array, generate one with nulls */ 6686 if ( typeof oInit.aoColumns == 'undefined' ) 6687 { 6688 aoColumnsInit = []; 6689 for ( i=0, iLen=anThs.length ; i<iLen ; i++ ) 6690 { 6691 aoColumnsInit.push( null ); 6692 } 6693 } 6694 else 6695 { 6696 aoColumnsInit = oInit.aoColumns; 6697 } 6698 6699 /* Add the columns */ 6700 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ ) 6701 { 6702 /* Check if we have column visibilty state to restore */ 6703 if ( typeof oInit.saved_aoColumns != 'undefined' && oInit.saved_aoColumns.length == iLen ) 6704 { 6705 if ( aoColumnsInit[i] === null ) 6706 { 6707 aoColumnsInit[i] = {}; 6708 } 6709 aoColumnsInit[i].bVisible = oInit.saved_aoColumns[i].bVisible; 6710 } 6711 6712 _fnAddColumn( oSettings, anThs ? anThs[i] : null ); 6713 } 6714 6715 /* Add options from column definations */ 6716 if ( typeof oInit.aoColumnDefs != 'undefined' ) 6717 { 6718 /* Loop over the column defs array - loop in reverse so first instace has priority */ 6719 for ( i=oInit.aoColumnDefs.length-1 ; i>=0 ; i-- ) 6720 { 6721 /* Each column def can target multiple columns, as it is an array */ 6722 var aTargets = oInit.aoColumnDefs[i].aTargets; 6723 if ( !$.isArray( aTargets ) ) 6724 { 6725 _fnLog( oSettings, 1, 'aTargets must be an array of targets, not a '+(typeof aTargets) ); 6726 } 6727 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ ) 6728 { 6729 if ( typeof aTargets[j] == 'number' && aTargets[j] >= 0 ) 6730 { 6731 /* 0+ integer, left to right column counting. We add columns which are unknown 6732 * automatically. Is this the right behaviour for this? We should at least 6733 * log it in future. We cannot do this for the negative or class targets, only here. 6734 */ 6735 while( oSettings.aoColumns.length <= aTargets[j] ) 6736 { 6737 _fnAddColumn( oSettings ); 6738 } 6739 _fnColumnOptions( oSettings, aTargets[j], oInit.aoColumnDefs[i] ); 6740 } 6741 else if ( typeof aTargets[j] == 'number' && aTargets[j] < 0 ) 6742 { 6743 /* Negative integer, right to left column counting */ 6744 _fnColumnOptions( oSettings, oSettings.aoColumns.length+aTargets[j], 6745 oInit.aoColumnDefs[i] ); 6746 } 6747 else if ( typeof aTargets[j] == 'string' ) 6748 { 6749 /* Class name matching on TH element */
6750 for ( k=0, kLen=oSettings.aoColumns.length ; k<kLen ; k++ ) 6751 { 6752 if ( aTargets[j] == "_all" || 6753 oSettings.aoColumns[k].nTh.className.indexOf( aTargets[j] ) != -1 ) 6754 { 6755 _fnColumnOptions( oSettings, k, oInit.aoColumnDefs[i] ); 6756 } 6757 } 6758 } 6759 } 6760 } 6761 } 6762 6763 /* Add options from column array - after the defs array so this has priority */ 6764 if ( typeof aoColumnsInit != 'undefined' ) 6765 { 6766 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ ) 6767 { 6768 _fnColumnOptions( oSettings, i, aoColumnsInit[i] ); 6769 } 6770 } 6771 6772 /* 6773 * Sorting 6774 * Check the aaSorting array 6775 */ 6776 for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ ) 6777 { 6778 if ( oSettings.aaSorting[i][0] >= oSettings.aoColumns.length ) 6779 { 6780 oSettings.aaSorting[i][0] = 0; 6781 } 6782 var oColumn = oSettings.aoColumns[ oSettings.aaSorting[i][0] ]; 6783 6784 /* Add a default sorting index */ 6785 if ( typeof oSettings.aaSorting[i][2] == 'undefined' ) 6786 { 6787 oSettings.aaSorting[i][2] = 0; 6788 } 6789 6790 /* If aaSorting is not defined, then we use the first indicator in asSorting */ 6791 if ( typeof oInit.aaSorting == "undefined" && 6792 typeof oSettings.saved_aaSorting == "undefined" ) 6793 { 6794 oSettings.aaSorting[i][1] = oColumn.asSorting[0]; 6795 } 6796 6797 /* Set the current sorting index based on aoColumns.asSorting */ 6798 for ( j=0, jLen=oColumn.asSorting.length ; j<jLen ; j++ ) 6799 { 6800 if ( oSettings.aaSorting[i][1] == oColumn.asSorting[j] ) 6801 { 6802 oSettings.aaSorting[i][2] = j; 6803 break; 6804 } 6805 } 6806 } 6807 6808 /* Do a first pass on the sorting classes (allows any size changes to be taken into 6809 * account, and also will apply sorting disabled classes if disabled 6810 */ 6811 _fnSortingClasses( oSettings ); 6812 6813 /* 6814 * Final init 6815 * Sanity check that there is a thead and tbody. If not let's just create them 6816 */ 6817 if ( this.getElementsByTagName('thead').length === 0 ) 6818 { 6819 this.appendChild( document.createElement( 'thead' ) ); 6820 } 6821 6822 if ( this.getElementsByTagName('tbody').length === 0 ) 6823 { 6824 this.appendChild( document.createElement( 'tbody' ) ); 6825 } 6826 6827 oSettings.nTHead = this.getElementsByTagName('thead')[0]; 6828 oSettings.nTBody = this.getElementsByTagName('tbody')[0]; 6829 if ( this.getElementsByTagName('tfoot').length > 0 ) 6830 { 6831 oSettings.nTFoot = this.getElementsByTagName('tfoot')[0]; 6832 } 6833 6834 /* Check if there is data passing into the constructor */ 6835 if ( bUsePassedData ) 6836 { 6837 for ( i=0 ; i<oInit.aaData.length ; i++ ) 6838 { 6839 _fnAddData( oSettings, oInit.aaData[ i ] ); 6840 } 6841 } 6842 else 6843 { 6844 /* Grab the data from the page */ 6845 _fnGatherData( oSettings ); 6846 } 6847 6848 /* Copy the data index array */ 6849 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 6850 6851 /* Initialisation complete - table can be drawn */ 6852 oSettings.bInitialised = true; 6853 6854 /* Check if we need to initialise the table (it might not have been handed off to the 6855 * language processor) 6856 */ 6857 if ( bInitHandedOff === false ) 6858 { 6859 _fnInitalise( oSettings ); 6860 } 6861 }); 6862 }; 6863})(jQuery, window, document);
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.