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