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