jquery-datatables-rails 1.9.1.3 → 1.10.0

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