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