Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2629 vikas 1
/**
2
 * @summary     FixedColumns
3
 * @description Freeze columns in place on a scrolling DataTable
4
 * @file        FixedColumns.js
5
 * @version     2.0.1
6
 * @author      Allan Jardine (www.sprymedia.co.uk)
7
 * @license     GPL v2 or BSD 3 point style
8
 * @contact     www.sprymedia.co.uk/contact
9
 *
10
 * @copyright Copyright 2010-2011 Allan Jardine, all rights reserved.
11
 *
12
 * This source file is free software, under either the GPL v2 license or a
13
 * BSD style license, available at:
14
 *   http://datatables.net/license_gpl2
15
 *   http://datatables.net/license_bsd
16
 */
17
 
18
 
19
/* Global scope for FixedColumns */
20
var FixedColumns;
21
 
22
(function($, window, document) {
23
 
24
 
25
/** 
26
 * When making use of DataTables' x-axis scrolling feature, you may wish to 
27
 * fix the left most column in place. This plug-in for DataTables provides 
28
 * exactly this option (note for non-scrolling tables, please use the  
29
 * FixedHeader plug-in, which can fix headers, footers and columns). Key 
30
 * features include:
31
 *   <ul class="limit_length">
32
 *     <li>Freezes the left or right most columns to the side of the table</li>
33
 *     <li>Option to freeze two or more columns</li>
34
 *     <li>Full integration with DataTables' scrolling options</li>
35
 *     <li>Speed - FixedColumns is fast in its operation</li>
36
 *   </ul>
37
 *
38
 *  @class
39
 *  @constructor
40
 *  @param {object} oDT DataTables instance
41
 *  @param {object} [oInit={}] Configuration object for FixedColumns. Options are defined by {@link FixedColumns.defaults}
42
 * 
43
 *  @requires jQuery 1.3+
44
 *  @requires DataTables 1.8.0.dev+
45
 * 
46
 *  @example
47
 *  	var oTable = $('#example').dataTable( {
48
 *  		"sScrollX": "100%"
49
 *  	} );
50
 *  	new FixedColumns( oTable );
51
 */
52
FixedColumns = function ( oDT, oInit ) {
53
	/* Sanity check - you just know it will happen */
54
	if ( ! this instanceof FixedColumns )
55
	{
56
		alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
57
		return;
58
	}
59
 
60
	if ( typeof oInit == 'undefined' )
61
	{
62
		oInit = {};
63
	}
64
 
65
	/**
66
	 * Settings object which contains customisable information for FixedColumns instance
67
	 * @namespace
68
	 * @extends FixedColumns.defaults
69
	 */
70
	this.s = {
71
		/** 
72
		 * DataTables settings objects
73
		 *  @type     object
74
		 *  @default  Obtained from DataTables instance
75
		 */
76
		"dt": oDT.fnSettings(),
77
 
78
		/** 
79
		 * Number of columns in the DataTable - stored for quick access
80
		 *  @type     int
81
		 *  @default  Obtained from DataTables instance
82
		 */
83
		"iTableColumns": oDT.fnSettings().aoColumns.length,
84
 
85
		/** 
86
		 * Original widths of the columns as rendered by DataTables
87
		 *  @type     array.<int>
88
		 *  @default  []
89
		 */
90
		"aiWidths": [],
91
 
92
		/** 
93
		 * Flag to indicate if we are dealing with IE6/7 as these browsers need a little hack
94
		 * in the odd place
95
		 *  @type     boolean
96
		 *  @default  Automatically calculated
97
		 *  @readonly
98
		 */
99
		"bOldIE": ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"))
100
	};
101
 
102
 
103
	/**
104
	 * DOM elements used by the class instance
105
	 * @namespace
106
	 * 
107
	 */
108
	this.dom = {
109
		/**
110
		 * DataTables scrolling element
111
		 *  @type     node
112
		 *  @default  null
113
		 */
114
		"scroller": null,
115
 
116
		/**
117
		 * DataTables header table
118
		 *  @type     node
119
		 *  @default  null
120
		 */
121
		"header": null,
122
 
123
		/**
124
		 * DataTables body table
125
		 *  @type     node
126
		 *  @default  null
127
		 */
128
		"body": null,
129
 
130
		/**
131
		 * DataTables footer table
132
		 *  @type     node
133
		 *  @default  null
134
		 */
135
		"footer": null,
136
 
137
		/**
138
		 * Display grid elements
139
		 * @namespace
140
		 */
141
		"grid": {
142
			/**
143
			 * Grid wrapper. This is the container element for the 3x3 grid
144
			 *  @type     node
145
			 *  @default  null
146
			 */
147
			"wrapper": null,
148
 
149
			/**
150
			 * DataTables scrolling element. This element is the DataTables
151
			 * component in the display grid (making up the main table - i.e.
152
			 * not the fixed columns).
153
			 *  @type     node
154
			 *  @default  null
155
			 */
156
			"dt": null,
157
 
158
			/**
159
			 * Left fixed column grid components
160
			 * @namespace
161
			 */
162
			"left": {
163
				"wrapper": null,
164
				"head": null,
165
				"body": null,
166
				"foot": null
167
			},
168
 
169
			/**
170
			 * Right fixed column grid components
171
			 * @namespace
172
			 */
173
			"right": {
174
				"wrapper": null,
175
				"head": null,
176
				"body": null,
177
				"foot": null
178
			}
179
		},
180
 
181
		/**
182
		 * Cloned table nodes
183
		 * @namespace
184
		 */
185
		"clone": {
186
			/**
187
			 * Left column cloned table nodes
188
			 * @namespace
189
			 */
190
			"left": {
191
				/**
192
				 * Cloned header table
193
				 *  @type     node
194
				 *  @default  null
195
				 */
196
				"header": null,
197
 
198
				/**
199
				 * Cloned body table
200
				 *  @type     node
201
				 *  @default  null
202
				 */
203
				"body": null,
204
 
205
				/**
206
				 * Cloned footer table
207
				 *  @type     node
208
				 *  @default  null
209
				 */
210
				"footer": null
211
			},
212
 
213
			/**
214
			 * Right column cloned table nodes
215
			 * @namespace
216
			 */
217
			"right": {
218
				/**
219
				 * Cloned header table
220
				 *  @type     node
221
				 *  @default  null
222
				 */
223
				"header": null,
224
 
225
				/**
226
				 * Cloned body table
227
				 *  @type     node
228
				 *  @default  null
229
				 */
230
				"body": null,
231
 
232
				/**
233
				 * Cloned footer table
234
				 *  @type     node
235
				 *  @default  null
236
				 */
237
				"footer": null
238
			}
239
		}
240
	};
241
 
242
	/* Attach the instance to the DataTables instance so it can be accessed easily */
243
	this.s.dt.oFixedColumns = this;
244
 
245
	/* Let's do it */
246
	this._fnConstruct( oInit );
247
};
248
 
249
 
250
 
251
FixedColumns.prototype = {
252
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
253
	 * Public methods
254
	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
255
 
256
	/**
257
	 * Update the fixed columns - including headers and footers. Note that FixedColumns will
258
	 * automatically update the display whenever the host DataTable redraws.
259
	 *  @returns {void}
260
	 *  @example
261
	 *  	var oTable = $('#example').dataTable( {
262
	 *  		"sScrollX": "100%"
263
	 *  	} );
264
	 *  	var oFC = new FixedColumns( oTable );
265
	 *  	
266
	 *  	// at some later point when the table has been manipulated....
267
	 *  	oFC.fnUpdate();
268
	 */
269
	"fnUpdate": function ()
270
	{
271
		this._fnDraw( true );
272
	},
273
 
274
 
275
	/**
276
	 * Recalculate the resizes of the 3x3 grid that FixedColumns uses for display of the table.
277
	 * This is useful if you update the width of the table container. Note that FixedColumns will
278
	 * perform this function automatically when the window.resize event is fired.
279
	 *  @returns {void}
280
	 *  @example
281
	 *  	var oTable = $('#example').dataTable( {
282
	 *  		"sScrollX": "100%"
283
	 *  	} );
284
	 *  	var oFC = new FixedColumns( oTable );
285
	 *  	
286
	 *  	// Resize the table container and then have FixedColumns adjust its layout....
287
	 *      $('#content').width( 1200 );
288
	 *  	oFC.fnRedrawLayout();
289
	 */
290
	"fnRedrawLayout": function ()
291
	{
292
		this.__fnGridLayout();
293
	},
294
 
295
 
296
	/**
297
	 * Mark a row such that it's height should be recalculated when using 'semiauto' row
298
	 * height matching. This function will have no effect when 'none' or 'auto' row height
299
	 * matching is used.
300
	 *  @param   {Node} nTr TR element that should have it's height recalculated
301
	 *  @returns {void}
302
	 *  @example
303
	 *  	var oTable = $('#example').dataTable( {
304
	 *  		"sScrollX": "100%"
305
	 *  	} );
306
	 *  	var oFC = new FixedColumns( oTable );
307
	 *  	
308
	 *  	// manipulate the table - mark the row as needing an update then update the table
309
	 *  	// this allows the redraw performed by DataTables fnUpdate to recalculate the row
310
	 *  	// height
311
	 *  	oFC.fnRecalculateHeight();
312
	 *  	oTable.fnUpdate( $('#example tbody tr:eq(0)')[0], ["insert date", 1, 2, 3 ... ]);
313
	 */
314
	"fnRecalculateHeight": function ( nTr )
315
	{
316
		nTr._DTTC_iHeight = null;
317
		nTr.style.height = 'auto';
318
	},
319
 
320
 
321
	/**
322
	 * Set the height of a given row - provides cross browser compatibility
323
	 *  @param   {Node} nTarget TR element that should have it's height recalculated
324
	 *  @param   {int} iHeight Height in pixels to set
325
	 *  @returns {void}
326
	 *  @example
327
	 *  	var oTable = $('#example').dataTable( {
328
	 *  		"sScrollX": "100%"
329
	 *  	} );
330
	 *  	var oFC = new FixedColumns( oTable );
331
	 *  	
332
	 *  	// You may want to do this after manipulating a row in the fixed column
333
	 *  	oFC.fnSetRowHeight( $('#example tbody tr:eq(0)')[0], 50 );
334
	 */
335
	"fnSetRowHeight": function ( nTarget, iHeight )
336
	{
337
		var jqBoxHack = $(nTarget).children(':first');
338
		var iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height();
339
 
340
		/* Can we use some kind of object detection here?! This is very nasty - damn browsers */
341
		if ( $.browser.mozilla || $.browser.opera )
342
		{
343
			nTarget.style.height = iHeight+"px";
344
		}
345
		else
346
		{
347
			$(nTarget).children().height( iHeight-iBoxHack );
348
		}
349
	},
350
 
351
 
352
 
353
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
354
	 * Private methods (they are of course public in JS, but recommended as private)
355
	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
356
 
357
	/**
358
	 * Initialisation for FixedColumns
359
	 *  @param   {Object} oInit User settings for initialisation
360
	 *  @returns {void}
361
	 *  @private
362
	 */
363
	"_fnConstruct": function ( oInit )
364
	{
365
		var i, iLen, iWidth,
366
			that = this;
367
 
368
		/* Sanity checking */
369
		if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' ||
370
		     this.s.dt.oInstance.fnVersionCheck( '1.8.0' ) !== true )
371
		{
372
			alert( "FixedColumns "+FixedColumns.VERSION+" required DataTables 1.8.0 or later. "+
373
				"Please upgrade your DataTables installation" );
374
			return;
375
		}
376
 
377
		if ( this.s.dt.oScroll.sX === "" )
378
		{
379
			this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
380
				"x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+
381
				"column fixing when scrolling is not enabled" );
382
			return;
383
		}
384
 
385
		/* Apply the settings from the user / defaults */
386
		this.s = $.extend( true, this.s, FixedColumns.defaults, oInit );
387
 
388
		/* Set up the DOM as we need it and cache nodes */
389
		this.dom.grid.dt = $(this.s.dt.nTable).parents('div.dataTables_scroll')[0];
390
		this.dom.scroller = $('div.dataTables_scrollBody', this.dom.grid.dt )[0];
391
 
392
		var iScrollWidth = $(this.dom.grid.dt).width();
393
		var iLeftWidth = 0;
394
		var iRightWidth = 0;
395
 
396
		$('tbody>tr:eq(0)>td', this.s.dt.nTable).each( function (i) {
397
			iWidth = $(this).outerWidth();
398
			that.s.aiWidths.push( iWidth );
399
			if ( i < that.s.iLeftColumns )
400
			{
401
				iLeftWidth += iWidth;
402
			}
403
			if ( that.s.iTableColumns-that.s.iRightColumns <= i )
404
			{
405
				iRightWidth += iWidth;
406
			}
407
		} );
408
 
409
		if ( this.s.iLeftWidth === null )
410
		{
411
			this.s.iLeftWidth = this.s.sLeftWidth == 'fixed' ?
412
				iLeftWidth : (iLeftWidth/iScrollWidth) * 100; 
413
		}
414
 
415
		if ( this.s.iRightWidth === null )
416
		{
417
			this.s.iRightWidth = this.s.sRightWidth == 'fixed' ?
418
				iRightWidth : (iRightWidth/iScrollWidth) * 100;
419
		}
420
 
421
		/* Set up the DOM that we want for the fixed column layout grid */
422
		this._fnGridSetup();
423
 
424
		/* Use the DataTables API method fnSetColumnVis to hide the columns we are going to fix */
425
		for ( i=0 ; i<this.s.iLeftColumns ; i++ )
426
		{
427
			this.s.dt.oInstance.fnSetColumnVis( i, false );
428
		}
429
		for ( i=this.s.iTableColumns - this.s.iRightColumns ; i<this.s.iTableColumns ; i++ )
430
		{
431
			this.s.dt.oInstance.fnSetColumnVis( i, false );
432
		}
433
 
434
		/* Event handlers */
435
		$(this.dom.scroller).scroll( function () {
436
			that.dom.grid.left.body.scrollTop = that.dom.scroller.scrollTop;
437
			if ( that.s.iRightColumns > 0 )
438
			{
439
				that.dom.grid.right.body.scrollTop = that.dom.scroller.scrollTop;
440
			}
441
		} );
442
 
443
		$(window).resize( function () {
444
			that._fnGridLayout.call( that );
445
		} );
446
 
447
		var bFirstDraw = true;
448
		this.s.dt.aoDrawCallback = [ {
449
			"fn": function () {
450
				that._fnDraw.call( that, bFirstDraw );
451
				that._fnGridHeight( that );
452
				bFirstDraw = false;
453
			},
454
			"sName": "FixedColumns"
455
		} ].concat( this.s.dt.aoDrawCallback );
456
 
457
		/* Get things right to start with - note that due to adjusting the columns, there must be
458
		 * another redraw of the main table. It doesn't need to be a full redraw however.
459
		 */
460
		this._fnGridLayout();
461
		this._fnGridHeight();
462
		this.s.dt.oInstance.fnDraw(false);
463
	},
464
 
465
 
466
	/**
467
	 * Set up the DOM for the fixed column. The way the layout works is to create a 1x3 grid
468
	 * for the left column, the DataTable (for which we just reuse the scrolling element DataTable
469
	 * puts into the DOM) and the right column. In each of he two fixed column elements there is a
470
	 * grouping wrapper element and then a head, body and footer wrapper. In each of these we then
471
	 * place the cloned header, body or footer tables. This effectively gives as 3x3 grid structure.
472
	 *  @returns {void}
473
	 *  @private
474
	 */
475
	"_fnGridSetup": function ()
476
	{
477
		var that = this;
478
 
479
		this.dom.body = this.s.dt.nTable;
480
		this.dom.header = this.s.dt.nTHead.parentNode;
481
		this.dom.header.parentNode.parentNode.style.position = "relative";
482
 
483
		var nSWrapper = 
484
			$('<div class="DTFC_ScrollWrapper" style="position:relative; clear:both;">'+
485
				'<div class="DTFC_LeftWrapper" style="position:absolute; top:0; left:0;">'+
486
					'<div class="DTFC_LeftHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
487
					'<div class="DTFC_LeftBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
488
					'<div class="DTFC_LeftFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
489
			  	'</div>'+
490
				'<div class="DTFC_RightWrapper" style="position:absolute; top:0; left:0;">'+
491
					'<div class="DTFC_RightHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
492
					'<div class="DTFC_RightBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
493
					'<div class="DTFC_RightFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
494
			  	'</div>'+
495
			  '</div>')[0];
496
		nLeft = nSWrapper.childNodes[0];
497
		nRight = nSWrapper.childNodes[1];
498
 
499
		this.dom.grid.wrapper = nSWrapper;
500
		this.dom.grid.left.wrapper = nLeft;
501
		this.dom.grid.left.head = nLeft.childNodes[0];
502
		this.dom.grid.left.body = nLeft.childNodes[1];
503
 
504
		if ( this.s.iRightColumns > 0 )
505
		{
506
			this.dom.grid.right.wrapper = nRight;
507
			this.dom.grid.right.head = nRight.childNodes[0];
508
			this.dom.grid.right.body = nRight.childNodes[1];
509
		}
510
 
511
		if ( this.s.dt.nTFoot )
512
		{
513
			this.dom.footer = this.s.dt.nTFoot.parentNode;
514
			this.dom.grid.left.foot = nLeft.childNodes[2];
515
			if ( this.s.iRightColumns > 0 )
516
			{
517
				this.dom.grid.right.foot = nRight.childNodes[2];
518
			}
519
		}
520
 
521
		nSWrapper.appendChild( nLeft );
522
		this.dom.grid.dt.parentNode.insertBefore( nSWrapper, this.dom.grid.dt );
523
		nSWrapper.appendChild( this.dom.grid.dt );
524
 
525
		this.dom.grid.dt.style.position = "absolute";
526
		this.dom.grid.dt.style.top = "0px";
527
		this.dom.grid.dt.style.left = this.s.iLeftWidth+"px";
528
		this.dom.grid.dt.style.width = ($(this.dom.grid.dt).width()-this.s.iLeftWidth-this.s.iRightWidth)+"px";
529
	},
530
 
531
 
532
	/**
533
	 * Style and position the grid used for the FixedColumns layout based on the instance settings.
534
	 * Specifically sLeftWidth ('fixed' or 'absolute'), iLeftWidth (px if fixed, % if absolute) and
535
	 * there 'right' counterparts.
536
	 *  @returns {void}
537
	 *  @private
538
	 */
539
	"_fnGridLayout": function ()
540
	{
541
		var oGrid = this.dom.grid;
542
		var iTotal = $(oGrid.wrapper).width();
543
		var iLeft = 0, iRight = 0, iRemainder = 0;
544
 
545
		if ( this.s.sLeftWidth == 'fixed' )
546
		{
547
			iLeft = this.s.iLeftWidth;
548
		}
549
		else
550
		{
551
			iLeft = ( this.s.iLeftWidth / 100 ) * iTotal;
552
		}
553
 
554
		if ( this.s.sRightWidth == 'fixed' )
555
		{
556
			iRight = this.s.iRightWidth;
557
		}
558
		else
559
		{
560
			iRight = ( this.s.iRightWidth / 100 ) * iTotal;
561
		}
562
 
563
		iRemainder = iTotal - iLeft - iRight;
564
 
565
		oGrid.left.wrapper.style.width = iLeft+"px";
566
		oGrid.dt.style.width = iRemainder+"px";
567
		oGrid.dt.style.left = iLeft+"px";
568
 
569
		if ( this.s.iRightColumns > 0 )
570
		{
571
			oGrid.right.wrapper.style.width = iRight+"px";
572
			oGrid.right.wrapper.style.left = (iTotal-iRight)+"px";
573
		}
574
	},
575
 
576
 
577
	/**
578
	 * Recalculate and set the height of the grid components used for positioning of the 
579
	 * FixedColumn display grid.
580
	 *  @returns {void}
581
	 *  @private
582
	 */
583
	"_fnGridHeight": function ()
584
	{
585
		var oGrid = this.dom.grid;
586
		var iHeight = $(this.dom.grid.dt).height();
587
 
588
		oGrid.wrapper.style.height = iHeight+"px";
589
		oGrid.left.body.style.height = $(this.dom.scroller).height()+"px";
590
		oGrid.left.wrapper.style.height = iHeight+"px";
591
 
592
		if ( this.s.iRightColumns > 0 )
593
		{
594
			oGrid.right.wrapper.style.height = iHeight+"px";
595
			oGrid.right.body.style.height = $(this.dom.scroller).height()+"px";
596
		}
597
	},
598
 
599
 
600
	/**
601
	 * Clone and position the fixed columns
602
	 *  @returns {void}
603
	 *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
604
	 *  @private
605
	 */
606
	"_fnDraw": function ( bAll )
607
	{
608
		this._fnCloneLeft( bAll );
609
		this._fnCloneRight( bAll );
610
 
611
		/* Draw callback function */
612
		if ( this.s.fnDrawCallback !== null )
613
		{
614
			this.s.fnDrawCallback.call( this, this.dom.clone.left, this.dom.clone.right );
615
		}
616
 
617
		/* Event triggering */
618
		$(this).trigger( 'draw', { 
619
			"leftClone": this.dom.clone.left,
620
			"rightClone": this.dom.clone.right
621
		} );
622
	},
623
 
624
 
625
	/**
626
	 * Clone the right columns
627
	 *  @returns {void}
628
	 *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
629
	 *  @private
630
	 */
631
	"_fnCloneRight": function ( bAll )
632
	{
633
		if ( this.s.iRightColumns <= 0 )
634
		{
635
			return;
636
		}
637
 
638
		var that = this,
639
			i, jq,
640
			aiColumns = [];
641
 
642
		for ( i=this.s.iTableColumns-this.s.iRightColumns ; i<this.s.iTableColumns ; i++ )
643
		{
644
			aiColumns.push( i );
645
		}
646
 
647
		this._fnClone( this.dom.clone.right, this.dom.grid.right, aiColumns, bAll );
648
	},
649
 
650
 
651
	/**
652
	 * Clone the left columns
653
	 *  @returns {void}
654
	 *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
655
	 *  @private
656
	 */
657
	"_fnCloneLeft": function ( bAll )
658
	{
659
		if ( this.s.iLeftColumns <= 0 )
660
		{
661
			return;
662
		}
663
 
664
		var that = this,
665
			i, jq,
666
			aiColumns = [];
667
 
668
		for ( i=0 ; i<this.s.iLeftColumns ; i++ )
669
		{
670
			aiColumns.push( i );
671
		}
672
 
673
		this._fnClone( this.dom.clone.left, this.dom.grid.left, aiColumns, bAll );
674
	},
675
 
676
 
677
	/**
678
	 * Make a copy of the layout object for a header or footer element from DataTables. Note that
679
	 * this method will clone the nodes in the layout object.
680
	 *  @returns {Array} Copy of the layout array
681
	 *  @param   {Object} aoOriginal Layout array from DataTables (aoHeader or aoFooter)
682
	 *  @param   {Object} aiColumns Columns to copy
683
	 *  @private
684
	 */
685
	"_fnCopyLayout": function ( aoOriginal, aiColumns )
686
	{
687
		var aReturn = [];
688
		var aClones = [];
689
		var aCloned = [];
690
 
691
		for ( var i=0, iLen=aoOriginal.length ; i<iLen ; i++ )
692
		{
693
			var aRow = [];
694
			aRow.nTr = $(aoOriginal[i].nTr).clone(true)[0];
695
 
696
			for ( var j=0, jLen=this.s.iTableColumns ; j<jLen ; j++ )
697
			{
698
				if ( $.inArray( j, aiColumns ) === -1 )
699
				{
700
					continue;
701
				}
702
 
703
				var iCloned = $.inArray( aoOriginal[i][j].cell, aCloned );
704
				if ( iCloned === -1 )
705
				{
706
					var nClone = $(aoOriginal[i][j].cell).clone(true)[0];
707
					aClones.push( nClone );
708
					aCloned.push( aoOriginal[i][j].cell );
709
 
710
					aRow.push( {
711
						"cell": nClone,
712
						"unique": aoOriginal[i][j].unique
713
					} );
714
				}
715
				else
716
				{
717
					aRow.push( {
718
						"cell": aClones[ iCloned ],
719
						"unique": aoOriginal[i][j].unique
720
					} );
721
				}
722
			}
723
 
724
			aReturn.push( aRow );
725
		}
726
 
727
		return aReturn;
728
	},
729
 
730
 
731
	/**
732
	 * Clone the DataTable nodes and place them in the DOM (sized correctly)
733
	 *  @returns {void}
734
	 *  @param   {Object} oClone Object containing the header, footer and body cloned DOM elements
735
	 *  @param   {Object} oGrid Grid object containing the display grid elements for the cloned 
736
	 *                    column (left or right)
737
	 *  @param   {Array} aiColumns Column indexes which should be operated on from the DataTable
738
	 *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
739
	 *  @private
740
	 */
741
	"_fnClone": function ( oClone, oGrid, aiColumns, bAll )
742
	{
743
		var that = this,
744
			i, iLen, jq, nTarget, iColumn, nClone, iIndex;
745
 
746
		/* 
747
		 * Header
748
		 */
749
		if ( bAll )
750
		{
751
			if ( oClone.header !== null )
752
			{
753
				oClone.header.parentNode.removeChild( oClone.header );
754
			}
755
			oClone.header = $(this.dom.header).clone(true)[0];
756
			oClone.header.className += " DTFC_Cloned";
757
			oClone.header.style.width = "100%";
758
			oGrid.head.appendChild( oClone.header );
759
 
760
			/* Copy the DataTables layout cache for the header for our floating column */
761
			var aoCloneLayout = this._fnCopyLayout( this.s.dt.aoHeader, aiColumns );
762
			var jqCloneThead = $('>thead', oClone.header);
763
			jqCloneThead.empty();
764
 
765
			/* Add the created cloned TR elements to the table */
766
			for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
767
			{
768
				jqCloneThead[0].appendChild( aoCloneLayout[i].nTr );
769
			}
770
 
771
			/* Use the handy _fnDrawHead function in DataTables to do the rowspan/colspan
772
			 * calculations for us
773
			 */
774
			this.s.dt.oApi._fnDrawHead( this.s.dt, aoCloneLayout, true );
775
		}
776
		else
777
		{
778
			for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
779
			{
780
				$('>thead th:eq('+iIndex+')', oClone.header)[0].className =
781
					this.s.dt.aoColumns[ aiColumns[iIndex] ].nTh.className;
782
 
783
				$('>thead th:eq('+iIndex+') span.DataTables_sort_icon', oClone.header).each( function (i) {
784
					this.className = $('span.DataTables_sort_icon', that.s.dt.aoColumns[ aiColumns[iIndex] ].nTh)[i].className;
785
				} );
786
			}
787
		}
788
		this._fnEqualiseHeights( 'thead', this.dom.header, oClone.header );
789
 
790
		/* 
791
		 * Body
792
		 */
793
		if ( this.s.sHeightMatch == 'auto' )
794
		{
795
			/* Remove any heights which have been applied already and let the browser figure it out */
796
			$('>tbody>tr', that.dom.body).css('height', 'auto');
797
		}
798
 
799
		if ( oClone.body !== null )
800
		{
801
			oClone.body.parentNode.removeChild( oClone.body );
802
			oClone.body = null;
803
		}
804
 
805
		oClone.body = $(this.dom.body).clone(true)[0];
806
		oClone.body.className += " DTFC_Cloned";
807
		oClone.body.style.paddingBottom = this.s.dt.oScroll.iBarWidth+"px";
808
		oClone.body.style.marginBottom = (this.s.dt.oScroll.iBarWidth*2)+"px"; /* For IE */
809
		if ( oClone.body.getAttribute('id') !== null )
810
		{
811
			oClone.body.removeAttribute('id');
812
		}
813
 
814
		$('>thead>tr', oClone.body).empty();
815
		$('>tfoot', oClone.body).empty();
816
 
817
		var nBody = $('tbody', oClone.body)[0];
818
		$(nBody).empty();
819
		if ( this.s.dt.aiDisplay.length > 0 )
820
		{
821
			$('>tbody>tr', that.dom.body).each( function (z) {
822
				var n = this.cloneNode(false);
823
				var i = that.s.dt.oFeatures.bServerSide===false ?
824
					that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z;
825
				for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
826
				{
827
					iColumn = aiColumns[iIndex];
828
					if ( typeof that.s.dt.aoData[i]._anHidden[iColumn] != 'undefined' )
829
					{
830
						nClone = $(that.s.dt.aoData[i]._anHidden[iColumn]).clone(true)[0];
831
						nClone.style.width = that.s.aiWidths[iColumn]+"px";
832
						n.appendChild( nClone );
833
					}
834
				}
835
				nBody.appendChild( n );
836
			} );
837
		}
838
		else
839
		{
840
			$('>tbody>tr', that.dom.body).each( function (z) {
841
				nClone = this.cloneNode(true);
842
				nClone.className += ' DTFC_NoData';
843
				$('td', nClone).html('');
844
				nBody.appendChild( nClone );
845
			} );
846
		}
847
 
848
		oClone.body.style.width = "100%";
849
		oGrid.body.appendChild( oClone.body );
850
 
851
		this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body );
852
 
853
		/*
854
		 * Footer
855
		 */
856
		if ( this.s.dt.nTFoot !== null )
857
		{
858
			if ( bAll )
859
			{
860
				if ( oClone.footer !== null )
861
				{
862
					oClone.footer.parentNode.removeChild( oClone.footer );
863
				}
864
				oClone.footer = $(this.dom.footer).clone(true)[0];
865
				oClone.footer.className += " DTFC_Cloned";
866
				oClone.footer.style.width = "100%";
867
				oGrid.foot.appendChild( oClone.footer );
868
 
869
				/* Copy the footer just like we do for the header */
870
				var aoCloneLayout = this._fnCopyLayout( this.s.dt.aoFooter, aiColumns );
871
				var jqCloneTfoot = $('>tfoot', oClone.footer);
872
				jqCloneTfoot.empty();
873
 
874
				for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
875
				{
876
					jqCloneTfoot[0].appendChild( aoCloneLayout[i].nTr );
877
				}
878
				this.s.dt.oApi._fnDrawHead( this.s.dt, aoCloneLayout, true );
879
			}
880
			else
881
			{
882
				for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
883
				{
884
					$('>tfoot th:eq('+iIndex+')', oClone.footer)[0].className =
885
						this.s.dt.aoColumns[ aiColumns[iIndex] ].nTf.className;
886
				}
887
			}
888
			this._fnEqualiseHeights( 'tfoot', this.dom.footer, oClone.footer );
889
		}
890
 
891
		/* Equalise the column widths between the header footer and body - body get's priority */
892
		var jqBody = $('>tbody>tr:eq(0)', oClone.body);
893
		var jqHead = $('>thead>tr:eq(0)', oClone.header);
894
		if ( this.s.dt.nTFoot !== null )
895
		{
896
			var jqFoot = $('>tfoot>tr:eq(0)', oClone.footer);
897
		}
898
 
899
		jqBody.children().each( function (i) {
900
			var iWidth = $(this).width();
901
 
902
			jqHead.children(':eq('+i+')').width( iWidth );
903
			if ( that.s.dt.nTFoot !== null )
904
			{
905
				jqFoot.children(':eq('+i+')').width( iWidth );
906
			}
907
		} );
908
	},
909
 
910
 
911
	/**
912
	 * From a given table node (THEAD etc), get a list of TR direct child elements
913
	 *  @param   {Node} nIn Table element to search for TR elements (THEAD, TBODY or TFOOT element)
914
	 *  @returns {Array} List of TR elements found
915
	 *  @private
916
	 */
917
	"_fnGetTrNodes": function ( nIn )
918
	{
919
		var aOut = [];
920
		for ( var i=0, iLen=nIn.childNodes.length ; i<iLen ; i++ )
921
		{
922
			if ( nIn.childNodes[i].nodeName.toUpperCase() == "TR" )
923
			{
924
				aOut.push( nIn.childNodes[i] );
925
			}
926
		}
927
		return aOut;
928
	},
929
 
930
 
931
	/**
932
	 * Equalise the heights of the rows in a given table node in a cross browser way
933
	 *  @returns {void}
934
	 *  @param   {String} nodeName Node type - thead, tbody or tfoot
935
	 *  @param   {Node} original Original node to take the heights from
936
	 *  @param   {Node} clone Copy the heights to
937
	 *  @private
938
	 */
939
	"_fnEqualiseHeights": function ( nodeName, original, clone )
940
	{
941
		if ( this.s.sHeightMatch == 'none' )
942
		{
943
			return;
944
		}
945
 
946
		var that = this,
947
			i, iLen, iHeight, iHeight2, iHeightOriginal, iHeightClone,
948
			rootOriginal = original.getElementsByTagName(nodeName)[0],
949
			rootClone    = clone.getElementsByTagName(nodeName)[0],
950
			jqBoxHack    = $('>'+nodeName+'>tr:eq(0)', original).children(':first'),
951
			iBoxHack     = jqBoxHack.outerHeight() - jqBoxHack.height(),
952
			anOriginal   = this._fnGetTrNodes( rootOriginal ),
953
		 	anClone      = this._fnGetTrNodes( rootClone );
954
 
955
		for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
956
		{
957
			if ( this.s.sHeightMatch == 'semiauto' && typeof anOriginal[i]._DTTC_iHeight != 'undefined' && 
958
				anOriginal[i]._DTTC_iHeight !== null )
959
			{
960
				/* Oddly enough, IE / Chrome seem not to copy the style height - Mozilla and Opera keep it */
961
				if ( $.browser.msie )
962
				{
963
					$(anClone[i]).children().height( anOriginal[i]._DTTC_iHeight-iBoxHack );
964
				}
965
				continue;
966
			}
967
 
968
			iHeightOriginal = anOriginal[i].offsetHeight;
969
			iHeightClone = anClone[i].offsetHeight;
970
			iHeight = iHeightClone > iHeightOriginal ? iHeightClone : iHeightOriginal;
971
 
972
			if ( this.s.sHeightMatch == 'semiauto' )
973
			{
974
				anOriginal[i]._DTTC_iHeight = iHeight;
975
			}
976
 
977
			/* Can we use some kind of object detection here?! This is very nasty - damn browsers */
978
			if ( $.browser.msie )
979
			{
980
				$(anClone[i]).children().height( iHeight-iBoxHack );
981
				$(anOriginal[i]).children().height( iHeight-iBoxHack );	
982
			}
983
			else
984
			{
985
				anClone[i].style.height = iHeight+"px";
986
				anOriginal[i].style.height = iHeight+"px";
987
			}
988
		}
989
	}
990
};
991
 
992
 
993
 
994
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
995
 * Statics
996
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
997
 
998
 
999
/**
1000
 * FixedColumns default settings for initialisation
1001
 *  @namespace
1002
 *  @static
1003
 */
1004
FixedColumns.defaults = {
1005
	/** 
1006
	 * Number of left hand columns to fix in position
1007
	 *  @type     int
1008
	 *  @default  1
1009
	 *  @static
1010
	 *  @example
1011
	 *  	var oTable = $('#example').dataTable( {
1012
	 *  		"sScrollX": "100%"
1013
	 *  	} );
1014
	 *  	new FixedColumns( oTable, {
1015
	 *  		"iLeftColumns": 2
1016
	 *  	} );
1017
	 */
1018
	"iLeftColumns": 1,
1019
 
1020
	/** 
1021
	 * Number of right hand columns to fix in position
1022
	 *  @type     int
1023
	 *  @default  0
1024
	 *  @static
1025
	 *  @example
1026
	 *  	var oTable = $('#example').dataTable( {
1027
	 *  		"sScrollX": "100%"
1028
	 *  	} );
1029
	 *  	new FixedColumns( oTable, {
1030
	 *  		"iRightColumns": 1
1031
	 *  	} );
1032
	 */
1033
	"iRightColumns": 0,
1034
 
1035
	/** 
1036
	 * Draw callback function which is called when FixedColumns has redrawn the fixed assets
1037
	 *  @type     function(object, object):void
1038
	 *  @default  null
1039
	 *  @static
1040
	 *  @example
1041
	 *  	var oTable = $('#example').dataTable( {
1042
	 *  		"sScrollX": "100%"
1043
	 *  	} );
1044
	 *  	new FixedColumns( oTable, {
1045
	 *  		"fnDrawCallback": function () {
1046
	 *				alert( "FixedColumns redraw" );
1047
	 *			}
1048
	 *  	} );
1049
	 */
1050
	"fnDrawCallback": null,
1051
 
1052
	/** 
1053
	 * Type of left column size calculation. Can take the values of "fixed", whereby the iLeftWidth
1054
	 * value will be treated as a pixel value, or "relative" for which case iLeftWidth will be
1055
	 * treated as a percentage value.
1056
	 *  @type     string
1057
	 *  @default  fixed
1058
	 *  @static
1059
	 *  @example
1060
	 *  	var oTable = $('#example').dataTable( {
1061
	 *  		"sScrollX": "100%"
1062
	 *  	} );
1063
	 *  	new FixedColumns( oTable, {
1064
	 *  		"sLeftWidth": "relative",
1065
	 *  		"iLeftWidth": 10 // percentage
1066
	 *  	} );
1067
	 */
1068
	"sLeftWidth": "fixed",
1069
 
1070
	/** 
1071
	 * Width to set for the width of the left fixed column(s) - note that the behaviour of this
1072
	 * property is directly effected by the sLeftWidth property. If not defined then this property
1073
	 * is calculated automatically from what has been assigned by DataTables.
1074
	 *  @type     int
1075
	 *  @default  null
1076
	 *  @static
1077
	 *  @example
1078
	 *  	var oTable = $('#example').dataTable( {
1079
	 *  		"sScrollX": "100%"
1080
	 *  	} );
1081
	 *  	new FixedColumns( oTable, {
1082
	 *  		"iLeftWidth": 100 // pixels
1083
	 *  	} );
1084
	 */
1085
	"iLeftWidth": null,
1086
 
1087
	/** 
1088
	 * Type of right column size calculation. Can take the values of "fixed", whereby the 
1089
	 * iRightWidth value will be treated as a pixel value, or "relative" for which case 
1090
	 * iRightWidth will be treated as a percentage value.
1091
	 *  @type     string
1092
	 *  @default  fixed
1093
	 *  @static
1094
	 *  @example
1095
	 *  	var oTable = $('#example').dataTable( {
1096
	 *  		"sScrollX": "100%"
1097
	 *  	} );
1098
	 *  	new FixedColumns( oTable, {
1099
	 *  		"sRightWidth": "relative",
1100
	 *  		"iRightWidth": 10 // percentage
1101
	 *  	} );
1102
	 */
1103
	"sRightWidth": "fixed",
1104
 
1105
	/**
1106
	 * Width to set for the width of the right fixed column(s) - note that the behaviour of this
1107
	 * property is directly effected by the sRightWidth property. If not defined then this property
1108
	 * is calculated automatically from what has been assigned by DataTables.
1109
	 *  @type     int
1110
	 *  @default  null
1111
	 *  @static
1112
	 *  @example
1113
	 *  	var oTable = $('#example').dataTable( {
1114
	 *  		"sScrollX": "100%"
1115
	 *  	} );
1116
	 *  	new FixedColumns( oTable, {
1117
	 *  		"iRightWidth": 200 // pixels
1118
	 *  	} );
1119
	 */
1120
	"iRightWidth": null,
1121
 
1122
	/** 
1123
	 * Height matching algorthim to use. This can be "none" which will result in no height
1124
	 * matching being applied by FixedColumns (height matching could be forced by CSS in this
1125
	 * case), "semiauto" whereby the height calculation will be performed once, and the result
1126
	 * cached to be used again (fnRecalculateHeight can be used to force recalculation), or
1127
	 * "auto" when height matching is performed on every draw (slowest but must accurate)
1128
	 *  @type     string
1129
	 *  @default  semiauto
1130
	 *  @static
1131
	 *  @example
1132
	 *  	var oTable = $('#example').dataTable( {
1133
	 *  		"sScrollX": "100%"
1134
	 *  	} );
1135
	 *  	new FixedColumns( oTable, {
1136
	 *  		"sHeightMatch": "auto"
1137
	 *  	} );
1138
	 */
1139
	"sHeightMatch": "semiauto"
1140
};
1141
 
1142
 
1143
 
1144
 
1145
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1146
 * Constants
1147
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1148
 
1149
 
1150
/**
1151
 * Name of this class
1152
 *  @constant CLASS
1153
 *  @type     String
1154
 *  @default  FixedColumns
1155
 */
1156
FixedColumns.prototype.CLASS = "FixedColumns";
1157
 
1158
 
1159
/**
1160
 * FixedColumns version
1161
 *  @constant  FixedColumns.VERSION
1162
 *  @type      String
1163
 *  @default   See code
1164
 *  @static
1165
 */
1166
FixedColumns.VERSION = "2.0.1";
1167
 
1168
 
1169
 
1170
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1171
 * Fired events (for documentation)
1172
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1173
 
1174
 
1175
/**
1176
 * Event fired whenever FixedColumns redraws the fixed columns (i.e. clones the table elements from the main DataTable). This will occur whenever the DataTable that the FixedColumns instance is attached does its own draw.
1177
 * @name FixedColumns#draw
1178
 * @event
1179
 * @param {event} e jQuery event object
1180
 * @param {object} o Event parameters from FixedColumns
1181
 * @param {object} o.leftClone Instance's object dom.clone.left for easy reference. This object contains references to the left fixed clumn column's nodes
1182
 * @param {object} o.rightClone Instance's object dom.clone.right for easy reference. This object contains references to the right fixed clumn column's nodes
1183
 */
1184
 
1185
})(jQuery, window, document);