Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2629 vikas 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
} );