1/** 2 * @summary FixedColumns 3 * @description Freeze columns in place on a scrolling DataTable 4 * @file FixedColumns.js 5 * @version 2.0.3 6 * @author Allan Jardine (www.sprymedia.co.uk) 7 * @license GPL v2 or BSD 3 point style 8 * @contact www.sprymedia.co.uk/contact 9 * 10 * @copyright Copyright 2010-2011 Allan Jardine, all rights reserved. 11 * 12 * This source file is free software, under either the GPL v2 license or a 13 * BSD style license, available at: 14 * http://datatables.net/license_gpl2 15 * http://datatables.net/license_bsd 16 */ 17 18 19/* Global scope for FixedColumns */ 20var FixedColumns; 21 22(function($, window, document) { 23 24 25/** 26 * When making use of DataTables' x-axis scrolling feature, you may wish to 27 * fix the left most column in place. This plug-in for DataTables provides 28 * exactly this option (note for non-scrolling tables, please use the 29 * FixedHeader plug-in, which can fix headers, footers and columns). Key 30 * features include: 31 * <ul class="limit_length"> 32 * <li>Freezes the left or right most columns to the side of the table</li> 33 * <li>Option to freeze two or more columns</li> 34 * <li>Full integration with DataTables' scrolling options</li> 35 * <li>Speed - FixedColumns is fast in its operation</li> 36 * </ul> 37 * 38 * @class 39 * @constructor 40 * @param {object} oDT DataTables instance 41 * @param {object} [oInit={}] Configuration object for FixedColumns. Options are defined by {@link FixedColumns.defaults} 42 * 43 * @requires jQuery 1.3+ 44 * @requires DataTables 1.8.0+ 45 * 46 * @example 47 * var oTable = $('#example').dataTable( { 48 * "sScrollX": "100%" 49 * } ); 50 * new FixedColumns( oTable ); 51 */ 52FixedColumns = function ( oDT, oInit ) { 53 /* Sanity check - you just know it will happen */ 54 if ( ! this instanceof FixedColumns ) 55 { 56 alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." ); 57 return; 58 } 59 60 if ( typeof oInit == 'undefined' ) 61 { 62 oInit = {}; 63 } 64 65 /** 66 * Settings object which contains customisable information for FixedColumns instance 67 * @namespace 68 * @extends FixedColumns.defaults 69 */ 70 this.s = { 71 /** 72 * DataTables settings objects 73 * @type object 74 * @default Obtained from DataTables instance 75 */ 76 "dt": oDT.fnSettings(), 77 78 /** 79 * Number of columns in the DataTable - stored for quick access 80 * @type int 81 * @default Obtained from DataTables instance 82 */ 83 "iTableColumns": oDT.fnSettings().aoColumns.length, 84 85 /** 86 * Original widths of the columns as rendered by DataTables 87 * @type array.<int> 88 * @default [] 89 */ 90 "aiWidths": [], 91 92 /** 93 * Flag to indicate if we are dealing with IE6/7 as these browsers need a little hack 94 * in the odd place 95 * @type boolean 96 * @default Automatically calculated 97 * @readonly 98 */ 99 "bOldIE": ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0")) 100 }; 101 102 103 /** 104 * DOM elements used by the class instance 105 * @namespace 106 * 107 */ 108 this.dom = { 109 /** 110 * DataTables scrolling element 111 * @type node 112 * @default null 113 */ 114 "scroller": null, 115 116 /** 117 * DataTables header table 118 * @type node 119 * @default null 120 */ 121 "header": null, 122 123 /** 124 * DataTables body table 125 * @type node 126 * @default null 127 */ 128 "body": null, 129 130 /** 131 * DataTables footer table 132 * @type node 133 * @default null 134 */ 135 "footer": null, 136 137 /** 138 * Display grid elements 139 * @namespace 140 */ 141 "grid": { 142 /** 143 * Grid wrapper. This is the container element for the 3x3 grid 144 * @type node 145 * @default null 146 */ 147 "wrapper": null, 148 149 /** 150 * DataTables scrolling element. This element is the DataTables 151 * component in the display grid (making up the main table - i.e. 152 * not the fixed columns). 153 * @type node 154 * @default null 155 */ 156 "dt": null, 157 158 /** 159 * Left fixed column grid components 160 * @namespace 161 */ 162 "left": { 163 "wrapper": null, 164 "head": null, 165 "body": null, 166 "foot": null 167 }, 168 169 /** 170 * Right fixed column grid components 171 * @namespace 172 */ 173 "right": { 174 "wrapper": null, 175 "head": null, 176 "body": null, 177 "foot": null 178 } 179 }, 180 181 /** 182 * Cloned table nodes 183 * @namespace 184 */
185 "clone": { 186 /** 187 * Left column cloned table nodes 188 * @namespace 189 */ 190 "left": { 191 /** 192 * Cloned header table 193 * @type node 194 * @default null 195 */ 196 "header": null, 197 198 /** 199 * Cloned body table 200 * @type node 201 * @default null 202 */ 203 "body": null, 204 205 /** 206 * Cloned footer table 207 * @type node 208 * @default null 209 */ 210 "footer": null 211 }, 212 213 /** 214 * Right column cloned table nodes 215 * @namespace 216 */ 217 "right": { 218 /** 219 * Cloned header table 220 * @type node 221 * @default null 222 */ 223 "header": null, 224 225 /** 226 * Cloned body table 227 * @type node 228 * @default null 229 */ 230 "body": null, 231 232 /** 233 * Cloned footer table 234 * @type node 235 * @default null 236 */ 237 "footer": null 238 } 239 } 240 }; 241 242 /* Attach the instance to the DataTables instance so it can be accessed easily */ 243 this.s.dt.oFixedColumns = this; 244 245 /* Let's do it */ 246 this._fnConstruct( oInit ); 247}; 248 249 250 251FixedColumns.prototype = { 252 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 253 * Public methods 254 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 255 256 /** 257 * Update the fixed columns - including headers and footers. Note that FixedColumns will 258 * automatically update the display whenever the host DataTable redraws. 259 * @returns {void} 260 * @example 261 * var oTable = $('#example').dataTable( { 262 * "sScrollX": "100%" 263 * } ); 264 * var oFC = new FixedColumns( oTable ); 265 * 266 * // at some later point when the table has been manipulated.... 267 * oFC.fnUpdate(); 268 */ 269 "fnUpdate": function () 270 { 271 this._fnDraw( true ); 272 }, 273 274 275 /** 276 * Recalculate the resizes of the 3x3 grid that FixedColumns uses for display of the table. 277 * This is useful if you update the width of the table container. Note that FixedColumns will 278 * perform this function automatically when the window.resize event is fired. 279 * @returns {void} 280 * @example 281 * var oTable = $('#example').dataTable( { 282 * "sScrollX": "100%" 283 * } ); 284 * var oFC = new FixedColumns( oTable ); 285 * 286 * // Resize the table container and then have FixedColumns adjust its layout.... 287 * $('#content').width( 1200 ); 288 * oFC.fnRedrawLayout(); 289 */ 290 "fnRedrawLayout": function () 291 { 292 this._fnGridLayout(); 293 }, 294 295 296 /** 297 * Mark a row such that it's height should be recalculated when using 'semiauto' row 298 * height matching. This function will have no effect when 'none' or 'auto' row height 299 * matching is used. 300 * @param {Node} nTr TR element that should have it's height recalculated 301 * @returns {void} 302 * @example 303 * var oTable = $('#example').dataTable( { 304 * "sScrollX": "100%" 305 * } ); 306 * var oFC = new FixedColumns( oTable ); 307 * 308 * // manipulate the table - mark the row as needing an update then update the table 309 * // this allows the redraw performed by DataTables fnUpdate to recalculate the row 310 * // height 311 * oFC.fnRecalculateHeight(); 312 * oTable.fnUpdate( $('#example tbody tr:eq(0)')[0], ["insert date", 1, 2, 3 ... ]); 313 */ 314 "fnRecalculateHeight": function ( nTr ) 315 { 316 nTr._DTTC_iHeight = null; 317 nTr.style.height = 'auto'; 318 }, 319 320 321 /** 322 * Set the height of a given row - provides cross browser compatibility 323 * @param {Node} nTarget TR element that should have it's height recalculated 324 * @param {int} iHeight Height in pixels to set 325 * @returns {void} 326 * @example 327 * var oTable = $('#example').dataTable( { 328 * "sScrollX": "100%" 329 * } ); 330 * var oFC = new FixedColumns( oTable ); 331 * 332 * // You may want to do this after manipulating a row in the fixed column 333 * oFC.fnSetRowHeight( $('#example tbody tr:eq(0)')[0], 50 ); 334 */ 335 "fnSetRowHeight": function ( nTarget, iHeight ) 336 { 337 var jqBoxHack = $(nTarget).children(':first'); 338 var iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(); 339 340 /* Can we use some kind of object detection here?! This is very nasty - damn browsers */ 341 if ( $.browser.mozilla || $.browser.opera ) 342 { 343 nTarget.style.height = iHeight+"px"; 344 } 345 else 346 { 347 $(nTarget).children().height( iHeight-iBoxHack ); 348 } 349 }, 350 351 352 353 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 354 * Private methods (they are of course public in JS, but recommended as private) 355 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 356 357 /** 358 * Initialisation for FixedColumns 359 * @param {Object} oInit User settings for initialisation 360 * @returns {void} 361 * @private 362 */ 363 "_fnConstruct": function ( oInit ) 364 { 365 var i, iLen, iWidth, 366 that = this; 367
368 /* Sanity checking */ 369 if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' || 370 this.s.dt.oInstance.fnVersionCheck( '1.8.0' ) !== true ) 371 { 372 alert( "FixedColumns "+FixedColumns.VERSION+" required DataTables 1.8.0 or later. "+ 373 "Please upgrade your DataTables installation" ); 374 return; 375 } 376 377 if ( this.s.dt.oScroll.sX === "" ) 378 { 379 this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+ 380 "x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+ 381 "column fixing when scrolling is not enabled" ); 382 return; 383 } 384 385 /* Apply the settings from the user / defaults */ 386 this.s = $.extend( true, this.s, FixedColumns.defaults, oInit ); 387 388 /* Set up the DOM as we need it and cache nodes */ 389 this.dom.grid.dt = $(this.s.dt.nTable).parents('div.dataTables_scroll')[0]; 390 this.dom.scroller = $('div.dataTables_scrollBody', this.dom.grid.dt )[0]; 391 392 var iScrollWidth = $(this.dom.grid.dt).width(); 393 var iLeftWidth = 0; 394 var iRightWidth = 0; 395 396 $('tbody>tr:eq(0)>td', this.s.dt.nTable).each( function (i) { 397 iWidth = $(this).outerWidth(); 398 that.s.aiWidths.push( iWidth ); 399 if ( i < that.s.iLeftColumns ) 400 { 401 iLeftWidth += iWidth; 402 } 403 if ( that.s.iTableColumns-that.s.iRightColumns <= i ) 404 { 405 iRightWidth += iWidth; 406 } 407 } ); 408 409 if ( this.s.iLeftWidth === null ) 410 { 411 this.s.iLeftWidth = this.s.sLeftWidth == 'fixed' ? 412 iLeftWidth : (iLeftWidth/iScrollWidth) * 100; 413 } 414 415 if ( this.s.iRightWidth === null ) 416 { 417 this.s.iRightWidth = this.s.sRightWidth == 'fixed' ? 418 iRightWidth : (iRightWidth/iScrollWidth) * 100; 419 } 420 421 /* Set up the DOM that we want for the fixed column layout grid */ 422 this._fnGridSetup(); 423 424 /* Use the DataTables API method fnSetColumnVis to hide the columns we are going to fix */ 425 for ( i=0 ; i<this.s.iLeftColumns ; i++ ) 426 { 427 this.s.dt.oInstance.fnSetColumnVis( i, false ); 428 } 429 for ( i=this.s.iTableColumns - this.s.iRightColumns ; i<this.s.iTableColumns ; i++ ) 430 { 431 this.s.dt.oInstance.fnSetColumnVis( i, false ); 432 } 433 434 /* Event handlers */ 435 $(this.dom.scroller).scroll( function () { 436 that.dom.grid.left.body.scrollTop = that.dom.scroller.scrollTop; 437 if ( that.s.iRightColumns > 0 ) 438 { 439 that.dom.grid.right.body.scrollTop = that.dom.scroller.scrollTop; 440 } 441 } ); 442 443 $(window).resize( function () { 444 that._fnGridLayout.call( that ); 445 } ); 446
447 var bFirstDraw = true; 448 this.s.dt.aoDrawCallback = [ { 449 "fn": function () { 450 that._fnDraw.call( that, bFirstDraw ); 451 that._fnGridHeight( that ); 452 bFirstDraw = false; 453 }, 454 "sName": "FixedColumns" 455 } ].concat( this.s.dt.aoDrawCallback ); 456 457 /* Get things right to start with - note that due to adjusting the columns, there must be 458 * another redraw of the main table. It doesn't need to be a full redraw however. 459 */ 460 this._fnGridLayout(); 461 this._fnGridHeight(); 462 this.s.dt.oInstance.fnDraw(false); 463 }, 464 465 466 /** 467 * Set up the DOM for the fixed column. The way the layout works is to create a 1x3 grid 468 * for the left column, the DataTable (for which we just reuse the scrolling element DataTable 469 * puts into the DOM) and the right column. In each of he two fixed column elements there is a 470 * grouping wrapper element and then a head, body and footer wrapper. In each of these we then 471 * place the cloned header, body or footer tables. This effectively gives as 3x3 grid structure. 472 * @returns {void} 473 * @private 474 */ 475 "_fnGridSetup": function () 476 { 477 var that = this; 478 479 this.dom.body = this.s.dt.nTable; 480 this.dom.header = this.s.dt.nTHead.parentNode; 481 this.dom.header.parentNode.parentNode.style.position = "relative"; 482 483 var nSWrapper = 484 $('<div class="DTFC_ScrollWrapper" style="position:relative; clear:both;">'+ 485 '<div class="DTFC_LeftWrapper" style="position:absolute; top:0; left:0;">'+ 486 '<div class="DTFC_LeftHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+ 487 '<div class="DTFC_LeftBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+ 488 '<div class="DTFC_LeftFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+ 489 '</div>'+ 490 '<div class="DTFC_RightWrapper" style="position:absolute; top:0; left:0;">'+ 491 '<div class="DTFC_RightHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+ 492 '<div class="DTFC_RightBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+ 493 '<div class="DTFC_RightFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+ 494 '</div>'+ 495 '</div>')[0]; 496 nLeft = nSWrapper.childNodes[0]; 497 nRight = nSWrapper.childNodes[1]; 498 499 this.dom.grid.wrapper = nSWrapper; 500 this.dom.grid.left.wrapper = nLeft; 501 this.dom.grid.left.head = nLeft.childNodes[0]; 502 this.dom.grid.left.body = nLeft.childNodes[1]; 503 504 if ( this.s.iRightColumns > 0 ) 505 { 506 this.dom.grid.right.wrapper = nRight; 507 this.dom.grid.right.head = nRight.childNodes[0]; 508 this.dom.grid.right.body = nRight.childNodes[1]; 509 } 510 511 if ( this.s.dt.nTFoot ) 512 { 513 this.dom.footer = this.s.dt.nTFoot.parentNode; 514 this.dom.grid.left.foot = nLeft.childNodes[2]; 515 if ( this.s.iRightColumns > 0 ) 516 { 517 this.dom.grid.right.foot = nRight.childNodes[2]; 518 } 519 } 520 521 nSWrapper.appendChild( nLeft ); 522 this.dom.grid.dt.parentNode.insertBefore( nSWrapper, this.dom.grid.dt ); 523 nSWrapper.appendChild( this.dom.grid.dt ); 524 525 this.dom.grid.dt.style.position = "absolute"; 526 this.dom.grid.dt.style.top = "0px"; 527 this.dom.grid.dt.style.left = this.s.iLeftWidth+"px"; 528 this.dom.grid.dt.style.width = ($(this.dom.grid.dt).width()-this.s.iLeftWidth-this.s.iRightWidth)+"px"; 529 }, 530 531 532 /** 533 * Style and position the grid used for the FixedColumns layout based on the instance settings. 534 * Specifically sLeftWidth ('fixed' or 'absolute'), iLeftWidth (px if fixed, % if absolute) and 535 * there 'right' counterparts. 536 * @returns {void} 537 * @private 538 */ 539 "_fnGridLayout": function () 540 { 541 var oGrid = this.dom.grid; 542 var iTotal = $(oGrid.wrapper).width(); 543 var iLeft = 0, iRight = 0, iRemainder = 0; 544 545 if ( this.s.sLeftWidth == 'fixed' ) 546 { 547 iLeft = this.s.iLeftWidth; 548 } 549 else 550 { 551 iLeft = ( this.s.iLeftWidth / 100 ) * iTotal; 552 } 553 554 if ( this.s.sRightWidth == 'fixed' ) 555 { 556 iRight = this.s.iRightWidth; 557 } 558 else 559 { 560 iRight = ( this.s.iRightWidth / 100 ) * iTotal; 561 } 562 563 iRemainder = iTotal - iLeft - iRight; 564 565 oGrid.left.wrapper.style.width = iLeft+"px"; 566 oGrid.dt.style.width = iRemainder+"px"; 567 oGrid.dt.style.left = iLeft+"px"; 568 569 if ( this.s.iRightColumns > 0 ) 570 { 571 oGrid.right.wrapper.style.width = iRight+"px"; 572 oGrid.right.wrapper.style.left = (iTotal-iRight)+"px"; 573 } 574 }, 575 576 577 /** 578 * Recalculate and set the height of the grid components used for positioning of the 579 * FixedColumn display grid. 580 * @returns {void} 581 * @private 582 */ 583 "_fnGridHeight": function () 584 { 585 var oGrid = this.dom.grid; 586 var iHeight = $(this.dom.grid.dt).height(); 587 588 oGrid.wrapper.style.height = iHeight+"px"; 589 oGrid.left.body.style.height = $(this.dom.scroller).height()+"px"; 590 oGrid.left.wrapper.style.height = iHeight+"px"; 591 592 if ( this.s.iRightColumns > 0 ) 593 { 594 oGrid.right.wrapper.style.height = iHeight+"px"; 595 oGrid.right.body.style.height = $(this.dom.scroller).height()+"px"; 596 } 597 }, 598 599 600 /** 601 * Clone and position the fixed columns 602 * @returns {void} 603 * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true) 604 * @private 605 */ 606 "_fnDraw": function ( bAll ) 607 { 608 this._fnCloneLeft( bAll ); 609 this._fnCloneRight( bAll ); 610 611 /* Draw callback function */ 612 if ( this.s.fnDrawCallback !== null ) 613 { 614 this.s.fnDrawCallback.call( this, this.dom.clone.left, this.dom.clone.right ); 615 } 616 617 /* Event triggering */ 618 $(this).trigger( 'draw', {
619 "leftClone": this.dom.clone.left, 620 "rightClone": this.dom.clone.right 621 } ); 622 }, 623 624 625 /** 626 * Clone the right columns 627 * @returns {void} 628 * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true) 629 * @private 630 */ 631 "_fnCloneRight": function ( bAll ) 632 { 633 if ( this.s.iRightColumns <= 0 ) 634 { 635 return; 636 } 637 638 var that = this, 639 i, jq, 640 aiColumns = []; 641 642 for ( i=this.s.iTableColumns-this.s.iRightColumns ; i<this.s.iTableColumns ; i++ ) 643 { 644 aiColumns.push( i ); 645 } 646 647 this._fnClone( this.dom.clone.right, this.dom.grid.right, aiColumns, bAll ); 648 }, 649 650 651 /** 652 * Clone the left columns 653 * @returns {void} 654 * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true) 655 * @private 656 */ 657 "_fnCloneLeft": function ( bAll ) 658 { 659 if ( this.s.iLeftColumns <= 0 ) 660 { 661 return; 662 } 663 664 var that = this, 665 i, jq, 666 aiColumns = []; 667 668 for ( i=0 ; i<this.s.iLeftColumns ; i++ ) 669 { 670 aiColumns.push( i ); 671 } 672 673 this._fnClone( this.dom.clone.left, this.dom.grid.left, aiColumns, bAll ); 674 }, 675 676 677 /** 678 * Make a copy of the layout object for a header or footer element from DataTables. Note that 679 * this method will clone the nodes in the layout object. 680 * @returns {Array} Copy of the layout array 681 * @param {Object} aoOriginal Layout array from DataTables (aoHeader or aoFooter) 682 * @param {Object} aiColumns Columns to copy 683 * @private 684 */ 685 "_fnCopyLayout": function ( aoOriginal, aiColumns ) 686 { 687 var aReturn = []; 688 var aClones = []; 689 var aCloned = []; 690 691 for ( var i=0, iLen=aoOriginal.length ; i<iLen ; i++ ) 692 { 693 var aRow = []; 694 aRow.nTr = $(aoOriginal[i].nTr).clone(true)[0]; 695 696 for ( var j=0, jLen=this.s.iTableColumns ; j<jLen ; j++ ) 697 { 698 if ( $.inArray( j, aiColumns ) === -1 ) 699 { 700 continue; 701 } 702 703 var iCloned = $.inArray( aoOriginal[i][j].cell, aCloned ); 704 if ( iCloned === -1 ) 705 { 706 var nClone = $(aoOriginal[i][j].cell).clone(true)[0]; 707 aClones.push( nClone ); 708 aCloned.push( aoOriginal[i][j].cell ); 709 710 aRow.push( { 711 "cell": nClone, 712 "unique": aoOriginal[i][j].unique 713 } ); 714 } 715 else 716 { 717 aRow.push( { 718 "cell": aClones[ iCloned ], 719 "unique": aoOriginal[i][j].unique 720 } ); 721 } 722 } 723 724 aReturn.push( aRow ); 725 } 726 727 return aReturn; 728 }, 729 730 731 /** 732 * Clone the DataTable nodes and place them in the DOM (sized correctly) 733 * @returns {void} 734 * @param {Object} oClone Object containing the header, footer and body cloned DOM elements 735 * @param {Object} oGrid Grid object containing the display grid elements for the cloned 736 * column (left or right) 737 * @param {Array} aiColumns Column indexes which should be operated on from the DataTable 738 * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true) 739 * @private 740 */ 741 "_fnClone": function ( oClone, oGrid, aiColumns, bAll ) 742 { 743 var that = this, 744 i, iLen, j, jLen, jq, nTarget, iColumn, nClone, iIndex; 745 746 /* 747 * Header 748 */ 749 if ( bAll ) 750 { 751 if ( oClone.header !== null ) 752 { 753 oClone.header.parentNode.removeChild( oClone.header ); 754 } 755 oClone.header = $(this.dom.header).clone(true)[0]; 756 oClone.header.className += " DTFC_Cloned"; 757 oClone.header.style.width = "100%"; 758 oGrid.head.appendChild( oClone.header ); 759 760 /* Copy the DataTables layout cache for the header for our floating column */ 761 var aoCloneLayout = this._fnCopyLayout( this.s.dt.aoHeader, aiColumns ); 762 var jqCloneThead = $('>thead', oClone.header); 763 jqCloneThead.empty(); 764 765 /* Add the created cloned TR elements to the table */ 766 for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ ) 767 { 768 jqCloneThead[0].appendChild( aoCloneLayout[i].nTr ); 769 } 770 771 /* Use the handy _fnDrawHead function in DataTables to do the rowspan/colspan 772 * calculations for us 773 */ 774 this.s.dt.oApi._fnDrawHead( this.s.dt, aoCloneLayout, true ); 775 } 776 else 777 { 778 /* To ensure that we copy cell classes exactly, regardless of colspan, multiple rows 779 * etc, we make a copy of the header from the DataTable again, but don't insert the 780 * cloned cells, just copy the classes across. To get the matching layout for the 781 * fixed component, we use the DataTables _fnDetectHeader method, allowing 1:1 mapping 782 */ 783 var aoCloneLayout = this._fnCopyLayout( this.s.dt.aoHeader, aiColumns ); 784 var aoCurrHeader=[]; 785 786 this.s.dt.oApi._fnDetectHeader( aoCurrHeader, $('>thead', oClone.header)[0] ); 787 788 for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ ) 789 { 790 for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ ) 791 { 792 aoCurrHeader[i][j].cell.className = aoCloneLayout[i][j].cell.className; 793 794 // If jQuery UI theming is used we need to copy those elements as well 795 $('span.DataTables_sort_icon', aoCurrHeader[i][j].cell).each( function () { 796 this.className = $('span.DataTables_sort_icon', aoCloneLayout[i][j].cell)[0].className; 797 } ); 798 } 799 } 800 } 801 this._fnEqualiseHeights( 'thead', this.dom.header, oClone.header ); 802 803 /* 804 * Body 805 */ 806 if ( this.s.sHeightMatch == 'auto' ) 807 { 808 /* Remove any heights which have been applied already and let the browser figure it out */ 809 $('>tbody>tr', that.dom.body).css('height', 'auto'); 810 } 811 812 if ( oClone.body !== null ) 813 { 814 oClone.body.parentNode.removeChild( oClone.body ); 815 oClone.body = null; 816 } 817 818 oClone.body = $(this.dom.body).clone(true)[0]; 819 oClone.body.className += " DTFC_Cloned"; 820 oClone.body.style.paddingBottom = this.s.dt.oScroll.iBarWidth+"px"; 821 oClone.body.style.marginBottom = (this.s.dt.oScroll.iBarWidth*2)+"px"; /* For IE */ 822 if ( oClone.body.getAttribute('id') !== null ) 823 { 824 oClone.body.removeAttribute('id'); 825 } 826 827 $('>thead>tr', oClone.body).empty(); 828 $('>tfoot', oClone.body).remove(); 829 830 var nBody = $('tbody', oClone.body)[0]; 831 $(nBody).empty(); 832 if ( this.s.dt.aiDisplay.length > 0 ) 833 { 834 /* Copy the DataTables' header elements to force the column width in exactly the 835 * same way that DataTables does it - have the header element, apply the width and 836 * colapse it down 837 */ 838 var nInnerThead = $('>thead>tr', oClone.body)[0]; 839 for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ ) 840 { 841 iColumn = aiColumns[iIndex]; 842 843 nClone = this.s.dt.aoColumns[iColumn].nTh; 844 nClone.innerHTML = ""; 845 846 oStyle = nClone.style; 847 oStyle.paddingTop = "0"; 848 oStyle.paddingBottom = "0"; 849 oStyle.borderTopWidth = "0"; 850 oStyle.borderBottomWidth = "0"; 851 oStyle.height = 0; 852 oStyle.width = that.s.aiWidths[iColumn]+"px"; 853 854 nInnerThead.appendChild( nClone ); 855 } 856 857 /* Add in the tbody elements, cloning form the master table */ 858 $('>tbody>tr', that.dom.body).each( function (z) { 859 var n = this.cloneNode(false); 860 var i = that.s.dt.oFeatures.bServerSide===false ? 861 that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z; 862 for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ ) 863 { 864 iColumn = aiColumns[iIndex]; 865 if ( typeof that.s.dt.aoData[i]._anHidden[iColumn] != 'undefined' ) 866 { 867 nClone = $(that.s.dt.aoData[i]._anHidden[iColumn]).clone(true)[0]; 868 n.appendChild( nClone ); 869 } 870 }
871 nBody.appendChild( n ); 872 } ); 873 } 874 else 875 { 876 $('>tbody>tr', that.dom.body).each( function (z) { 877 nClone = this.cloneNode(true); 878 nClone.className += ' DTFC_NoData'; 879 $('td', nClone).html(''); 880 nBody.appendChild( nClone ); 881 } ); 882 } 883 884 oClone.body.style.width = "100%"; 885 oGrid.body.appendChild( oClone.body ); 886 887 this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body ); 888 889 /* 890 * Footer 891 */ 892 if ( this.s.dt.nTFoot !== null ) 893 { 894 if ( bAll ) 895 { 896 if ( oClone.footer !== null ) 897 { 898 oClone.footer.parentNode.removeChild( oClone.footer ); 899 } 900 oClone.footer = $(this.dom.footer).clone(true)[0]; 901 oClone.footer.className += " DTFC_Cloned"; 902 oClone.footer.style.width = "100%"; 903 oGrid.foot.appendChild( oClone.footer ); 904 905 /* Copy the footer just like we do for the header */ 906 var aoCloneLayout = this._fnCopyLayout( this.s.dt.aoFooter, aiColumns ); 907 var jqCloneTfoot = $('>tfoot', oClone.footer); 908 jqCloneTfoot.empty(); 909 910 for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ ) 911 { 912 jqCloneTfoot[0].appendChild( aoCloneLayout[i].nTr ); 913 } 914 this.s.dt.oApi._fnDrawHead( this.s.dt, aoCloneLayout, true ); 915 } 916 else 917 { 918 var aoCloneLayout = this._fnCopyLayout( this.s.dt.aoFooter, aiColumns ); 919 var aoCurrFooter=[]; 920 921 this.s.dt.oApi._fnDetectHeader( aoCurrFooter, $('>tfoot', oClone.footer)[0] ); 922 923 for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ ) 924 { 925 for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ ) 926 { 927 aoCurrFooter[i][j].cell.className = aoCloneLayout[i][j].cell.className; 928 } 929 } 930 } 931 this._fnEqualiseHeights( 'tfoot', this.dom.footer, oClone.footer ); 932 } 933 934 /* Equalise the column widths between the header footer and body - body get's priority */ 935 var anUnique = this.s.dt.oApi._fnGetUniqueThs( this.s.dt, $('>thead', oClone.header)[0] ); 936 $(anUnique).each( function (i) { 937 iColumn = aiColumns[i]; 938 this.style.width = that.s.aiWidths[iColumn]+"px"; 939 } ); 940 941 if ( that.s.dt.nTFoot !== null ) 942 { 943 anUnique = this.s.dt.oApi._fnGetUniqueThs( this.s.dt, $('>tfoot', oClone.footer)[0] ); 944 $(anUnique).each( function (i) { 945 iColumn = aiColumns[i]; 946 this.style.width = that.s.aiWidths[iColumn]+"px"; 947 } ); 948 } 949 }, 950 951 952 /** 953 * From a given table node (THEAD etc), get a list of TR direct child elements 954 * @param {Node} nIn Table element to search for TR elements (THEAD, TBODY or TFOOT element) 955 * @returns {Array} List of TR elements found 956 * @private 957 */ 958 "_fnGetTrNodes": function ( nIn ) 959 { 960 var aOut = []; 961 for ( var i=0, iLen=nIn.childNodes.length ; i<iLen ; i++ ) 962 { 963 if ( nIn.childNodes[i].nodeName.toUpperCase() == "TR" ) 964 { 965 aOut.push( nIn.childNodes[i] ); 966 } 967 } 968 return aOut; 969 }, 970 971 972 /** 973 * Equalise the heights of the rows in a given table node in a cross browser way 974 * @returns {void} 975 * @param {String} nodeName Node type - thead, tbody or tfoot 976 * @param {Node} original Original node to take the heights from 977 * @param {Node} clone Copy the heights to 978 * @private 979 */ 980 "_fnEqualiseHeights": function ( nodeName, original, clone ) 981 { 982 if ( this.s.sHeightMatch == 'none' && nodeName !== 'thead' && nodeName !== 'tfoot' ) 983 { 984 return; 985 } 986 987 var that = this, 988 i, iLen, iHeight, iHeight2, iHeightOriginal, iHeightClone, 989 rootOriginal = original.getElementsByTagName(nodeName)[0], 990 rootClone = clone.getElementsByTagName(nodeName)[0], 991 jqBoxHack = $('>'+nodeName+'>tr:eq(0)', original).children(':first'), 992 iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(), 993 anOriginal = this._fnGetTrNodes( rootOriginal ), 994 anClone = this._fnGetTrNodes( rootClone ); 995 996 for ( i=0, iLen=anClone.length ; i<iLen ; i++ ) 997 { 998 if ( this.s.sHeightMatch == 'semiauto' && typeof anOriginal[i]._DTTC_iHeight != 'undefined' && 999 anOriginal[i]._DTTC_iHeight !== null ) 1000 { 1001 /* Oddly enough, IE / Chrome seem not to copy the style height - Mozilla and Opera keep it */ 1002 if ( $.browser.msie ) 1003 { 1004 $(anClone[i]).children().height( anOriginal[i]._DTTC_iHeight-iBoxHack ); 1005 } 1006 continue; 1007 } 1008 1009 iHeightOriginal = anOriginal[i].offsetHeight;
1010 iHeightClone = anClone[i].offsetHeight; 1011 iHeight = iHeightClone > iHeightOriginal ? iHeightClone : iHeightOriginal; 1012 1013 if ( this.s.sHeightMatch == 'semiauto' ) 1014 { 1015 anOriginal[i]._DTTC_iHeight = iHeight; 1016 } 1017 1018 /* Can we use some kind of object detection here?! This is very nasty - damn browsers */ 1019 if ( $.browser.msie && $.browser.version < 8 ) 1020 { 1021 $(anClone[i]).children().height( iHeight-iBoxHack ); 1022 $(anOriginal[i]).children().height( iHeight-iBoxHack ); 1023 } 1024 else 1025 { 1026 anClone[i].style.height = iHeight+"px"; 1027 anOriginal[i].style.height = iHeight+"px"; 1028 } 1029 } 1030 } 1031}; 1032 1033 1034 1035/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1036 * Statics 1037 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1038 1039 1040/** 1041 * FixedColumns default settings for initialisation 1042 * @namespace 1043 * @static 1044 */ 1045FixedColumns.defaults = { 1046 /** 1047 * Number of left hand columns to fix in position 1048 * @type int 1049 * @default 1 1050 * @static 1051 * @example 1052 * var oTable = $('#example').dataTable( { 1053 * "sScrollX": "100%" 1054 * } ); 1055 * new FixedColumns( oTable, { 1056 * "iLeftColumns": 2 1057 * } ); 1058 */ 1059 "iLeftColumns": 1, 1060 1061 /** 1062 * Number of right hand columns to fix in position 1063 * @type int 1064 * @default 0 1065 * @static 1066 * @example 1067 * var oTable = $('#example').dataTable( { 1068 * "sScrollX": "100%" 1069 * } ); 1070 * new FixedColumns( oTable, { 1071 * "iRightColumns": 1 1072 * } ); 1073 */ 1074 "iRightColumns": 0, 1075 1076 /** 1077 * Draw callback function which is called when FixedColumns has redrawn the fixed assets 1078 * @type function(object, object):void 1079 * @default null 1080 * @static 1081 * @example 1082 * var oTable = $('#example').dataTable( { 1083 * "sScrollX": "100%" 1084 * } ); 1085 * new FixedColumns( oTable, { 1086 * "fnDrawCallback": function () { 1087 * alert( "FixedColumns redraw" ); 1088 * } 1089 * } ); 1090 */ 1091 "fnDrawCallback": null, 1092 1093 /** 1094 * Type of left column size calculation. Can take the values of "fixed", whereby the iLeftWidth 1095 * value will be treated as a pixel value, or "relative" for which case iLeftWidth will be 1096 * treated as a percentage value. 1097 * @type string 1098 * @default fixed 1099 * @static 1100 * @example 1101 * var oTable = $('#example').dataTable( { 1102 * "sScrollX": "100%" 1103 * } ); 1104 * new FixedColumns( oTable, { 1105 * "sLeftWidth": "relative", 1106 * "iLeftWidth": 10 // percentage 1107 * } ); 1108 */ 1109 "sLeftWidth": "fixed", 1110 1111 /** 1112 * Width to set for the width of the left fixed column(s) - note that the behaviour of this 1113 * property is directly effected by the sLeftWidth property. If not defined then this property 1114 * is calculated automatically from what has been assigned by DataTables. 1115 * @type int 1116 * @default null 1117 * @static 1118 * @example 1119 * var oTable = $('#example').dataTable( { 1120 * "sScrollX": "100%" 1121 * } ); 1122 * new FixedColumns( oTable, { 1123 * "iLeftWidth": 100 // pixels 1124 * } ); 1125 */ 1126 "iLeftWidth": null, 1127 1128 /** 1129 * Type of right column size calculation. Can take the values of "fixed", whereby the 1130 * iRightWidth value will be treated as a pixel value, or "relative" for which case 1131 * iRightWidth will be treated as a percentage value. 1132 * @type string 1133 * @default fixed 1134 * @static 1135 * @example 1136 * var oTable = $('#example').dataTable( { 1137 * "sScrollX": "100%" 1138 * } ); 1139 * new FixedColumns( oTable, { 1140 * "sRightWidth": "relative", 1141 * "iRightWidth": 10 // percentage 1142 * } ); 1143 */ 1144 "sRightWidth": "fixed", 1145 1146 /** 1147 * Width to set for the width of the right fixed column(s) - note that the behaviour of this 1148 * property is directly effected by the sRightWidth property. If not defined then this property 1149 * is calculated automatically from what has been assigned by DataTables. 1150 * @type int 1151 * @default null 1152 * @static 1153 * @example 1154 * var oTable = $('#example').dataTable( { 1155 * "sScrollX": "100%" 1156 * } ); 1157 * new FixedColumns( oTable, { 1158 * "iRightWidth": 200 // pixels 1159 * } ); 1160 */ 1161 "iRightWidth": null, 1162 1163 /** 1164 * Height matching algorthim to use. This can be "none" which will result in no height 1165 * matching being applied by FixedColumns (height matching could be forced by CSS in this 1166 * case), "semiauto" whereby the height calculation will be performed once, and the result 1167 * cached to be used again (fnRecalculateHeight can be used to force recalculation), or 1168 * "auto" when height matching is performed on every draw (slowest but must accurate)
1169 * @type string 1170 * @default semiauto 1171 * @static 1172 * @example 1173 * var oTable = $('#example').dataTable( { 1174 * "sScrollX": "100%" 1175 * } ); 1176 * new FixedColumns( oTable, { 1177 * "sHeightMatch": "auto" 1178 * } ); 1179 */ 1180 "sHeightMatch": "semiauto" 1181}; 1182 1183 1184 1185 1186/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1187 * Constants 1188 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1189 1190 1191/** 1192 * Name of this class 1193 * @constant CLASS 1194 * @type String 1195 * @default FixedColumns 1196 */ 1197FixedColumns.prototype.CLASS = "FixedColumns"; 1198 1199 1200/** 1201 * FixedColumns version 1202 * @constant FixedColumns.VERSION 1203 * @type String 1204 * @default See code 1205 * @static 1206 */ 1207FixedColumns.VERSION = "2.0.3"; 1208 1209 1210 1211/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1212 * Fired events (for documentation) 1213 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 1214 1215 1216/** 1217 * Event fired whenever FixedColumns redraws the fixed columns (i.e. clones the table elements from the main DataTable). This will occur whenever the DataTable that the FixedColumns instance is attached does its own draw. 1218 * @name FixedColumns#draw 1219 * @event 1220 * @param {event} e jQuery event object 1221 * @param {object} o Event parameters from FixedColumns 1222 * @param {object} o.leftClone Instance's object dom.clone.left for easy reference. This object c
1222ontains references to the left fixed clumn column's nodes 1223 * @param {object} o.rightClone Instance's object dom.clone.right for easy reference. This object contains references to the right fixed clumn column's nodes 1224 */ 1225 1226})(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.