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