Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
875 lars 1
/*! FixedColumns 3.1.0
2
 * ©2010-2014 SpryMedia Ltd - datatables.net/license
3
 */
4
 
5
/**
6
 * @summary     FixedColumns
7
 * @description Freeze columns in place on a scrolling DataTable
8
 * @version     3.1.0
9
 * @file        dataTables.fixedColumns.js
10
 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
11
 * @contact     www.sprymedia.co.uk/contact
12
 * @copyright   Copyright 2010-2014 SpryMedia Ltd.
13
 *
14
 * This source file is free software, available under the following license:
15
 *   MIT license - http://datatables.net/license/mit
16
 *
17
 * This source file is distributed in the hope that it will be useful, but
18
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19
 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20
 *
21
 * For details please refer to: http://www.datatables.net
22
 */
23
 
24
 
25
(function(window, document, undefined) {
26
 
27
 
28
var factory = function( $, DataTable ) {
29
"use strict";
30
 
31
/**
32
 * When making use of DataTables' x-axis scrolling feature, you may wish to
33
 * fix the left most column in place. This plug-in for DataTables provides
34
 * exactly this option (note for non-scrolling tables, please use the
35
 * FixedHeader plug-in, which can fix headers, footers and columns). Key
36
 * features include:
37
 *
38
 * * Freezes the left or right most columns to the side of the table
39
 * * Option to freeze two or more columns
40
 * * Full integration with DataTables' scrolling options
41
 * * Speed - FixedColumns is fast in its operation
42
 *
43
 *  @class
44
 *  @constructor
45
 *  @global
46
 *  @param {object} dt DataTables instance. With DataTables 1.10 this can also
47
 *    be a jQuery collection, a jQuery selector, DataTables API instance or
48
 *    settings object.
49
 *  @param {object} [init={}] Configuration object for FixedColumns. Options are
50
 *    defined by {@link FixedColumns.defaults}
51
 *
52
 *  @requires jQuery 1.7+
53
 *  @requires DataTables 1.8.0+
54
 *
55
 *  @example
56
 *      var table = $('#example').dataTable( {
57
 *        "scrollX": "100%"
58
 *      } );
59
 *      new $.fn.dataTable.fixedColumns( table );
60
 */
61
var FixedColumns = function ( dt, init ) {
62
	var that = this;
63
 
64
	/* Sanity check - you just know it will happen */
65
	if ( ! ( this instanceof FixedColumns ) ) {
66
		alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
67
		return;
68
	}
69
 
70
	if ( init === undefined || init === true ) {
71
		init = {};
72
	}
73
 
74
	// Use the DataTables Hungarian notation mapping method, if it exists to
75
	// provide forwards compatibility for camel case variables
76
	var camelToHungarian = $.fn.dataTable.camelToHungarian;
77
	if ( camelToHungarian ) {
78
		camelToHungarian( FixedColumns.defaults, FixedColumns.defaults, true );
79
		camelToHungarian( FixedColumns.defaults, init );
80
	}
81
 
82
	// v1.10 allows the settings object to be got form a number of sources
83
	var dtSettings = new $.fn.dataTable.Api( dt ).settings()[0];
84
 
85
	/**
86
	 * Settings object which contains customisable information for FixedColumns instance
87
	 * @namespace
88
	 * @extends FixedColumns.defaults
89
	 * @private
90
	 */
91
	this.s = {
92
		/**
93
		 * DataTables settings objects
94
		 *  @type     object
95
		 *  @default  Obtained from DataTables instance
96
		 */
97
		"dt": dtSettings,
98
 
99
		/**
100
		 * Number of columns in the DataTable - stored for quick access
101
		 *  @type     int
102
		 *  @default  Obtained from DataTables instance
103
		 */
104
		"iTableColumns": dtSettings.aoColumns.length,
105
 
106
		/**
107
		 * Original outer widths of the columns as rendered by DataTables - used to calculate
108
		 * the FixedColumns grid bounding box
109
		 *  @type     array.<int>
110
		 *  @default  []
111
		 */
112
		"aiOuterWidths": [],
113
 
114
		/**
115
		 * Original inner widths of the columns as rendered by DataTables - used to apply widths
116
		 * to the columns
117
		 *  @type     array.<int>
118
		 *  @default  []
119
		 */
120
		"aiInnerWidths": []
121
	};
122
 
123
 
124
	/**
125
	 * DOM elements used by the class instance
126
	 * @namespace
127
	 * @private
128
	 *
129
	 */
130
	this.dom = {
131
		/**
132
		 * DataTables scrolling element
133
		 *  @type     node
134
		 *  @default  null
135
		 */
136
		"scroller": null,
137
 
138
		/**
139
		 * DataTables header table
140
		 *  @type     node
141
		 *  @default  null
142
		 */
143
		"header": null,
144
 
145
		/**
146
		 * DataTables body table
147
		 *  @type     node
148
		 *  @default  null
149
		 */
150
		"body": null,
151
 
152
		/**
153
		 * DataTables footer table
154
		 *  @type     node
155
		 *  @default  null
156
		 */
157
		"footer": null,
158
 
159
		/**
160
		 * Display grid elements
161
		 * @namespace
162
		 */
163
		"grid": {
164
			/**
165
			 * Grid wrapper. This is the container element for the 3x3 grid
166
			 *  @type     node
167
			 *  @default  null
168
			 */
169
			"wrapper": null,
170
 
171
			/**
172
			 * DataTables scrolling element. This element is the DataTables
173
			 * component in the display grid (making up the main table - i.e.
174
			 * not the fixed columns).
175
			 *  @type     node
176
			 *  @default  null
177
			 */
178
			"dt": null,
179
 
180
			/**
181
			 * Left fixed column grid components
182
			 * @namespace
183
			 */
184
			"left": {
185
				"wrapper": null,
186
				"head": null,
187
				"body": null,
188
				"foot": null
189
			},
190
 
191
			/**
192
			 * Right fixed column grid components
193
			 * @namespace
194
			 */
195
			"right": {
196
				"wrapper": null,
197
				"head": null,
198
				"body": null,
199
				"foot": null
200
			}
201
		},
202
 
203
		/**
204
		 * Cloned table nodes
205
		 * @namespace
206
		 */
207
		"clone": {
208
			/**
209
			 * Left column cloned table nodes
210
			 * @namespace
211
			 */
212
			"left": {
213
				/**
214
				 * Cloned header table
215
				 *  @type     node
216
				 *  @default  null
217
				 */
218
				"header": null,
219
 
220
				/**
221
				 * Cloned body table
222
				 *  @type     node
223
				 *  @default  null
224
				 */
225
				"body": null,
226
 
227
				/**
228
				 * Cloned footer table
229
				 *  @type     node
230
				 *  @default  null
231
				 */
232
				"footer": null
233
			},
234
 
235
			/**
236
			 * Right column cloned table nodes
237
			 * @namespace
238
			 */
239
			"right": {
240
				/**
241
				 * Cloned header table
242
				 *  @type     node
243
				 *  @default  null
244
				 */
245
				"header": null,
246
 
247
				/**
248
				 * Cloned body table
249
				 *  @type     node
250
				 *  @default  null
251
				 */
252
				"body": null,
253
 
254
				/**
255
				 * Cloned footer table
256
				 *  @type     node
257
				 *  @default  null
258
				 */
259
				"footer": null
260
			}
261
		}
262
	};
263
 
264
	if ( dtSettings._oFixedColumns ) {
265
		throw 'FixedColumns already initialised on this table';
266
	}
267
 
268
	/* Attach the instance to the DataTables instance so it can be accessed easily */
269
	dtSettings._oFixedColumns = this;
270
 
271
	/* Let's do it */
272
	if ( ! dtSettings._bInitComplete )
273
	{
274
		dtSettings.oApi._fnCallbackReg( dtSettings, 'aoInitComplete', function () {
275
			that._fnConstruct( init );
276
		}, 'FixedColumns' );
277
	}
278
	else
279
	{
280
		this._fnConstruct( init );
281
	}
282
};
283
 
284
 
285
 
286
FixedColumns.prototype = /** @lends FixedColumns.prototype */{
287
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
288
	 * Public methods
289
	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
290
 
291
	/**
292
	 * Update the fixed columns - including headers and footers. Note that FixedColumns will
293
	 * automatically update the display whenever the host DataTable redraws.
294
	 *  @returns {void}
295
	 *  @example
296
	 *      var table = $('#example').dataTable( {
297
	 *          "scrollX": "100%"
298
	 *      } );
299
	 *      var fc = new $.fn.dataTable.fixedColumns( table );
300
	 *
301
	 *      // at some later point when the table has been manipulated....
302
	 *      fc.fnUpdate();
303
	 */
304
	"fnUpdate": function ()
305
	{
306
		this._fnDraw( true );
307
	},
308
 
309
 
310
	/**
311
	 * Recalculate the resizes of the 3x3 grid that FixedColumns uses for display of the table.
312
	 * This is useful if you update the width of the table container. Note that FixedColumns will
313
	 * perform this function automatically when the window.resize event is fired.
314
	 *  @returns {void}
315
	 *  @example
316
	 *      var table = $('#example').dataTable( {
317
	 *          "scrollX": "100%"
318
	 *      } );
319
	 *      var fc = new $.fn.dataTable.fixedColumns( table );
320
	 *
321
	 *      // Resize the table container and then have FixedColumns adjust its layout....
322
	 *      $('#content').width( 1200 );
323
	 *      fc.fnRedrawLayout();
324
	 */
325
	"fnRedrawLayout": function ()
326
	{
327
		this._fnColCalc();
328
		this._fnGridLayout();
329
		this.fnUpdate();
330
	},
331
 
332
 
333
	/**
334
	 * Mark a row such that it's height should be recalculated when using 'semiauto' row
335
	 * height matching. This function will have no effect when 'none' or 'auto' row height
336
	 * matching is used.
337
	 *  @param   {Node} nTr TR element that should have it's height recalculated
338
	 *  @returns {void}
339
	 *  @example
340
	 *      var table = $('#example').dataTable( {
341
	 *          "scrollX": "100%"
342
	 *      } );
343
	 *      var fc = new $.fn.dataTable.fixedColumns( table );
344
	 *
345
	 *      // manipulate the table - mark the row as needing an update then update the table
346
	 *      // this allows the redraw performed by DataTables fnUpdate to recalculate the row
347
	 *      // height
348
	 *      fc.fnRecalculateHeight();
349
	 *      table.fnUpdate( $('#example tbody tr:eq(0)')[0], ["insert date", 1, 2, 3 ... ]);
350
	 */
351
	"fnRecalculateHeight": function ( nTr )
352
	{
353
		delete nTr._DTTC_iHeight;
354
		nTr.style.height = 'auto';
355
	},
356
 
357
 
358
	/**
359
	 * Set the height of a given row - provides cross browser compatibility
360
	 *  @param   {Node} nTarget TR element that should have it's height recalculated
361
	 *  @param   {int} iHeight Height in pixels to set
362
	 *  @returns {void}
363
	 *  @example
364
	 *      var table = $('#example').dataTable( {
365
	 *          "scrollX": "100%"
366
	 *      } );
367
	 *      var fc = new $.fn.dataTable.fixedColumns( table );
368
	 *
369
	 *      // You may want to do this after manipulating a row in the fixed column
370
	 *      fc.fnSetRowHeight( $('#example tbody tr:eq(0)')[0], 50 );
371
	 */
372
	"fnSetRowHeight": function ( nTarget, iHeight )
373
	{
374
		nTarget.style.height = iHeight+"px";
375
	},
376
 
377
 
378
	/**
379
	 * Get data index information about a row or cell in the table body.
380
	 * This function is functionally identical to fnGetPosition in DataTables,
381
	 * taking the same parameter (TH, TD or TR node) and returning exactly the
382
	 * the same information (data index information). THe difference between
383
	 * the two is that this method takes into account the fixed columns in the
384
	 * table, so you can pass in nodes from the master table, or the cloned
385
	 * tables and get the index position for the data in the main table.
386
	 *  @param {node} node TR, TH or TD element to get the information about
387
	 *  @returns {int} If nNode is given as a TR, then a single index is
388
	 *    returned, or if given as a cell, an array of [row index, column index
389
	 *    (visible), column index (all)] is given.
390
	 */
391
	"fnGetPosition": function ( node )
392
	{
393
		var idx;
394
		var inst = this.s.dt.oInstance;
395
 
396
		if ( ! $(node).parents('.DTFC_Cloned').length )
397
		{
398
			// Not in a cloned table
399
			return inst.fnGetPosition( node );
400
		}
401
		else
402
		{
403
			// Its in the cloned table, so need to look up position
404
			if ( node.nodeName.toLowerCase() === 'tr' ) {
405
				idx = $(node).index();
406
				return inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] );
407
			}
408
			else
409
			{
410
				var colIdx = $(node).index();
411
				idx = $(node.parentNode).index();
412
				var row = inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] );
413
 
414
				return [
415
					row,
416
					colIdx,
417
					inst.oApi._fnVisibleToColumnIndex( this.s.dt, colIdx )
418
				];
419
			}
420
		}
421
	},
422
 
423
 
424
 
425
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
426
	 * Private methods (they are of course public in JS, but recommended as private)
427
	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
428
 
429
	/**
430
	 * Initialisation for FixedColumns
431
	 *  @param   {Object} oInit User settings for initialisation
432
	 *  @returns {void}
433
	 *  @private
434
	 */
435
	"_fnConstruct": function ( oInit )
436
	{
437
		var i, iLen, iWidth,
438
			that = this;
439
 
440
		/* Sanity checking */
441
		if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' ||
442
		     this.s.dt.oInstance.fnVersionCheck( '1.8.0' ) !== true )
443
		{
444
			alert( "FixedColumns "+FixedColumns.VERSION+" required DataTables 1.8.0 or later. "+
445
				"Please upgrade your DataTables installation" );
446
			return;
447
		}
448
 
449
		if ( this.s.dt.oScroll.sX === "" )
450
		{
451
			this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
452
				"x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+
453
				"column fixing when scrolling is not enabled" );
454
			return;
455
		}
456
 
457
		/* Apply the settings from the user / defaults */
458
		this.s = $.extend( true, this.s, FixedColumns.defaults, oInit );
459
 
460
		/* Set up the DOM as we need it and cache nodes */
461
		var classes = this.s.dt.oClasses;
462
		this.dom.grid.dt = $(this.s.dt.nTable).parents('div.'+classes.sScrollWrapper)[0];
463
		this.dom.scroller = $('div.'+classes.sScrollBody, this.dom.grid.dt )[0];
464
 
465
		/* Set up the DOM that we want for the fixed column layout grid */
466
		this._fnColCalc();
467
		this._fnGridSetup();
468
 
469
		/* Event handlers */
470
		var mouseController;
471
 
472
		// When the body is scrolled - scroll the left and right columns
473
		$(this.dom.scroller)
474
			.on( 'mouseover.DTFC touchstart.DTFC', function () {
475
				mouseController = 'main';
476
			} )
477
			.on( 'scroll.DTFC', function (e) {
478
				if ( ! mouseController && e.originalEvent ) {
479
					mouseController = 'main';
480
				}
481
 
482
				if ( mouseController === 'main' ) {
483
					if ( that.s.iLeftColumns > 0 ) {
484
						that.dom.grid.left.liner.scrollTop = that.dom.scroller.scrollTop;
485
					}
486
					if ( that.s.iRightColumns > 0 ) {
487
						that.dom.grid.right.liner.scrollTop = that.dom.scroller.scrollTop;
488
					}
489
				}
490
			} );
491
 
492
		var wheelType = 'onwheel' in document.createElement('div') ?
493
			'wheel.DTFC' :
494
			'mousewheel.DTFC';
495
 
496
		if ( that.s.iLeftColumns > 0 ) {
497
			// When scrolling the left column, scroll the body and right column
498
			$(that.dom.grid.left.liner)
499
				.on( 'mouseover.DTFC touchstart.DTFC', function () {
500
					mouseController = 'left';
501
				} )
502
				.on( 'scroll.DTFC', function ( e ) {
503
					if ( ! mouseController && e.originalEvent ) {
504
						mouseController = 'left';
505
					}
506
 
507
					if ( mouseController === 'left' ) {
508
						that.dom.scroller.scrollTop = that.dom.grid.left.liner.scrollTop;
509
						if ( that.s.iRightColumns > 0 ) {
510
							that.dom.grid.right.liner.scrollTop = that.dom.grid.left.liner.scrollTop;
511
						}
512
					}
513
				} )
514
				.on( wheelType, function(e) {
515
					// Pass horizontal scrolling through
516
					var xDelta = e.type === 'wheel' ?
517
						-e.originalEvent.deltaX :
518
						e.originalEvent.wheelDeltaX;
519
					that.dom.scroller.scrollLeft -= xDelta;
520
				} );
521
		}
522
 
523
		if ( that.s.iRightColumns > 0 ) {
524
			// When scrolling the right column, scroll the body and the left column
525
			$(that.dom.grid.right.liner)
526
				.on( 'mouseover.DTFC touchstart.DTFC', function () {
527
					mouseController = 'right';
528
				} )
529
				.on( 'scroll.DTFC', function ( e ) {
530
					if ( ! mouseController && e.originalEvent ) {
531
						mouseController = 'right';
532
					}
533
 
534
					if ( mouseController === 'right' ) {
535
						that.dom.scroller.scrollTop = that.dom.grid.right.liner.scrollTop;
536
						if ( that.s.iLeftColumns > 0 ) {
537
							that.dom.grid.left.liner.scrollTop = that.dom.grid.right.liner.scrollTop;
538
						}
539
					}
540
				} )
541
				.on( wheelType, function(e) {
542
					// Pass horizontal scrolling through
543
					var xDelta = e.type === 'wheel' ?
544
						-e.originalEvent.deltaX :
545
						e.originalEvent.wheelDeltaX;
546
					that.dom.scroller.scrollLeft -= xDelta;
547
				} );
548
		}
549
 
550
		$(window).on( 'resize.DTFC', function () {
551
			that._fnGridLayout.call( that );
552
		} );
553
 
554
		var bFirstDraw = true;
555
		var jqTable = $(this.s.dt.nTable);
556
 
557
		jqTable
558
			.on( 'draw.dt.DTFC', function () {
559
				that._fnDraw.call( that, bFirstDraw );
560
				bFirstDraw = false;
561
			} )
562
			.on( 'column-sizing.dt.DTFC', function () {
563
				that._fnColCalc();
564
				that._fnGridLayout( that );
565
			} )
566
			.on( 'column-visibility.dt.DTFC', function () {
567
				that._fnColCalc();
568
				that._fnGridLayout( that );
569
				that._fnDraw( true );
570
			} )
571
			.on( 'destroy.dt.DTFC', function () {
572
				jqTable.off( 'column-sizing.dt.DTFC column-visibility.dt.DTFC destroy.dt.DTFC draw.dt.DTFC' );
573
 
574
				$(that.dom.scroller).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC' );
575
				$(window).off( 'resize.DTFC' );
576
 
577
				$(that.dom.grid.left.liner).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC '+wheelType );
578
				$(that.dom.grid.left.wrapper).remove();
579
 
580
				$(that.dom.grid.right.liner).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC '+wheelType );
581
				$(that.dom.grid.right.wrapper).remove();
582
			} );
583
 
584
		/* Get things right to start with - note that due to adjusting the columns, there must be
585
		 * another redraw of the main table. It doesn't need to be a full redraw however.
586
		 */
587
		this._fnGridLayout();
588
		this.s.dt.oInstance.fnDraw(false);
589
	},
590
 
591
 
592
	/**
593
	 * Calculate the column widths for the grid layout
594
	 *  @returns {void}
595
	 *  @private
596
	 */
597
	"_fnColCalc": function ()
598
	{
599
		var that = this;
600
		var iLeftWidth = 0;
601
		var iRightWidth = 0;
602
 
603
		this.s.aiInnerWidths = [];
604
		this.s.aiOuterWidths = [];
605
 
606
		$.each( this.s.dt.aoColumns, function (i, col) {
607
			var th = $(col.nTh);
608
			var border;
609
 
610
			if ( ! th.filter(':visible').length ) {
611
				that.s.aiInnerWidths.push( 0 );
612
				that.s.aiOuterWidths.push( 0 );
613
			}
614
			else
615
			{
616
				// Inner width is used to assign widths to cells
617
				// Outer width is used to calculate the container
618
				var iWidth = th.outerWidth();
619
 
620
				// When working with the left most-cell, need to add on the
621
				// table's border to the outerWidth, since we need to take
622
				// account of it, but it isn't in any cell
623
				if ( that.s.aiOuterWidths.length === 0 ) {
624
					border = $(that.s.dt.nTable).css('border-left-width');
625
					iWidth += typeof border === 'string' ? 1 : parseInt( border, 10 );
626
				}
627
 
628
				// Likewise with the final column on the right
629
				if ( that.s.aiOuterWidths.length === that.s.dt.aoColumns.length-1 ) {
630
					border = $(that.s.dt.nTable).css('border-right-width');
631
					iWidth += typeof border === 'string' ? 1 : parseInt( border, 10 );
632
				}
633
 
634
				that.s.aiOuterWidths.push( iWidth );
635
				that.s.aiInnerWidths.push( th.width() );
636
 
637
				if ( i < that.s.iLeftColumns )
638
				{
639
					iLeftWidth += iWidth;
640
				}
641
 
642
				if ( that.s.iTableColumns-that.s.iRightColumns <= i )
643
				{
644
					iRightWidth += iWidth;
645
				}
646
			}
647
		} );
648
 
649
		this.s.iLeftWidth = iLeftWidth;
650
		this.s.iRightWidth = iRightWidth;
651
	},
652
 
653
 
654
	/**
655
	 * Set up the DOM for the fixed column. The way the layout works is to create a 1x3 grid
656
	 * for the left column, the DataTable (for which we just reuse the scrolling element DataTable
657
	 * puts into the DOM) and the right column. In each of he two fixed column elements there is a
658
	 * grouping wrapper element and then a head, body and footer wrapper. In each of these we then
659
	 * place the cloned header, body or footer tables. This effectively gives as 3x3 grid structure.
660
	 *  @returns {void}
661
	 *  @private
662
	 */
663
	"_fnGridSetup": function ()
664
	{
665
		var that = this;
666
		var oOverflow = this._fnDTOverflow();
667
		var block;
668
 
669
		this.dom.body = this.s.dt.nTable;
670
		this.dom.header = this.s.dt.nTHead.parentNode;
671
		this.dom.header.parentNode.parentNode.style.position = "relative";
672
 
673
		var nSWrapper =
674
			$('<div class="DTFC_ScrollWrapper" style="position:relative; clear:both;">'+
675
				'<div class="DTFC_LeftWrapper" style="position:absolute; top:0; left:0;">'+
676
					'<div class="DTFC_LeftHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
677
					'<div class="DTFC_LeftBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;">'+
678
						'<div class="DTFC_LeftBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+
679
					'</div>'+
680
					'<div class="DTFC_LeftFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
681
				'</div>'+
682
				'<div class="DTFC_RightWrapper" style="position:absolute; top:0; left:0;">'+
683
					'<div class="DTFC_RightHeadWrapper" style="position:relative; top:0; left:0;">'+
684
						'<div class="DTFC_RightHeadBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+
685
					'</div>'+
686
					'<div class="DTFC_RightBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;">'+
687
						'<div class="DTFC_RightBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+
688
					'</div>'+
689
					'<div class="DTFC_RightFootWrapper" style="position:relative; top:0; left:0;">'+
690
						'<div class="DTFC_RightFootBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+
691
					'</div>'+
692
				'</div>'+
693
			'</div>')[0];
694
		var nLeft = nSWrapper.childNodes[0];
695
		var nRight = nSWrapper.childNodes[1];
696
 
697
		this.dom.grid.dt.parentNode.insertBefore( nSWrapper, this.dom.grid.dt );
698
		nSWrapper.appendChild( this.dom.grid.dt );
699
 
700
		this.dom.grid.wrapper = nSWrapper;
701
 
702
		if ( this.s.iLeftColumns > 0 )
703
		{
704
			this.dom.grid.left.wrapper = nLeft;
705
			this.dom.grid.left.head = nLeft.childNodes[0];
706
			this.dom.grid.left.body = nLeft.childNodes[1];
707
			this.dom.grid.left.liner = $('div.DTFC_LeftBodyLiner', nSWrapper)[0];
708
 
709
			nSWrapper.appendChild( nLeft );
710
		}
711
 
712
		if ( this.s.iRightColumns > 0 )
713
		{
714
			this.dom.grid.right.wrapper = nRight;
715
			this.dom.grid.right.head = nRight.childNodes[0];
716
			this.dom.grid.right.body = nRight.childNodes[1];
717
			this.dom.grid.right.liner = $('div.DTFC_RightBodyLiner', nSWrapper)[0];
718
 
719
			block = $('div.DTFC_RightHeadBlocker', nSWrapper)[0];
720
			block.style.width = oOverflow.bar+"px";
721
			block.style.right = -oOverflow.bar+"px";
722
			this.dom.grid.right.headBlock = block;
723
 
724
			block = $('div.DTFC_RightFootBlocker', nSWrapper)[0];
725
			block.style.width = oOverflow.bar+"px";
726
			block.style.right = -oOverflow.bar+"px";
727
			this.dom.grid.right.footBlock = block;
728
 
729
			nSWrapper.appendChild( nRight );
730
		}
731
 
732
		if ( this.s.dt.nTFoot )
733
		{
734
			this.dom.footer = this.s.dt.nTFoot.parentNode;
735
			if ( this.s.iLeftColumns > 0 )
736
			{
737
				this.dom.grid.left.foot = nLeft.childNodes[2];
738
			}
739
			if ( this.s.iRightColumns > 0 )
740
			{
741
				this.dom.grid.right.foot = nRight.childNodes[2];
742
			}
743
		}
744
	},
745
 
746
 
747
	/**
748
	 * Style and position the grid used for the FixedColumns layout
749
	 *  @returns {void}
750
	 *  @private
751
	 */
752
	"_fnGridLayout": function ()
753
	{
754
		var oGrid = this.dom.grid;
755
		var iWidth = $(oGrid.wrapper).width();
756
		var iBodyHeight = $(this.s.dt.nTable.parentNode).outerHeight();
757
		var iFullHeight = $(this.s.dt.nTable.parentNode.parentNode).outerHeight();
758
		var oOverflow = this._fnDTOverflow();
759
		var
760
			iLeftWidth = this.s.iLeftWidth,
761
			iRightWidth = this.s.iRightWidth,
762
			iRight;
763
		var scrollbarAdjust = function ( node, width ) {
764
			if ( ! oOverflow.bar ) {
765
				// If there is no scrollbar (Macs) we need to hide the auto scrollbar
766
				node.style.width = (width+20)+"px";
767
				node.style.paddingRight = "20px";
768
				node.style.boxSizing = "border-box";
769
			}
770
			else {
771
				// Otherwise just overflow by the scrollbar
772
				node.style.width = (width+oOverflow.bar)+"px";
773
			}
774
		};
775
 
776
		// When x scrolling - don't paint the fixed columns over the x scrollbar
777
		if ( oOverflow.x )
778
		{
779
			iBodyHeight -= oOverflow.bar;
780
		}
781
 
782
		oGrid.wrapper.style.height = iFullHeight+"px";
783
 
784
		if ( this.s.iLeftColumns > 0 )
785
		{
786
			oGrid.left.wrapper.style.width = iLeftWidth+"px";
787
			oGrid.left.wrapper.style.height = "1px";
788
			oGrid.left.body.style.height = iBodyHeight+"px";
789
			if ( oGrid.left.foot ) {
790
				oGrid.left.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px"; // shift footer for scrollbar
791
			}
792
 
793
			scrollbarAdjust( oGrid.left.liner, iLeftWidth );
794
			oGrid.left.liner.style.height = iBodyHeight+"px";
795
		}
796
 
797
		if ( this.s.iRightColumns > 0 )
798
		{
799
			iRight = iWidth - iRightWidth;
800
			if ( oOverflow.y )
801
			{
802
				iRight -= oOverflow.bar;
803
			}
804
 
805
			oGrid.right.wrapper.style.width = iRightWidth+"px";
806
			oGrid.right.wrapper.style.left = iRight+"px";
807
			oGrid.right.wrapper.style.height = "1px";
808
			oGrid.right.body.style.height = iBodyHeight+"px";
809
			if ( oGrid.right.foot ) {
810
				oGrid.right.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px";
811
			}
812
 
813
			scrollbarAdjust( oGrid.right.liner, iRightWidth );
814
			oGrid.right.liner.style.height = iBodyHeight+"px";
815
 
816
			oGrid.right.headBlock.style.display = oOverflow.y ? 'block' : 'none';
817
			oGrid.right.footBlock.style.display = oOverflow.y ? 'block' : 'none';
818
		}
819
	},
820
 
821
 
822
	/**
823
	 * Get information about the DataTable's scrolling state - specifically if the table is scrolling
824
	 * on either the x or y axis, and also the scrollbar width.
825
	 *  @returns {object} Information about the DataTables scrolling state with the properties:
826
	 *    'x', 'y' and 'bar'
827
	 *  @private
828
	 */
829
	"_fnDTOverflow": function ()
830
	{
831
		var nTable = this.s.dt.nTable;
832
		var nTableScrollBody = nTable.parentNode;
833
		var out = {
834
			"x": false,
835
			"y": false,
836
			"bar": this.s.dt.oScroll.iBarWidth
837
		};
838
 
839
		if ( nTable.offsetWidth > nTableScrollBody.clientWidth )
840
		{
841
			out.x = true;
842
		}
843
 
844
		if ( nTable.offsetHeight > nTableScrollBody.clientHeight )
845
		{
846
			out.y = true;
847
		}
848
 
849
		return out;
850
	},
851
 
852
 
853
	/**
854
	 * Clone and position the fixed columns
855
	 *  @returns {void}
856
	 *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
857
	 *  @private
858
	 */
859
	"_fnDraw": function ( bAll )
860
	{
861
		this._fnGridLayout();
862
		this._fnCloneLeft( bAll );
863
		this._fnCloneRight( bAll );
864
 
865
		/* Draw callback function */
866
		if ( this.s.fnDrawCallback !== null )
867
		{
868
			this.s.fnDrawCallback.call( this, this.dom.clone.left, this.dom.clone.right );
869
		}
870
 
871
		/* Event triggering */
872
		$(this).trigger( 'draw.dtfc', {
873
			"leftClone": this.dom.clone.left,
874
			"rightClone": this.dom.clone.right
875
		} );
876
	},
877
 
878
 
879
	/**
880
	 * Clone the right columns
881
	 *  @returns {void}
882
	 *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
883
	 *  @private
884
	 */
885
	"_fnCloneRight": function ( bAll )
886
	{
887
		if ( this.s.iRightColumns <= 0 ) {
888
			return;
889
		}
890
 
891
		var that = this,
892
			i, jq,
893
			aiColumns = [];
894
 
895
		for ( i=this.s.iTableColumns-this.s.iRightColumns ; i<this.s.iTableColumns ; i++ ) {
896
			if ( this.s.dt.aoColumns[i].bVisible ) {
897
				aiColumns.push( i );
898
			}
899
		}
900
 
901
		this._fnClone( this.dom.clone.right, this.dom.grid.right, aiColumns, bAll );
902
	},
903
 
904
 
905
	/**
906
	 * Clone the left columns
907
	 *  @returns {void}
908
	 *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
909
	 *  @private
910
	 */
911
	"_fnCloneLeft": function ( bAll )
912
	{
913
		if ( this.s.iLeftColumns <= 0 ) {
914
			return;
915
		}
916
 
917
		var that = this,
918
			i, jq,
919
			aiColumns = [];
920
 
921
		for ( i=0 ; i<this.s.iLeftColumns ; i++ ) {
922
			if ( this.s.dt.aoColumns[i].bVisible ) {
923
				aiColumns.push( i );
924
			}
925
		}
926
 
927
		this._fnClone( this.dom.clone.left, this.dom.grid.left, aiColumns, bAll );
928
	},
929
 
930
 
931
	/**
932
	 * Make a copy of the layout object for a header or footer element from DataTables. Note that
933
	 * this method will clone the nodes in the layout object.
934
	 *  @returns {Array} Copy of the layout array
935
	 *  @param   {Object} aoOriginal Layout array from DataTables (aoHeader or aoFooter)
936
	 *  @param   {Object} aiColumns Columns to copy
937
	 *  @param   {boolean} events Copy cell events or not
938
	 *  @private
939
	 */
940
	"_fnCopyLayout": function ( aoOriginal, aiColumns, events )
941
	{
942
		var aReturn = [];
943
		var aClones = [];
944
		var aCloned = [];
945
 
946
		for ( var i=0, iLen=aoOriginal.length ; i<iLen ; i++ )
947
		{
948
			var aRow = [];
949
			aRow.nTr = $(aoOriginal[i].nTr).clone(events, false)[0];
950
 
951
			for ( var j=0, jLen=this.s.iTableColumns ; j<jLen ; j++ )
952
			{
953
				if ( $.inArray( j, aiColumns ) === -1 )
954
				{
955
					continue;
956
				}
957
 
958
				var iCloned = $.inArray( aoOriginal[i][j].cell, aCloned );
959
				if ( iCloned === -1 )
960
				{
961
					var nClone = $(aoOriginal[i][j].cell).clone(events, false)[0];
962
					aClones.push( nClone );
963
					aCloned.push( aoOriginal[i][j].cell );
964
 
965
					aRow.push( {
966
						"cell": nClone,
967
						"unique": aoOriginal[i][j].unique
968
					} );
969
				}
970
				else
971
				{
972
					aRow.push( {
973
						"cell": aClones[ iCloned ],
974
						"unique": aoOriginal[i][j].unique
975
					} );
976
				}
977
			}
978
 
979
			aReturn.push( aRow );
980
		}
981
 
982
		return aReturn;
983
	},
984
 
985
 
986
	/**
987
	 * Clone the DataTable nodes and place them in the DOM (sized correctly)
988
	 *  @returns {void}
989
	 *  @param   {Object} oClone Object containing the header, footer and body cloned DOM elements
990
	 *  @param   {Object} oGrid Grid object containing the display grid elements for the cloned
991
	 *                    column (left or right)
992
	 *  @param   {Array} aiColumns Column indexes which should be operated on from the DataTable
993
	 *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
994
	 *  @private
995
	 */
996
	"_fnClone": function ( oClone, oGrid, aiColumns, bAll )
997
	{
998
		var that = this,
999
			i, iLen, j, jLen, jq, nTarget, iColumn, nClone, iIndex, aoCloneLayout,
1000
			jqCloneThead, aoFixedHeader,
1001
			dt = this.s.dt;
1002
 
1003
		/*
1004
		 * Header
1005
		 */
1006
		if ( bAll )
1007
		{
1008
			$(oClone.header).remove();
1009
 
1010
			oClone.header = $(this.dom.header).clone(true, false)[0];
1011
			oClone.header.className += " DTFC_Cloned";
1012
			oClone.header.style.width = "100%";
1013
			oGrid.head.appendChild( oClone.header );
1014
 
1015
			/* Copy the DataTables layout cache for the header for our floating column */
1016
			aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, true );
1017
			jqCloneThead = $('>thead', oClone.header);
1018
			jqCloneThead.empty();
1019
 
1020
			/* Add the created cloned TR elements to the table */
1021
			for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
1022
			{
1023
				jqCloneThead[0].appendChild( aoCloneLayout[i].nTr );
1024
			}
1025
 
1026
			/* Use the handy _fnDrawHead function in DataTables to do the rowspan/colspan
1027
			 * calculations for us
1028
			 */
1029
			dt.oApi._fnDrawHead( dt, aoCloneLayout, true );
1030
		}
1031
		else
1032
		{
1033
			/* To ensure that we copy cell classes exactly, regardless of colspan, multiple rows
1034
			 * etc, we make a copy of the header from the DataTable again, but don't insert the
1035
			 * cloned cells, just copy the classes across. To get the matching layout for the
1036
			 * fixed component, we use the DataTables _fnDetectHeader method, allowing 1:1 mapping
1037
			 */
1038
			aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, false );
1039
			aoFixedHeader=[];
1040
 
1041
			dt.oApi._fnDetectHeader( aoFixedHeader, $('>thead', oClone.header)[0] );
1042
 
1043
			for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
1044
			{
1045
				for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
1046
				{
1047
					aoFixedHeader[i][j].cell.className = aoCloneLayout[i][j].cell.className;
1048
 
1049
					// If jQuery UI theming is used we need to copy those elements as well
1050
					$('span.DataTables_sort_icon', aoFixedHeader[i][j].cell).each( function () {
1051
						this.className = $('span.DataTables_sort_icon', aoCloneLayout[i][j].cell)[0].className;
1052
					} );
1053
				}
1054
			}
1055
		}
1056
		this._fnEqualiseHeights( 'thead', this.dom.header, oClone.header );
1057
 
1058
		/*
1059
		 * Body
1060
		 */
1061
		if ( this.s.sHeightMatch == 'auto' )
1062
		{
1063
			/* Remove any heights which have been applied already and let the browser figure it out */
1064
			$('>tbody>tr', that.dom.body).css('height', 'auto');
1065
		}
1066
 
1067
		if ( oClone.body !== null )
1068
		{
1069
			$(oClone.body).remove();
1070
			oClone.body = null;
1071
		}
1072
 
1073
		oClone.body = $(this.dom.body).clone(true)[0];
1074
		oClone.body.className += " DTFC_Cloned";
1075
		oClone.body.style.paddingBottom = dt.oScroll.iBarWidth+"px";
1076
		oClone.body.style.marginBottom = (dt.oScroll.iBarWidth*2)+"px"; /* For IE */
1077
		if ( oClone.body.getAttribute('id') !== null )
1078
		{
1079
			oClone.body.removeAttribute('id');
1080
		}
1081
 
1082
		$('>thead>tr', oClone.body).empty();
1083
		$('>tfoot', oClone.body).remove();
1084
 
1085
		var nBody = $('tbody', oClone.body)[0];
1086
		$(nBody).empty();
1087
		if ( dt.aiDisplay.length > 0 )
1088
		{
1089
			/* Copy the DataTables' header elements to force the column width in exactly the
1090
			 * same way that DataTables does it - have the header element, apply the width and
1091
			 * colapse it down
1092
			 */
1093
			var nInnerThead = $('>thead>tr', oClone.body)[0];
1094
			for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
1095
			{
1096
				iColumn = aiColumns[iIndex];
1097
 
1098
				nClone = $(dt.aoColumns[iColumn].nTh).clone(true)[0];
1099
				nClone.innerHTML = "";
1100
 
1101
				var oStyle = nClone.style;
1102
				oStyle.paddingTop = "0";
1103
				oStyle.paddingBottom = "0";
1104
				oStyle.borderTopWidth = "0";
1105
				oStyle.borderBottomWidth = "0";
1106
				oStyle.height = 0;
1107
				oStyle.width = that.s.aiInnerWidths[iColumn]+"px";
1108
 
1109
				nInnerThead.appendChild( nClone );
1110
			}
1111
 
1112
			/* Add in the tbody elements, cloning form the master table */
1113
			$('>tbody>tr', that.dom.body).each( function (z) {
1114
				var n = this.cloneNode(false);
1115
				n.removeAttribute('id');
1116
				var i = that.s.dt.oFeatures.bServerSide===false ?
1117
					that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z;
1118
				var aTds = that.s.dt.aoData[ i ].anCells || $(this).children('td, th');
1119
 
1120
				for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
1121
				{
1122
					iColumn = aiColumns[iIndex];
1123
 
1124
					if ( aTds.length > 0 )
1125
					{
1126
						nClone = $( aTds[iColumn] ).clone(true, true)[0];
1127
						n.appendChild( nClone );
1128
					}
1129
				}
1130
				nBody.appendChild( n );
1131
			} );
1132
		}
1133
		else
1134
		{
1135
			$('>tbody>tr', that.dom.body).each( function (z) {
1136
				nClone = this.cloneNode(true);
1137
				nClone.className += ' DTFC_NoData';
1138
				$('td', nClone).html('');
1139
				nBody.appendChild( nClone );
1140
			} );
1141
		}
1142
 
1143
		oClone.body.style.width = "100%";
1144
		oClone.body.style.margin = "0";
1145
		oClone.body.style.padding = "0";
1146
 
1147
		// Interop with Scroller - need to use a height forcing element in the
1148
		// scrolling area in the same way that Scroller does in the body scroll.
1149
		if ( dt.oScroller !== undefined )
1150
		{
1151
			var scrollerForcer = dt.oScroller.dom.force;
1152
 
1153
			if ( ! oGrid.forcer ) {
1154
				oGrid.forcer = scrollerForcer.cloneNode( true );
1155
				oGrid.liner.appendChild( oGrid.forcer );
1156
			}
1157
			else {
1158
				oGrid.forcer.style.height = scrollerForcer.style.height;
1159
			}
1160
		}
1161
 
1162
		oGrid.liner.appendChild( oClone.body );
1163
 
1164
		this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body );
1165
 
1166
		/*
1167
		 * Footer
1168
		 */
1169
		if ( dt.nTFoot !== null )
1170
		{
1171
			if ( bAll )
1172
			{
1173
				if ( oClone.footer !== null )
1174
				{
1175
					oClone.footer.parentNode.removeChild( oClone.footer );
1176
				}
1177
				oClone.footer = $(this.dom.footer).clone(true, true)[0];
1178
				oClone.footer.className += " DTFC_Cloned";
1179
				oClone.footer.style.width = "100%";
1180
				oGrid.foot.appendChild( oClone.footer );
1181
 
1182
				/* Copy the footer just like we do for the header */
1183
				aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, true );
1184
				var jqCloneTfoot = $('>tfoot', oClone.footer);
1185
				jqCloneTfoot.empty();
1186
 
1187
				for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
1188
				{
1189
					jqCloneTfoot[0].appendChild( aoCloneLayout[i].nTr );
1190
				}
1191
				dt.oApi._fnDrawHead( dt, aoCloneLayout, true );
1192
			}
1193
			else
1194
			{
1195
				aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, false );
1196
				var aoCurrFooter=[];
1197
 
1198
				dt.oApi._fnDetectHeader( aoCurrFooter, $('>tfoot', oClone.footer)[0] );
1199
 
1200
				for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
1201
				{
1202
					for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
1203
					{
1204
						aoCurrFooter[i][j].cell.className = aoCloneLayout[i][j].cell.className;
1205
					}
1206
				}
1207
			}
1208
			this._fnEqualiseHeights( 'tfoot', this.dom.footer, oClone.footer );
1209
		}
1210
 
1211
		/* Equalise the column widths between the header footer and body - body get's priority */
1212
		var anUnique = dt.oApi._fnGetUniqueThs( dt, $('>thead', oClone.header)[0] );
1213
		$(anUnique).each( function (i) {
1214
			iColumn = aiColumns[i];
1215
			this.style.width = that.s.aiInnerWidths[iColumn]+"px";
1216
		} );
1217
 
1218
		if ( that.s.dt.nTFoot !== null )
1219
		{
1220
			anUnique = dt.oApi._fnGetUniqueThs( dt, $('>tfoot', oClone.footer)[0] );
1221
			$(anUnique).each( function (i) {
1222
				iColumn = aiColumns[i];
1223
				this.style.width = that.s.aiInnerWidths[iColumn]+"px";
1224
			} );
1225
		}
1226
	},
1227
 
1228
 
1229
	/**
1230
	 * From a given table node (THEAD etc), get a list of TR direct child elements
1231
	 *  @param   {Node} nIn Table element to search for TR elements (THEAD, TBODY or TFOOT element)
1232
	 *  @returns {Array} List of TR elements found
1233
	 *  @private
1234
	 */
1235
	"_fnGetTrNodes": function ( nIn )
1236
	{
1237
		var aOut = [];
1238
		for ( var i=0, iLen=nIn.childNodes.length ; i<iLen ; i++ )
1239
		{
1240
			if ( nIn.childNodes[i].nodeName.toUpperCase() == "TR" )
1241
			{
1242
				aOut.push( nIn.childNodes[i] );
1243
			}
1244
		}
1245
		return aOut;
1246
	},
1247
 
1248
 
1249
	/**
1250
	 * Equalise the heights of the rows in a given table node in a cross browser way
1251
	 *  @returns {void}
1252
	 *  @param   {String} nodeName Node type - thead, tbody or tfoot
1253
	 *  @param   {Node} original Original node to take the heights from
1254
	 *  @param   {Node} clone Copy the heights to
1255
	 *  @private
1256
	 */
1257
	"_fnEqualiseHeights": function ( nodeName, original, clone )
1258
	{
1259
		if ( this.s.sHeightMatch == 'none' && nodeName !== 'thead' && nodeName !== 'tfoot' )
1260
		{
1261
			return;
1262
		}
1263
 
1264
		var that = this,
1265
			i, iLen, iHeight, iHeight2, iHeightOriginal, iHeightClone,
1266
			rootOriginal = original.getElementsByTagName(nodeName)[0],
1267
			rootClone    = clone.getElementsByTagName(nodeName)[0],
1268
			jqBoxHack    = $('>'+nodeName+'>tr:eq(0)', original).children(':first'),
1269
			iBoxHack     = jqBoxHack.outerHeight() - jqBoxHack.height(),
1270
			anOriginal   = this._fnGetTrNodes( rootOriginal ),
1271
			anClone      = this._fnGetTrNodes( rootClone ),
1272
			heights      = [];
1273
 
1274
		for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
1275
		{
1276
			iHeightOriginal = anOriginal[i].offsetHeight;
1277
			iHeightClone = anClone[i].offsetHeight;
1278
			iHeight = iHeightClone > iHeightOriginal ? iHeightClone : iHeightOriginal;
1279
 
1280
			if ( this.s.sHeightMatch == 'semiauto' )
1281
			{
1282
				anOriginal[i]._DTTC_iHeight = iHeight;
1283
			}
1284
 
1285
			heights.push( iHeight );
1286
		}
1287
 
1288
		for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
1289
		{
1290
			anClone[i].style.height = heights[i]+"px";
1291
			anOriginal[i].style.height = heights[i]+"px";
1292
		}
1293
	}
1294
};
1295
 
1296
 
1297
 
1298
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1299
 * Statics
1300
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1301
 
1302
/**
1303
 * FixedColumns default settings for initialisation
1304
 *  @name FixedColumns.defaults
1305
 *  @namespace
1306
 *  @static
1307
 */
1308
FixedColumns.defaults = /** @lends FixedColumns.defaults */{
1309
	/**
1310
	 * Number of left hand columns to fix in position
1311
	 *  @type     int
1312
	 *  @default  1
1313
	 *  @static
1314
	 *  @example
1315
	 *      var  = $('#example').dataTable( {
1316
	 *          "scrollX": "100%"
1317
	 *      } );
1318
	 *      new $.fn.dataTable.fixedColumns( table, {
1319
	 *          "leftColumns": 2
1320
	 *      } );
1321
	 */
1322
	"iLeftColumns": 1,
1323
 
1324
	/**
1325
	 * Number of right hand columns to fix in position
1326
	 *  @type     int
1327
	 *  @default  0
1328
	 *  @static
1329
	 *  @example
1330
	 *      var table = $('#example').dataTable( {
1331
	 *          "scrollX": "100%"
1332
	 *      } );
1333
	 *      new $.fn.dataTable.fixedColumns( table, {
1334
	 *          "rightColumns": 1
1335
	 *      } );
1336
	 */
1337
	"iRightColumns": 0,
1338
 
1339
	/**
1340
	 * Draw callback function which is called when FixedColumns has redrawn the fixed assets
1341
	 *  @type     function(object, object):void
1342
	 *  @default  null
1343
	 *  @static
1344
	 *  @example
1345
	 *      var table = $('#example').dataTable( {
1346
	 *          "scrollX": "100%"
1347
	 *      } );
1348
	 *      new $.fn.dataTable.fixedColumns( table, {
1349
	 *          "drawCallback": function () {
1350
	 *	            alert( "FixedColumns redraw" );
1351
	 *	        }
1352
	 *      } );
1353
	 */
1354
	"fnDrawCallback": null,
1355
 
1356
	/**
1357
	 * Height matching algorthim to use. This can be "none" which will result in no height
1358
	 * matching being applied by FixedColumns (height matching could be forced by CSS in this
1359
	 * case), "semiauto" whereby the height calculation will be performed once, and the result
1360
	 * cached to be used again (fnRecalculateHeight can be used to force recalculation), or
1361
	 * "auto" when height matching is performed on every draw (slowest but must accurate)
1362
	 *  @type     string
1363
	 *  @default  semiauto
1364
	 *  @static
1365
	 *  @example
1366
	 *      var table = $('#example').dataTable( {
1367
	 *          "scrollX": "100%"
1368
	 *      } );
1369
	 *      new $.fn.dataTable.fixedColumns( table, {
1370
	 *          "heightMatch": "auto"
1371
	 *      } );
1372
	 */
1373
	"sHeightMatch": "semiauto"
1374
};
1375
 
1376
 
1377
 
1378
 
1379
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1380
 * Constants
1381
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1382
 
1383
/**
1384
 * FixedColumns version
1385
 *  @name      FixedColumns.version
1386
 *  @type      String
1387
 *  @default   See code
1388
 *  @static
1389
 */
1390
FixedColumns.version = "3.1.0";
1391
 
1392
 
1393
 
1394
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1395
 * DataTables API integration
1396
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1397
 
1398
DataTable.Api.register( 'fixedColumns()', function () {
1399
	return this;
1400
} );
1401
 
1402
DataTable.Api.register( 'fixedColumns().update()', function () {
1403
	return this.iterator( 'table', function ( ctx ) {
1404
		if ( ctx._oFixedColumns ) {
1405
			ctx._oFixedColumns.fnUpdate();
1406
		}
1407
	} );
1408
} );
1409
 
1410
DataTable.Api.register( 'fixedColumns().relayout()', function () {
1411
	return this.iterator( 'table', function ( ctx ) {
1412
		if ( ctx._oFixedColumns ) {
1413
			ctx._oFixedColumns.fnRedrawLayout();
1414
		}
1415
	} );
1416
} );
1417
 
1418
DataTable.Api.register( 'rows().recalcHeight()', function () {
1419
	return this.iterator( 'row', function ( ctx, idx ) {
1420
		if ( ctx._oFixedColumns ) {
1421
			ctx._oFixedColumns.fnRecalculateHeight( this.row(idx).node() );
1422
		}
1423
	} );
1424
} );
1425
 
1426
DataTable.Api.register( 'fixedColumns().rowIndex()', function ( row ) {
1427
	row = $(row);
1428
 
1429
	return row.parents('.DTFC_Cloned').length ?
1430
		this.rows( { page: 'current' } ).indexes()[ row.index() ] :
1431
		this.row( row ).index();
1432
} );
1433
 
1434
DataTable.Api.register( 'fixedColumns().cellIndex()', function ( cell ) {
1435
	cell = $(cell);
1436
 
1437
	if ( cell.parents('.DTFC_Cloned').length ) {
1438
		var rowClonedIdx = cell.parent().index();
1439
		var rowIdx = this.rows( { page: 'current' } ).indexes()[ rowClonedIdx ];
1440
		var columnIdx;
1441
 
1442
		if ( cell.parents('.DTFC_LeftWrapper').length ) {
1443
			columnIdx = cell.index();
1444
		}
1445
		else {
1446
			var columns = this.columns().flatten().length;
1447
			columnIdx = columns - this.context[0]._oFixedColumns.s.iRightColumns + cell.index();
1448
		}
1449
 
1450
		return {
1451
			row: rowIdx,
1452
			column: this.column.index( 'toData', columnIdx ),
1453
			columnVisible: columnIdx
1454
		};
1455
	}
1456
	else {
1457
		return this.cell( cell ).index();
1458
	}
1459
} );
1460
 
1461
 
1462
 
1463
 
1464
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1465
 * Initialisation
1466
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1467
 
1468
// Attach a listener to the document which listens for DataTables initialisation
1469
// events so we can automatically initialise
1470
$(document).on( 'init.dt.fixedColumns', function (e, settings) {
1471
	if ( e.namespace !== 'dt' ) {
1472
		return;
1473
	}
1474
 
1475
	var init = settings.oInit.fixedColumns;
1476
	var defaults = DataTable.defaults.fixedColumns;
1477
 
1478
	if ( init || defaults ) {
1479
		var opts = $.extend( {}, init, defaults );
1480
 
1481
		if ( init !== false ) {
1482
			new FixedColumns( settings, opts );
1483
		}
1484
	}
1485
} );
1486
 
1487
 
1488
 
1489
// Make FixedColumns accessible from the DataTables instance
1490
$.fn.dataTable.FixedColumns = FixedColumns;
1491
$.fn.DataTable.FixedColumns = FixedColumns;
1492
 
1493
return FixedColumns;
1494
}; // /factory
1495
 
1496
 
1497
// Define as an AMD module if possible
1498
if ( typeof define === 'function' && define.amd ) {
1499
	define( ['jquery', 'datatables'], factory );
1500
}
1501
else if ( typeof exports === 'object' ) {
1502
    // Node/CommonJS
1503
    factory( require('jquery'), require('datatables') );
1504
}
1505
else if ( jQuery && !jQuery.fn.dataTable.FixedColumns ) {
1506
	// Otherwise simply initialise as normal, stopping multiple evaluation
1507
	factory( jQuery, jQuery.fn.dataTable );
1508
}
1509
 
1510
 
1511
})(window, document);
1512