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