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