Add datatables-1.9.4 and jquery-1.10.2 libraries
[proteocache.git] / webapp / resources / datatables-1.9.4 / extras / FixedHeader / js / FixedHeader.js
1 /*
2  * File:        FixedHeader.js
3  * Version:     2.0.6
4  * Description: "Fix" a header at the top of the table, so it scrolls with the table
5  * Author:      Allan Jardine (www.sprymedia.co.uk)
6  * Created:     Wed 16 Sep 2009 19:46:30 BST
7  * Language:    Javascript
8  * License:     GPL v2 or BSD 3 point style
9  * Project:     Just a little bit of fun - enjoy :-)
10  * Contact:     www.sprymedia.co.uk/contact
11  * 
12  * Copyright 2009-2012 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, available at:
16  *   http://datatables.net/license_gpl2
17  *   http://datatables.net/license_bsd
18  */
19
20 /*
21  * Function: FixedHeader
22  * Purpose:  Provide 'fixed' header, footer and columns on an HTML table
23  * Returns:  object:FixedHeader - must be called with 'new'
24  * Inputs:   mixed:mTable - target table
25  *                                         1. DataTable object - when using FixedHeader with DataTables, or
26  *                                         2. HTML table node - when using FixedHeader without DataTables
27  *           object:oInit - initialisation settings, with the following properties (each optional)
28  *             bool:top -    fix the header (default true)
29  *             bool:bottom - fix the footer (default false)
30  *             bool:left -   fix the left most column (default false)
31  *             bool:right -  fix the right most column (default false)
32  *             int:zTop -    fixed header zIndex
33  *             int:zBottom - fixed footer zIndex
34  *             int:zLeft -   fixed left zIndex
35  *             int:zRight -  fixed right zIndex
36  */
37 var FixedHeader = function ( mTable, oInit ) {
38         /* Sanity check - you just know it will happen */
39         if ( typeof this.fnInit != 'function' )
40         {
41                 alert( "FixedHeader warning: FixedHeader must be initialised with the 'new' keyword." );
42                 return;
43         }
44         
45         var that = this;
46         var oSettings = {
47                 "aoCache": [],
48                 "oSides": {
49                         "top": true,
50                         "bottom": false,
51                         "left": false,
52                         "right": false
53                 },
54                 "oZIndexes": {
55                         "top": 104,
56                         "bottom": 103,
57                         "left": 102,
58                         "right": 101
59                 },
60                 "oMes": {
61                         "iTableWidth": 0,
62                         "iTableHeight": 0,
63                         "iTableLeft": 0,
64                         "iTableRight": 0, /* note this is left+width, not actually "right" */
65                         "iTableTop": 0,
66                         "iTableBottom": 0 /* note this is top+height, not actually "bottom" */
67                 },
68                 "oOffset": {
69                         "top": 0
70                 },
71                 "nTable": null,
72                 "bUseAbsPos": false,
73                 "bFooter": false
74         };
75         
76         /*
77          * Function: fnGetSettings
78          * Purpose:  Get the settings for this object
79          * Returns:  object: - settings object
80          * Inputs:   -
81          */
82         this.fnGetSettings = function () {
83                 return oSettings;
84         };
85         
86         /*
87          * Function: fnUpdate
88          * Purpose:  Update the positioning and copies of the fixed elements
89          * Returns:  -
90          * Inputs:   -
91          */
92         this.fnUpdate = function () {
93                 this._fnUpdateClones();
94                 this._fnUpdatePositions();
95         };
96         
97         /*
98          * Function: fnPosition
99          * Purpose:  Update the positioning of the fixed elements
100          * Returns:  -
101          * Inputs:   -
102          */
103         this.fnPosition = function () {
104                 this._fnUpdatePositions();
105         };
106         
107         /* Let's do it */
108         this.fnInit( mTable, oInit );
109         
110         /* Store the instance on the DataTables object for easy access */
111         if ( typeof mTable.fnSettings == 'function' )
112         {
113                 mTable._oPluginFixedHeader = this;
114         }
115 };
116
117
118 /*
119  * Variable: FixedHeader
120  * Purpose:  Prototype for FixedHeader
121  * Scope:    global
122  */
123 FixedHeader.prototype = {
124         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
125          * Initialisation
126          */
127         
128         /*
129          * Function: fnInit
130          * Purpose:  The "constructor"
131          * Returns:  -
132          * Inputs:   {as FixedHeader function}
133          */
134         fnInit: function ( oTable, oInit )
135         {
136                 var s = this.fnGetSettings();
137                 var that = this;
138                 
139                 /* Record the user definable settings */
140                 this.fnInitSettings( s, oInit );
141                 
142                 /* DataTables specific stuff */
143                 if ( typeof oTable.fnSettings == 'function' )
144                 {
145                         if ( typeof oTable.fnVersionCheck == 'functon' &&
146                              oTable.fnVersionCheck( '1.6.0' ) !== true )
147                         {
148                                 alert( "FixedHeader 2 required DataTables 1.6.0 or later. "+
149                                         "Please upgrade your DataTables installation" );
150                                 return;
151                         }
152                         
153                         var oDtSettings = oTable.fnSettings();
154                         
155                         if ( oDtSettings.oScroll.sX != "" || oDtSettings.oScroll.sY != "" )
156                         {
157                                 alert( "FixedHeader 2 is not supported with DataTables' scrolling mode at this time" );
158                                 return;
159                         }
160                         
161                         s.nTable = oDtSettings.nTable;
162                         oDtSettings.aoDrawCallback.push( {
163                                 "fn": function () {
164                                         FixedHeader.fnMeasure();
165                                         that._fnUpdateClones.call(that);
166                                         that._fnUpdatePositions.call(that);
167                                 },
168                                 "sName": "FixedHeader"
169                         } );
170                 }
171                 else
172                 {
173                         s.nTable = oTable;
174                 }
175                 
176                 s.bFooter = ($('>tfoot', s.nTable).length > 0) ? true : false;
177                 
178                 /* "Detect" browsers that don't support absolute positioing - or have bugs */
179                 s.bUseAbsPos = (jQuery.browser.msie && (jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0"));
180                 
181                 /* Add the 'sides' that are fixed */
182                 if ( s.oSides.top )
183                 {
184                         s.aoCache.push( that._fnCloneTable( "fixedHeader", "FixedHeader_Header", that._fnCloneThead ) );
185                 }
186                 if ( s.oSides.bottom )
187                 {
188                         s.aoCache.push( that._fnCloneTable( "fixedFooter", "FixedHeader_Footer", that._fnCloneTfoot ) );
189                 }
190                 if ( s.oSides.left )
191                 {
192                         s.aoCache.push( that._fnCloneTable( "fixedLeft", "FixedHeader_Left", that._fnCloneTLeft ) );
193                 }
194                 if ( s.oSides.right )
195                 {
196                         s.aoCache.push( that._fnCloneTable( "fixedRight", "FixedHeader_Right", that._fnCloneTRight ) );
197                 }
198                 
199                 /* Event listeners for window movement */
200                 FixedHeader.afnScroll.push( function () {
201                         that._fnUpdatePositions.call(that);
202                 } );
203                 
204                 jQuery(window).resize( function () {
205                         FixedHeader.fnMeasure();
206                         that._fnUpdateClones.call(that);
207                         that._fnUpdatePositions.call(that);
208                 } );
209                 
210                 /* Get things right to start with */
211                 FixedHeader.fnMeasure();
212                 that._fnUpdateClones();
213                 that._fnUpdatePositions();
214         },
215         
216         
217         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
218          * Support functions
219          */
220         
221         /*
222          * Function: fnInitSettings
223          * Purpose:  Take the user's settings and copy them to our local store
224          * Returns:  -
225          * Inputs:   object:s - the local settings object
226          *           object:oInit - the user's settings object
227          */
228         fnInitSettings: function ( s, oInit )
229         {
230                 if ( typeof oInit != 'undefined' )
231                 {
232                         if ( typeof oInit.top != 'undefined' ) {
233                                 s.oSides.top = oInit.top;
234                         }
235                         if ( typeof oInit.bottom != 'undefined' ) {
236                                 s.oSides.bottom = oInit.bottom;
237                         }
238                         if ( typeof oInit.left != 'undefined' ) {
239                                 s.oSides.left = oInit.left;
240                         }
241                         if ( typeof oInit.right != 'undefined' ) {
242                                 s.oSides.right = oInit.right;
243                         }
244                         
245                         if ( typeof oInit.zTop != 'undefined' ) {
246                                 s.oZIndexes.top = oInit.zTop;
247                         }
248                         if ( typeof oInit.zBottom != 'undefined' ) {
249                                 s.oZIndexes.bottom = oInit.zBottom;
250                         }
251                         if ( typeof oInit.zLeft != 'undefined' ) {
252                                 s.oZIndexes.left = oInit.zLeft;
253                         }
254                         if ( typeof oInit.zRight != 'undefined' ) {
255                                 s.oZIndexes.right = oInit.zRight;
256                         }
257
258                         if ( typeof oInit.offsetTop != 'undefined' ) {
259                                 s.oOffset.top = oInit.offsetTop;
260                         }
261                 }
262                 
263                 /* Detect browsers which have poor position:fixed support so we can use absolute positions.
264                  * This is much slower since the position must be updated for each scroll, but widens
265                  * compatibility
266                  */
267                 s.bUseAbsPos = (jQuery.browser.msie && 
268                         (jQuery.browser.version=="6.0"||jQuery.browser.version=="7.0"));
269         },
270         
271         /*
272          * Function: _fnCloneTable
273          * Purpose:  Clone the table node and do basic initialisation
274          * Returns:  -
275          * Inputs:   -
276          */
277         _fnCloneTable: function ( sType, sClass, fnClone )
278         {
279                 var s = this.fnGetSettings();
280                 var nCTable;
281                 
282                 /* We know that the table _MUST_ has a DIV wrapped around it, because this is simply how
283                  * DataTables works. Therefore, we can set this to be relatively position (if it is not
284                  * alreadu absolute, and use this as the base point for the cloned header
285                  */
286                 if ( jQuery(s.nTable.parentNode).css('position') != "absolute" )
287                 {
288                         s.nTable.parentNode.style.position = "relative";
289                 }
290                 
291                 /* Just a shallow clone will do - we only want the table node */
292                 nCTable = s.nTable.cloneNode( false );
293                 nCTable.removeAttribute( 'id' );
294                 
295                 var nDiv = document.createElement( 'div' );
296                 nDiv.style.position = "absolute";
297                 nDiv.style.top = "0px";
298                 nDiv.style.left = "0px";
299                 nDiv.className += " FixedHeader_Cloned "+sType+" "+sClass;
300                 
301                 /* Set the zIndexes */
302                 if ( sType == "fixedHeader" )
303                 {
304                         nDiv.style.zIndex = s.oZIndexes.top;
305                 }
306                 if ( sType == "fixedFooter" )
307                 {
308                         nDiv.style.zIndex = s.oZIndexes.bottom;
309                 }
310                 if ( sType == "fixedLeft" )
311                 {
312                         nDiv.style.zIndex = s.oZIndexes.left;
313                 }
314                 else if ( sType == "fixedRight" )
315                 {
316                         nDiv.style.zIndex = s.oZIndexes.right;
317                 }
318
319                 /* remove margins since we are going to poistion it absolutely */
320                 nCTable.style.margin = "0";
321                 
322                 /* Insert the newly cloned table into the DOM, on top of the "real" header */
323                 nDiv.appendChild( nCTable );
324                 document.body.appendChild( nDiv );
325                 
326                 return {
327                         "nNode": nCTable,
328                         "nWrapper": nDiv,
329                         "sType": sType,
330                         "sPosition": "",
331                         "sTop": "",
332                         "sLeft": "",
333                         "fnClone": fnClone
334                 };
335         },
336         
337         /*
338          * Function: _fnUpdatePositions
339          * Purpose:  Get the current positioning of the table in the DOM
340          * Returns:  -
341          * Inputs:   -
342          */
343         _fnMeasure: function ()
344         {
345                 var
346                         s = this.fnGetSettings(),
347                         m = s.oMes,
348                         jqTable = jQuery(s.nTable),
349                         oOffset = jqTable.offset(),
350                         iParentScrollTop = this._fnSumScroll( s.nTable.parentNode, 'scrollTop' ),
351                         iParentScrollLeft = this._fnSumScroll( s.nTable.parentNode, 'scrollLeft' );
352                 
353                 m.iTableWidth = jqTable.outerWidth();
354                 m.iTableHeight = jqTable.outerHeight();
355                 m.iTableLeft = oOffset.left + s.nTable.parentNode.scrollLeft;
356                 m.iTableTop = oOffset.top + iParentScrollTop;
357                 m.iTableRight = m.iTableLeft + m.iTableWidth;
358                 m.iTableRight = FixedHeader.oDoc.iWidth - m.iTableLeft - m.iTableWidth;
359                 m.iTableBottom = FixedHeader.oDoc.iHeight - m.iTableTop - m.iTableHeight;
360         },
361         
362         /*
363          * Function: _fnSumScroll
364          * Purpose:  Sum node parameters all the way to the top
365          * Returns:  int: sum
366          * Inputs:   node:n - node to consider
367          *           string:side - scrollTop or scrollLeft
368          */
369         _fnSumScroll: function ( n, side )
370         {
371                 var i = n[side];
372                 while ( n = n.parentNode )
373                 {
374                         if ( n.nodeName == 'HTML' || n.nodeName == 'BODY' )
375                         {
376                                 break;
377                         }
378                         i = n[side];
379                 }
380                 return i;
381         },
382         
383         /*
384          * Function: _fnUpdatePositions
385          * Purpose:  Loop over the fixed elements for this table and update their positions
386          * Returns:  -
387          * Inputs:   -
388          */
389         _fnUpdatePositions: function ()
390         {
391                 var s = this.fnGetSettings();
392                 this._fnMeasure();
393                 
394                 for ( var i=0, iLen=s.aoCache.length ; i<iLen ; i++ )
395                 {
396                         if ( s.aoCache[i].sType == "fixedHeader" )
397                         {
398                                 this._fnScrollFixedHeader( s.aoCache[i] );
399                         }
400                         else if ( s.aoCache[i].sType == "fixedFooter" )
401                         {
402                                 this._fnScrollFixedFooter( s.aoCache[i] );
403                         }
404                         else if ( s.aoCache[i].sType == "fixedLeft" )
405                         {
406                                 this._fnScrollHorizontalLeft( s.aoCache[i] );
407                         }
408                         else
409                         {
410                                 this._fnScrollHorizontalRight( s.aoCache[i] );
411                         }
412                 }
413         },
414         
415         /*
416          * Function: _fnUpdateClones
417          * Purpose:  Loop over the fixed elements for this table and call their cloning functions
418          * Returns:  -
419          * Inputs:   -
420          */
421         _fnUpdateClones: function ()
422         {
423                 var s = this.fnGetSettings();
424                 for ( var i=0, iLen=s.aoCache.length ; i<iLen ; i++ )
425                 {
426                         s.aoCache[i].fnClone.call( this, s.aoCache[i] );
427                 }
428         },
429         
430         
431         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
432          * Scrolling functions
433          */
434         
435         /*
436          * Function: _fnScrollHorizontalLeft
437          * Purpose:  Update the positioning of the scrolling elements
438          * Returns:  -
439          * Inputs:   object:oCache - the cahced values for this fixed element
440          */
441         _fnScrollHorizontalRight: function ( oCache )
442         {
443                 var
444                         s = this.fnGetSettings(),
445                         oMes = s.oMes,
446                         oWin = FixedHeader.oWin,
447                         oDoc = FixedHeader.oDoc,
448                         nTable = oCache.nWrapper,
449                         iFixedWidth = jQuery(nTable).outerWidth();
450                 
451                 if ( oWin.iScrollRight < oMes.iTableRight )
452                 {
453                         /* Fully right aligned */
454                         this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
455                         this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
456                         this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft+oMes.iTableWidth-iFixedWidth)+"px", 'left', nTable.style );      
457                 }
458                 else if ( oMes.iTableLeft < oDoc.iWidth-oWin.iScrollRight-iFixedWidth )
459                 {
460                         /* Middle */
461                         if ( s.bUseAbsPos )
462                         {
463                                 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
464                                 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
465                                 this._fnUpdateCache( oCache, 'sLeft', (oDoc.iWidth-oWin.iScrollRight-iFixedWidth)+"px", 'left', nTable.style );
466                         }
467                         else
468                         {
469                                 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
470                                 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop-oWin.iScrollTop)+"px", 'top', nTable.style );
471                                 this._fnUpdateCache( oCache, 'sLeft', (oWin.iWidth-iFixedWidth)+"px", 'left', nTable.style );
472                         }       
473                 }
474                 else
475                 {
476                         /* Fully left aligned */
477                         this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
478                         this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
479                         this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );     
480                 }
481         },
482         
483         /*
484          * Function: _fnScrollHorizontalLeft
485          * Purpose:  Update the positioning of the scrolling elements
486          * Returns:  -
487          * Inputs:   object:oCache - the cahced values for this fixed element
488          */
489         _fnScrollHorizontalLeft: function ( oCache )
490         {
491                 var
492                         s = this.fnGetSettings(),
493                         oMes = s.oMes,
494                         oWin = FixedHeader.oWin,
495                         oDoc = FixedHeader.oDoc,
496                         nTable = oCache.nWrapper,
497                         iCellWidth = jQuery(nTable).outerWidth();
498                 
499                 if ( oWin.iScrollLeft < oMes.iTableLeft )
500                 {
501                         /* Fully left align */
502                         this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
503                         this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
504                         this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );     
505                 }
506                 else if ( oWin.iScrollLeft < oMes.iTableLeft+oMes.iTableWidth-iCellWidth )
507                 {
508                         /* Middle */
509                         if ( s.bUseAbsPos )
510                         {
511                                 this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
512                                 this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
513                                 this._fnUpdateCache( oCache, 'sLeft', oWin.iScrollLeft+"px", 'left', nTable.style );
514                         }
515                         else
516                         {
517                                 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
518                                 this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop-oWin.iScrollTop)+"px", 'top', nTable.style );
519                                 this._fnUpdateCache( oCache, 'sLeft', "0px", 'left', nTable.style );
520                         }       
521                 }
522                 else
523                 {
524                         /* Fully right align */
525                         this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
526                         this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
527                         this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft+oMes.iTableWidth-iCellWidth)+"px", 'left', nTable.style );       
528                 }
529         },
530         
531         /*
532          * Function: _fnScrollFixedFooter
533          * Purpose:  Update the positioning of the scrolling elements
534          * Returns:  -
535          * Inputs:   object:oCache - the cahced values for this fixed element
536          */
537         _fnScrollFixedFooter: function ( oCache )
538         {
539                 var
540                         s = this.fnGetSettings(),
541                         oMes = s.oMes,
542                         oWin = FixedHeader.oWin,
543                         oDoc = FixedHeader.oDoc,
544                         nTable = oCache.nWrapper,
545                         iTheadHeight = jQuery("thead", s.nTable).outerHeight(),
546                         iCellHeight = jQuery(nTable).outerHeight();
547                 
548                 if ( oWin.iScrollBottom < oMes.iTableBottom )
549                 {
550                         /* Below */
551                         this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
552                         this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+oMes.iTableHeight-iCellHeight)+"px", 'top', nTable.style );
553                         this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );     
554                 }
555                 else if ( oWin.iScrollBottom < oMes.iTableBottom+oMes.iTableHeight-iCellHeight-iTheadHeight )
556                 {
557                         /* Middle */
558                         if ( s.bUseAbsPos )
559                         {
560                                 this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
561                                 this._fnUpdateCache( oCache, 'sTop', (oDoc.iHeight-oWin.iScrollBottom-iCellHeight)+"px", 'top', nTable.style );
562                                 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
563                         }
564                         else
565                         {
566                                 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
567                                 this._fnUpdateCache( oCache, 'sTop', (oWin.iHeight-iCellHeight)+"px", 'top', nTable.style );
568                                 this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style );  
569                         }
570                 }
571                 else
572                 {
573                         /* Above */
574                         this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
575                         this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iCellHeight)+"px", 'top', nTable.style );
576                         this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );     
577                 }
578         },
579         
580         /*
581          * Function: _fnScrollFixedHeader
582          * Purpose:  Update the positioning of the scrolling elements
583          * Returns:  -
584          * Inputs:   object:oCache - the cahced values for this fixed element
585          */
586         _fnScrollFixedHeader: function ( oCache )
587         {
588                 var
589                         s = this.fnGetSettings(),
590                         oMes = s.oMes,
591                         oWin = FixedHeader.oWin,
592                         oDoc = FixedHeader.oDoc,
593                         nTable = oCache.nWrapper,
594                         iTbodyHeight = 0,
595                         anTbodies = s.nTable.getElementsByTagName('tbody');
596
597                 for (var i = 0; i < anTbodies.length; ++i) {
598                         iTbodyHeight += anTbodies[i].offsetHeight;
599                 }
600
601                 if ( oMes.iTableTop > oWin.iScrollTop + s.oOffset.top )
602                 {
603                         /* Above the table */
604                         this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
605                         this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
606                         this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
607                 }
608                 else if ( oWin.iScrollTop + s.oOffset.top > oMes.iTableTop+iTbodyHeight )
609                 {
610                         /* At the bottom of the table */
611                         this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
612                         this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iTbodyHeight)+"px", 'top', nTable.style );
613                         this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
614                 }
615                 else
616                 {
617                         /* In the middle of the table */
618                         if ( s.bUseAbsPos )
619                         {
620                                 this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
621                                 this._fnUpdateCache( oCache, 'sTop', oWin.iScrollTop+"px", 'top', nTable.style );
622                                 this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
623                         }
624                         else
625                         {
626                                 this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
627                                 this._fnUpdateCache( oCache, 'sTop', s.oOffset.top+"px", 'top', nTable.style );
628                                 this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style );
629                         }
630                 }
631         },
632         
633         /*
634          * Function: _fnUpdateCache
635          * Purpose:  Check the cache and update cache and value if needed
636          * Returns:  -
637          * Inputs:   object:oCache - local cache object
638          *           string:sCache - cache property
639          *           string:sSet - value to set
640          *           string:sProperty - object property to set
641          *           object:oObj - object to update
642          */
643         _fnUpdateCache: function ( oCache, sCache, sSet, sProperty, oObj )
644         {
645                 if ( oCache[sCache] != sSet )
646                 {
647                         oObj[sProperty] = sSet;
648                         oCache[sCache] = sSet;
649                 }
650         },
651         
652         
653         
654         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
655          * Cloning functions
656          */
657         
658         /*
659          * Function: _fnCloneThead
660          * Purpose:  Clone the thead element
661          * Returns:  -
662          * Inputs:   object:oCache - the cahced values for this fixed element
663          */
664         _fnCloneThead: function ( oCache )
665         {
666                 var s = this.fnGetSettings();
667                 var nTable = oCache.nNode;
668                 
669                 /* Set the wrapper width to match that of the cloned table */
670                 oCache.nWrapper.style.width = jQuery(s.nTable).outerWidth()+"px";
671                 
672                 /* Remove any children the cloned table has */
673                 while ( nTable.childNodes.length > 0 )
674                 {
675                         jQuery('thead th', nTable).unbind( 'click' );
676                         nTable.removeChild( nTable.childNodes[0] );
677                 }
678                 
679                 /* Clone the DataTables header */
680                 var nThead = jQuery('thead', s.nTable).clone(true)[0];
681                 nTable.appendChild( nThead );
682                 
683                 /* Copy the widths across - apparently a clone isn't good enough for this */
684                 jQuery("thead>tr th", s.nTable).each( function (i) {
685                         jQuery("thead>tr th:eq("+i+")", nTable).width( jQuery(this).width() );
686                 } );
687                 
688                 jQuery("thead>tr td", s.nTable).each( function (i) {
689                         jQuery("thead>tr td:eq("+i+")", nTable).width( jQuery(this).width() );
690                 } );
691         },
692         
693         /*
694          * Function: _fnCloneTfoot
695          * Purpose:  Clone the tfoot element
696          * Returns:  -
697          * Inputs:   object:oCache - the cahced values for this fixed element
698          */
699         _fnCloneTfoot: function ( oCache )
700         {
701                 var s = this.fnGetSettings();
702                 var nTable = oCache.nNode;
703                 
704                 /* Set the wrapper width to match that of the cloned table */
705                 oCache.nWrapper.style.width = jQuery(s.nTable).outerWidth()+"px";
706                 
707                 /* Remove any children the cloned table has */
708                 while ( nTable.childNodes.length > 0 )
709                 {
710                         nTable.removeChild( nTable.childNodes[0] );
711                 }
712                 
713                 /* Clone the DataTables footer */
714                 var nTfoot = jQuery('tfoot', s.nTable).clone(true)[0];
715                 nTable.appendChild( nTfoot );
716                 
717                 /* Copy the widths across - apparently a clone isn't good enough for this */
718                 jQuery("tfoot:eq(0)>tr th", s.nTable).each( function (i) {
719                         jQuery("tfoot:eq(0)>tr th:eq("+i+")", nTable).width( jQuery(this).width() );
720                 } );
721                 
722                 jQuery("tfoot:eq(0)>tr td", s.nTable).each( function (i) {
723                         jQuery("tfoot:eq(0)>tr th:eq("+i+")", nTable)[0].style.width( jQuery(this).width() );
724                 } );
725         },
726         
727         /*
728          * Function: _fnCloneTLeft
729          * Purpose:  Clone the left column
730          * Returns:  -
731          * Inputs:   object:oCache - the cached values for this fixed element
732          */
733         _fnCloneTLeft: function ( oCache )
734         {
735                 var s = this.fnGetSettings();
736                 var nTable = oCache.nNode;
737                 var nBody = $('tbody', s.nTable)[0];
738                 var iCols = $('tbody tr:eq(0) td', s.nTable).length;
739                 var bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
740                 
741                 /* Remove any children the cloned table has */
742                 while ( nTable.childNodes.length > 0 )
743                 {
744                         nTable.removeChild( nTable.childNodes[0] );
745                 }
746                 
747                 /* Is this the most efficient way to do this - it looks horrible... */
748                 nTable.appendChild( jQuery("thead", s.nTable).clone(true)[0] );
749                 nTable.appendChild( jQuery("tbody", s.nTable).clone(true)[0] );
750                 if ( s.bFooter )
751                 {
752                         nTable.appendChild( jQuery("tfoot", s.nTable).clone(true)[0] );
753                 }
754                 
755                 /* Remove unneeded cells */
756                 $('thead tr', nTable).each( function (k) {
757                         $('th:gt(0)', this).remove();
758                 } );
759
760                 $('tfoot tr', nTable).each( function (k) {
761                         $('th:gt(0)', this).remove();
762                 } );
763
764                 $('tbody tr', nTable).each( function (k) {
765                         $('td:gt(0)', this).remove();
766                 } );
767                 
768                 this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable );
769                 
770                 var iWidth = jQuery('thead tr th:eq(0)', s.nTable).outerWidth();
771                 nTable.style.width = iWidth+"px";
772                 oCache.nWrapper.style.width = iWidth+"px";
773         },
774         
775         /*
776          * Function: _fnCloneTRight
777          * Purpose:  Clone the right most colun
778          * Returns:  -
779          * Inputs:   object:oCache - the cahced values for this fixed element
780          */
781         _fnCloneTRight: function ( oCache )
782         {
783                 var s = this.fnGetSettings();
784                 var nBody = $('tbody', s.nTable)[0];
785                 var nTable = oCache.nNode;
786                 var iCols = jQuery('tbody tr:eq(0) td', s.nTable).length;
787                 var bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
788                 
789                 /* Remove any children the cloned table has */
790                 while ( nTable.childNodes.length > 0 )
791                 {
792                         nTable.removeChild( nTable.childNodes[0] );
793                 }
794                 
795                 /* Is this the most efficient way to do this - it looks horrible... */
796                 nTable.appendChild( jQuery("thead", s.nTable).clone(true)[0] );
797                 nTable.appendChild( jQuery("tbody", s.nTable).clone(true)[0] );
798                 if ( s.bFooter )
799                 {
800                         nTable.appendChild( jQuery("tfoot", s.nTable).clone(true)[0] );
801                 }
802                 jQuery('thead tr th:not(:nth-child('+iCols+'n))', nTable).remove();
803                 jQuery('tfoot tr th:not(:nth-child('+iCols+'n))', nTable).remove();
804                 
805                 /* Remove unneeded cells */
806                 $('tbody tr', nTable).each( function (k) {
807                         $('td:lt('+(iCols-1)+')', this).remove();
808                 } );
809                 
810                 this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable );
811                 
812                 var iWidth = jQuery('thead tr th:eq('+(iCols-1)+')', s.nTable).outerWidth();
813                 nTable.style.width = iWidth+"px";
814                 oCache.nWrapper.style.width = iWidth+"px";
815         },
816         
817         
818         /**
819          * Equalise the heights of the rows in a given table node in a cross browser way. Note that this
820          * is more or less lifted as is from FixedColumns
821          *  @method  fnEqualiseHeights
822          *  @returns void
823          *  @param   {string} parent Node type - thead, tbody or tfoot
824          *  @param   {element} original Original node to take the heights from
825          *  @param   {element} clone Copy the heights to
826          *  @private
827          */
828         "fnEqualiseHeights": function ( parent, original, clone )
829         {
830                 var that = this,
831                         jqBoxHack = $(parent+' tr:eq(0)', original).children(':eq(0)'),
832                         iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(),
833                         bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
834                 
835                 /* Remove cells which are not needed and copy the height from the original table */
836                 $(parent+' tr', clone).each( function (k) {
837                         /* Can we use some kind of object detection here?! This is very nasty - damn browsers */
838                         if ( $.browser.mozilla || $.browser.opera )
839                         {
840                                 $(this).children().height( $(parent+' tr:eq('+k+')', original).outerHeight() );
841                         }
842                         else
843                         {
844                                 $(this).children().height( $(parent+' tr:eq('+k+')', original).outerHeight() - iBoxHack );
845                         }
846                         
847                         if ( !bRubbishOldIE )
848                         {
849                                 $(parent+' tr:eq('+k+')', original).height( $(parent+' tr:eq('+k+')', original).outerHeight() );                
850                         }
851                 } );
852         }
853 };
854
855         
856 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
857  * Static properties and methods
858  *   We use these for speed! This information is common to all instances of FixedHeader, so no
859  * point if having them calculated and stored for each different instance.
860  */
861
862 /*
863  * Variable: oWin
864  * Purpose:  Store information about the window positioning
865  * Scope:    FixedHeader
866  */
867 FixedHeader.oWin = {
868         "iScrollTop": 0,
869         "iScrollRight": 0,
870         "iScrollBottom": 0,
871         "iScrollLeft": 0,
872         "iHeight": 0,
873         "iWidth": 0
874 };
875
876 /*
877  * Variable: oDoc
878  * Purpose:  Store information about the document size
879  * Scope:    FixedHeader
880  */
881 FixedHeader.oDoc = {
882         "iHeight": 0,
883         "iWidth": 0
884 };
885
886 /*
887  * Variable: afnScroll
888  * Purpose:  Array of functions that are to be used for the scrolling components
889  * Scope:    FixedHeader
890  */
891 FixedHeader.afnScroll = [];
892
893 /*
894  * Function: fnMeasure
895  * Purpose:  Update the measurements for the window and document
896  * Returns:  -
897  * Inputs:   -
898  */
899 FixedHeader.fnMeasure = function ()
900 {
901         var
902                 jqWin = jQuery(window),
903                 jqDoc = jQuery(document),
904                 oWin = FixedHeader.oWin,
905                 oDoc = FixedHeader.oDoc;
906         
907         oDoc.iHeight = jqDoc.height();
908         oDoc.iWidth = jqDoc.width();
909         
910         oWin.iHeight = jqWin.height();
911         oWin.iWidth = jqWin.width();
912         oWin.iScrollTop = jqWin.scrollTop();
913         oWin.iScrollLeft = jqWin.scrollLeft();
914         oWin.iScrollRight = oDoc.iWidth - oWin.iScrollLeft - oWin.iWidth;
915         oWin.iScrollBottom = oDoc.iHeight - oWin.iScrollTop - oWin.iHeight;
916 };
917
918
919 FixedHeader.VERSION = "2.0.6";
920 FixedHeader.prototype.VERSION = FixedHeader.VERSION;
921
922         
923 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
924  * Global processing
925  */
926
927 /*
928  * Just one 'scroll' event handler in FixedHeader, which calls the required components. This is
929  * done as an optimisation, to reduce calculation and proagation time
930  */
931 jQuery(window).scroll( function () {
932         FixedHeader.fnMeasure();
933         for ( var i=0, iLen=FixedHeader.afnScroll.length ; i<iLen ; i++ )
934         {
935                 FixedHeader.afnScroll[i]();
936         }
937 } );