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