0e0ec2749dfb8568a5304cf059c5bd0b14b9d2d0
[clamp.git] / src / main / resources / META-INF / resources / designer / lib / dataTables.fixedColumns.js
1 /*! FixedColumns 3.0.3
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.0.3
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         {
67                 alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
68                 return;
69         }
70
71         if ( typeof init == 'undefined' )
72         {
73                 init = {};
74         }
75
76         // Use the DataTables Hungarian notation mapping method, if it exists to
77         // provide forwards compatibility for camel case variables
78         var camelToHungarian = $.fn.dataTable.camelToHungarian;
79         if ( camelToHungarian ) {
80                 camelToHungarian( FixedColumns.defaults, FixedColumns.defaults, true );
81                 camelToHungarian( FixedColumns.defaults, init );
82         }
83
84         // v1.10 allows the settings object to be got form a number of sources
85         var dtSettings = $.fn.dataTable.Api ?
86                 new $.fn.dataTable.Api( dt ).settings()[0] :
87                 dt.fnSettings();
88
89         /**
90          * Settings object which contains customisable information for FixedColumns instance
91          * @namespace
92          * @extends FixedColumns.defaults
93          * @private
94          */
95         this.s = {
96                 /**
97                  * DataTables settings objects
98                  *  @type     object
99                  *  @default  Obtained from DataTables instance
100                  */
101                 "dt": dtSettings,
102
103                 /**
104                  * Number of columns in the DataTable - stored for quick access
105                  *  @type     int
106                  *  @default  Obtained from DataTables instance
107                  */
108                 "iTableColumns": dtSettings.aoColumns.length,
109
110                 /**
111                  * Original outer widths of the columns as rendered by DataTables - used to calculate
112                  * the FixedColumns grid bounding box
113                  *  @type     array.<int>
114                  *  @default  []
115                  */
116                 "aiOuterWidths": [],
117
118                 /**
119                  * Original inner widths of the columns as rendered by DataTables - used to apply widths
120                  * to the columns
121                  *  @type     array.<int>
122                  *  @default  []
123                  */
124                 "aiInnerWidths": []
125         };
126
127
128         /**
129          * DOM elements used by the class instance
130          * @namespace
131          * @private
132          *
133          */
134         this.dom = {
135                 /**
136                  * DataTables scrolling element
137                  *  @type     node
138                  *  @default  null
139                  */
140                 "scroller": null,
141
142                 /**
143                  * DataTables header table
144                  *  @type     node
145                  *  @default  null
146                  */
147                 "header": null,
148
149                 /**
150                  * DataTables body table
151                  *  @type     node
152                  *  @default  null
153                  */
154                 "body": null,
155
156                 /**
157                  * DataTables footer table
158                  *  @type     node
159                  *  @default  null
160                  */
161                 "footer": null,
162
163                 /**
164                  * Display grid elements
165                  * @namespace
166                  */
167                 "grid": {
168                         /**
169                          * Grid wrapper. This is the container element for the 3x3 grid
170                          *  @type     node
171                          *  @default  null
172                          */
173                         "wrapper": null,
174
175                         /**
176                          * DataTables scrolling element. This element is the DataTables
177                          * component in the display grid (making up the main table - i.e.
178                          * not the fixed columns).
179                          *  @type     node
180                          *  @default  null
181                          */
182                         "dt": null,
183
184                         /**
185                          * Left fixed column grid components
186                          * @namespace
187                          */
188                         "left": {
189                                 "wrapper": null,
190                                 "head": null,
191                                 "body": null,
192                                 "foot": null
193                         },
194
195                         /**
196                          * Right fixed column grid components
197                          * @namespace
198                          */
199                         "right": {
200                                 "wrapper": null,
201                                 "head": null,
202                                 "body": null,
203                                 "foot": null
204                         }
205                 },
206
207                 /**
208                  * Cloned table nodes
209                  * @namespace
210                  */
211                 "clone": {
212                         /**
213                          * Left column cloned table nodes
214                          * @namespace
215                          */
216                         "left": {
217                                 /**
218                                  * Cloned header table
219                                  *  @type     node
220                                  *  @default  null
221                                  */
222                                 "header": null,
223
224                                 /**
225                                  * Cloned body table
226                                  *  @type     node
227                                  *  @default  null
228                                  */
229                                 "body": null,
230
231                                 /**
232                                  * Cloned footer table
233                                  *  @type     node
234                                  *  @default  null
235                                  */
236                                 "footer": null
237                         },
238
239                         /**
240                          * Right column cloned table nodes
241                          * @namespace
242                          */
243                         "right": {
244                                 /**
245                                  * Cloned header table
246                                  *  @type     node
247                                  *  @default  null
248                                  */
249                                 "header": null,
250
251                                 /**
252                                  * Cloned body table
253                                  *  @type     node
254                                  *  @default  null
255                                  */
256                                 "body": null,
257
258                                 /**
259                                  * Cloned footer table
260                                  *  @type     node
261                                  *  @default  null
262                                  */
263                                 "footer": null
264                         }
265                 }
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 () {
478                                 if ( mouseController === 'main' ) {
479                                         if ( that.s.iLeftColumns > 0 ) {
480                                                 that.dom.grid.left.liner.scrollTop = that.dom.scroller.scrollTop;
481                                         }
482                                         if ( that.s.iRightColumns > 0 ) {
483                                                 that.dom.grid.right.liner.scrollTop = that.dom.scroller.scrollTop;
484                                         }
485                                 }
486                         } );
487
488                 var wheelType = 'onwheel' in document.createElement('div') ?
489                         'wheel.DTFC' :
490                         'mousewheel.DTFC';
491
492                 if ( that.s.iLeftColumns > 0 ) {
493                         // When scrolling the left column, scroll the body and right column
494                         $(that.dom.grid.left.liner)
495                                 .on( 'mouseover.DTFC touchstart.DTFC', function () {
496                                         mouseController = 'left';
497                                 } )
498                                 .on( 'scroll.DTFC', function () {
499                                         if ( mouseController === 'left' ) {
500                                                 that.dom.scroller.scrollTop = that.dom.grid.left.liner.scrollTop;
501                                                 if ( that.s.iRightColumns > 0 ) {
502                                                         that.dom.grid.right.liner.scrollTop = that.dom.grid.left.liner.scrollTop;
503                                                 }
504                                         }
505                                 } )
506                                 .on( wheelType, function(e) { // xxx update the destroy as well
507                                         // Pass horizontal scrolling through
508                                         var xDelta = e.type === 'wheel' ?
509                                                 -e.originalEvent.deltaX :
510                                                 e.originalEvent.wheelDeltaX;
511                                         that.dom.scroller.scrollLeft -= xDelta;
512                                 } );
513                 }
514
515                 if ( that.s.iRightColumns > 0 ) {
516                         // When scrolling the right column, scroll the body and the left column
517                         $(that.dom.grid.right.liner)
518                                 .on( 'mouseover.DTFC touchstart.DTFC', function () {
519                                         mouseController = 'right';
520                                 } )
521                                 .on( 'scroll.DTFC', function () {
522                                         if ( mouseController === 'right' ) {
523                                                 that.dom.scroller.scrollTop = that.dom.grid.right.liner.scrollTop;
524                                                 if ( that.s.iLeftColumns > 0 ) {
525                                                         that.dom.grid.left.liner.scrollTop = that.dom.grid.right.liner.scrollTop;
526                                                 }
527                                         }
528                                 } )
529                                 .on( wheelType, function(e) {
530                                         // Pass horizontal scrolling through
531                                         var xDelta = e.type === 'wheel' ?
532                                                 -e.originalEvent.deltaX :
533                                                 e.originalEvent.wheelDeltaX;
534                                         that.dom.scroller.scrollLeft -= xDelta;
535                                 } );
536                 }
537
538                 $(window).on( 'resize.DTFC', function () {
539                         that._fnGridLayout.call( that );
540                 } );
541
542                 var bFirstDraw = true;
543                 var jqTable = $(this.s.dt.nTable);
544
545                 jqTable
546                         .on( 'draw.dt.DTFC', function () {
547                                 that._fnDraw.call( that, bFirstDraw );
548                                 bFirstDraw = false;
549                         } )
550                         .on( 'column-sizing.dt.DTFC', function () {
551                                 that._fnColCalc();
552                                 that._fnGridLayout( that );
553                         } )
554                         .on( 'column-visibility.dt.DTFC', function () {
555                                 that._fnColCalc();
556                                 that._fnGridLayout( that );
557                                 that._fnDraw( true );
558                         } )
559                         .on( 'destroy.dt.DTFC', function () {
560                                 jqTable.off( 'column-sizing.dt.DTFC destroy.dt.DTFC draw.dt.DTFC' );
561
562                                 $(that.dom.scroller).off( 'scroll.DTFC mouseover.DTFC' );
563                                 $(window).off( 'resize.DTFC' );
564
565                                 $(that.dom.grid.left.liner).off( 'scroll.DTFC mouseover.DTFC '+wheelType );
566                                 $(that.dom.grid.left.wrapper).remove();
567
568                                 $(that.dom.grid.right.liner).off( 'scroll.DTFC mouseover.DTFC '+wheelType );
569                                 $(that.dom.grid.right.wrapper).remove();
570                         } );
571
572                 /* Get things right to start with - note that due to adjusting the columns, there must be
573                  * another redraw of the main table. It doesn't need to be a full redraw however.
574                  */
575                 this._fnGridLayout();
576                 this.s.dt.oInstance.fnDraw(false);
577         },
578
579
580         /**
581          * Calculate the column widths for the grid layout
582          *  @returns {void}
583          *  @private
584          */
585         "_fnColCalc": function ()
586         {
587                 var that = this;
588                 var iLeftWidth = 0;
589                 var iRightWidth = 0;
590
591                 this.s.aiInnerWidths = [];
592                 this.s.aiOuterWidths = [];
593
594                 $.each( this.s.dt.aoColumns, function (i, col) {
595                         var th = $(col.nTh);
596                         var border;
597
598                         if ( ! th.filter(':visible').length ) {
599                                 that.s.aiInnerWidths.push( 0 );
600                                 that.s.aiOuterWidths.push( 0 );
601                         }
602                         else
603                         {
604                                 // Inner width is used to assign widths to cells
605                                 // Outer width is used to calculate the container
606                                 var iWidth = th.outerWidth();
607
608                                 // When working with the left most-cell, need to add on the
609                                 // table's border to the outerWidth, since we need to take
610                                 // account of it, but it isn't in any cell
611                                 if ( that.s.aiOuterWidths.length === 0 ) {
612                                         border = $(that.s.dt.nTable).css('border-left-width');
613                                         iWidth += typeof border === 'string' ? 1 : parseInt( border, 10 );
614                                 }
615
616                                 // Likewise with the final column on the right
617                                 if ( that.s.aiOuterWidths.length === that.s.dt.aoColumns.length-1 ) {
618                                         border = $(that.s.dt.nTable).css('border-right-width');
619                                         iWidth += typeof border === 'string' ? 1 : parseInt( border, 10 );
620                                 }
621
622                                 that.s.aiOuterWidths.push( iWidth );
623                                 that.s.aiInnerWidths.push( th.width() );
624
625                                 if ( i < that.s.iLeftColumns )
626                                 {
627                                         iLeftWidth += iWidth;
628                                 }
629
630                                 if ( that.s.iTableColumns-that.s.iRightColumns <= i )
631                                 {
632                                         iRightWidth += iWidth;
633                                 }
634                         }
635                 } );
636
637                 this.s.iLeftWidth = iLeftWidth;
638                 this.s.iRightWidth = iRightWidth;
639         },
640
641
642         /**
643          * Set up the DOM for the fixed column. The way the layout works is to create a 1x3 grid
644          * for the left column, the DataTable (for which we just reuse the scrolling element DataTable
645          * puts into the DOM) and the right column. In each of he two fixed column elements there is a
646          * grouping wrapper element and then a head, body and footer wrapper. In each of these we then
647          * place the cloned header, body or footer tables. This effectively gives as 3x3 grid structure.
648          *  @returns {void}
649          *  @private
650          */
651         "_fnGridSetup": function ()
652         {
653                 var that = this;
654                 var oOverflow = this._fnDTOverflow();
655                 var block;
656
657                 this.dom.body = this.s.dt.nTable;
658                 this.dom.header = this.s.dt.nTHead.parentNode;
659                 this.dom.header.parentNode.parentNode.style.position = "relative";
660
661                 var nSWrapper =
662                         $('<div class="DTFC_ScrollWrapper" style="position:relative; clear:both;">'+
663                                 '<div class="DTFC_LeftWrapper" style="position:absolute; top:0; left:0;">'+
664                                         '<div class="DTFC_LeftHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
665                                         '<div class="DTFC_LeftBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;">'+
666                                                 '<div class="DTFC_LeftBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+
667                                         '</div>'+
668                                         '<div class="DTFC_LeftFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
669                                 '</div>'+
670                                 '<div class="DTFC_RightWrapper" style="position:absolute; top:0; left:0;">'+
671                                         '<div class="DTFC_RightHeadWrapper" style="position:relative; top:0; left:0;">'+
672                                                 '<div class="DTFC_RightHeadBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+
673                                         '</div>'+
674                                         '<div class="DTFC_RightBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;">'+
675                                                 '<div class="DTFC_RightBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+
676                                         '</div>'+
677                                         '<div class="DTFC_RightFootWrapper" style="position:relative; top:0; left:0;">'+
678                                                 '<div class="DTFC_RightFootBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+
679                                         '</div>'+
680                                 '</div>'+
681                         '</div>')[0];
682                 var nLeft = nSWrapper.childNodes[0];
683                 var nRight = nSWrapper.childNodes[1];
684
685                 this.dom.grid.dt.parentNode.insertBefore( nSWrapper, this.dom.grid.dt );
686                 nSWrapper.appendChild( this.dom.grid.dt );
687
688                 this.dom.grid.wrapper = nSWrapper;
689
690                 if ( this.s.iLeftColumns > 0 )
691                 {
692                         this.dom.grid.left.wrapper = nLeft;
693                         this.dom.grid.left.head = nLeft.childNodes[0];
694                         this.dom.grid.left.body = nLeft.childNodes[1];
695                         this.dom.grid.left.liner = $('div.DTFC_LeftBodyLiner', nSWrapper)[0];
696
697                         nSWrapper.appendChild( nLeft );
698                 }
699
700                 if ( this.s.iRightColumns > 0 )
701                 {
702                         this.dom.grid.right.wrapper = nRight;
703                         this.dom.grid.right.head = nRight.childNodes[0];
704                         this.dom.grid.right.body = nRight.childNodes[1];
705                         this.dom.grid.right.liner = $('div.DTFC_RightBodyLiner', nSWrapper)[0];
706
707                         block = $('div.DTFC_RightHeadBlocker', nSWrapper)[0];
708                         block.style.width = oOverflow.bar+"px";
709                         block.style.right = -oOverflow.bar+"px";
710                         this.dom.grid.right.headBlock = block;
711
712                         block = $('div.DTFC_RightFootBlocker', nSWrapper)[0];
713                         block.style.width = oOverflow.bar+"px";
714                         block.style.right = -oOverflow.bar+"px";
715                         this.dom.grid.right.footBlock = block;
716
717                         nSWrapper.appendChild( nRight );
718                 }
719
720                 if ( this.s.dt.nTFoot )
721                 {
722                         this.dom.footer = this.s.dt.nTFoot.parentNode;
723                         if ( this.s.iLeftColumns > 0 )
724                         {
725                                 this.dom.grid.left.foot = nLeft.childNodes[2];
726                         }
727                         if ( this.s.iRightColumns > 0 )
728                         {
729                                 this.dom.grid.right.foot = nRight.childNodes[2];
730                         }
731                 }
732         },
733
734
735         /**
736          * Style and position the grid used for the FixedColumns layout
737          *  @returns {void}
738          *  @private
739          */
740         "_fnGridLayout": function ()
741         {
742                 var oGrid = this.dom.grid;
743                 var iWidth = $(oGrid.wrapper).width();
744                 var iBodyHeight = $(this.s.dt.nTable.parentNode).outerHeight();
745                 var iFullHeight = $(this.s.dt.nTable.parentNode.parentNode).outerHeight();
746                 var oOverflow = this._fnDTOverflow();
747                 var
748                         iLeftWidth = this.s.iLeftWidth,
749                         iRightWidth = this.s.iRightWidth,
750                         iRight;
751                 var scrollbarAdjust = function ( node, width ) {
752                         if ( ! oOverflow.bar ) {
753                                 // If there is no scrollbar (Macs) we need to hide the auto scrollbar
754                                 node.style.width = (width+20)+"px";
755                                 node.style.paddingRight = "20px";
756                                 node.style.boxSizing = "border-box";
757                         }
758                         else {
759                                 // Otherwise just overflow by the scrollbar
760                                 node.style.width = (width+oOverflow.bar)+"px";
761                         }
762                 };
763
764                 // When x scrolling - don't paint the fixed columns over the x scrollbar
765                 if ( oOverflow.x )
766                 {
767                         iBodyHeight -= oOverflow.bar;
768                 }
769
770                 oGrid.wrapper.style.height = iFullHeight+"px";
771
772                 if ( this.s.iLeftColumns > 0 )
773                 {
774                         oGrid.left.wrapper.style.width = iLeftWidth+"px";
775                         oGrid.left.wrapper.style.height = "1px";
776                         oGrid.left.body.style.height = iBodyHeight+"px";
777                         if ( oGrid.left.foot ) {
778                                 oGrid.left.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px"; // shift footer for scrollbar
779                         }
780
781                         scrollbarAdjust( oGrid.left.liner, iLeftWidth );
782                         oGrid.left.liner.style.height = iBodyHeight+"px";
783                 }
784
785                 if ( this.s.iRightColumns > 0 )
786                 {
787                         iRight = iWidth - iRightWidth;
788                         if ( oOverflow.y )
789                         {
790                                 iRight -= oOverflow.bar;
791                         }
792
793                         oGrid.right.wrapper.style.width = iRightWidth+"px";
794                         oGrid.right.wrapper.style.left = iRight+"px";
795                         oGrid.right.wrapper.style.height = "1px";
796                         oGrid.right.body.style.height = iBodyHeight+"px";
797                         if ( oGrid.right.foot ) {
798                                 oGrid.right.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px";
799                         }
800
801                         scrollbarAdjust( oGrid.right.liner, iRightWidth );
802                         oGrid.right.liner.style.height = iBodyHeight+"px";
803
804                         oGrid.right.headBlock.style.display = oOverflow.y ? 'block' : 'none';
805                         oGrid.right.footBlock.style.display = oOverflow.y ? 'block' : 'none';
806                 }
807         },
808
809
810         /**
811          * Get information about the DataTable's scrolling state - specifically if the table is scrolling
812          * on either the x or y axis, and also the scrollbar width.
813          *  @returns {object} Information about the DataTables scrolling state with the properties:
814          *    'x', 'y' and 'bar'
815          *  @private
816          */
817         "_fnDTOverflow": function ()
818         {
819                 var nTable = this.s.dt.nTable;
820                 var nTableScrollBody = nTable.parentNode;
821                 var out = {
822                         "x": false,
823                         "y": false,
824                         "bar": this.s.dt.oScroll.iBarWidth
825                 };
826
827                 if ( nTable.offsetWidth > nTableScrollBody.clientWidth )
828                 {
829                         out.x = true;
830                 }
831
832                 if ( nTable.offsetHeight > nTableScrollBody.clientHeight )
833                 {
834                         out.y = true;
835                 }
836
837                 return out;
838         },
839
840
841         /**
842          * Clone and position the fixed columns
843          *  @returns {void}
844          *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
845          *  @private
846          */
847         "_fnDraw": function ( bAll )
848         {
849                 this._fnGridLayout();
850                 this._fnCloneLeft( bAll );
851                 this._fnCloneRight( bAll );
852
853                 /* Draw callback function */
854                 if ( this.s.fnDrawCallback !== null )
855                 {
856                         this.s.fnDrawCallback.call( this, this.dom.clone.left, this.dom.clone.right );
857                 }
858
859                 /* Event triggering */
860                 $(this).trigger( 'draw.dtfc', {
861                         "leftClone": this.dom.clone.left,
862                         "rightClone": this.dom.clone.right
863                 } );
864         },
865
866
867         /**
868          * Clone the right columns
869          *  @returns {void}
870          *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
871          *  @private
872          */
873         "_fnCloneRight": function ( bAll )
874         {
875                 if ( this.s.iRightColumns <= 0 ) {
876                         return;
877                 }
878
879                 var that = this,
880                         i, jq,
881                         aiColumns = [];
882
883                 for ( i=this.s.iTableColumns-this.s.iRightColumns ; i<this.s.iTableColumns ; i++ ) {
884                         if ( this.s.dt.aoColumns[i].bVisible ) {
885                                 aiColumns.push( i );
886                         }
887                 }
888
889                 this._fnClone( this.dom.clone.right, this.dom.grid.right, aiColumns, bAll );
890         },
891
892
893         /**
894          * Clone the left columns
895          *  @returns {void}
896          *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
897          *  @private
898          */
899         "_fnCloneLeft": function ( bAll )
900         {
901                 if ( this.s.iLeftColumns <= 0 ) {
902                         return;
903                 }
904
905                 var that = this,
906                         i, jq,
907                         aiColumns = [];
908
909                 for ( i=0 ; i<this.s.iLeftColumns ; i++ ) {
910                         if ( this.s.dt.aoColumns[i].bVisible ) {
911                                 aiColumns.push( i );
912                         }
913                 }
914
915                 this._fnClone( this.dom.clone.left, this.dom.grid.left, aiColumns, bAll );
916         },
917
918
919         /**
920          * Make a copy of the layout object for a header or footer element from DataTables. Note that
921          * this method will clone the nodes in the layout object.
922          *  @returns {Array} Copy of the layout array
923          *  @param   {Object} aoOriginal Layout array from DataTables (aoHeader or aoFooter)
924          *  @param   {Object} aiColumns Columns to copy
925          *  @private
926          */
927         "_fnCopyLayout": function ( aoOriginal, aiColumns )
928         {
929                 var aReturn = [];
930                 var aClones = [];
931                 var aCloned = [];
932
933                 for ( var i=0, iLen=aoOriginal.length ; i<iLen ; i++ )
934                 {
935                         var aRow = [];
936                         aRow.nTr = $(aoOriginal[i].nTr).clone(true, true)[0];
937
938                         for ( var j=0, jLen=this.s.iTableColumns ; j<jLen ; j++ )
939                         {
940                                 if ( $.inArray( j, aiColumns ) === -1 )
941                                 {
942                                         continue;
943                                 }
944
945                                 var iCloned = $.inArray( aoOriginal[i][j].cell, aCloned );
946                                 if ( iCloned === -1 )
947                                 {
948                                         var nClone = $(aoOriginal[i][j].cell).clone(true, true)[0];
949                                         aClones.push( nClone );
950                                         aCloned.push( aoOriginal[i][j].cell );
951
952                                         aRow.push( {
953                                                 "cell": nClone,
954                                                 "unique": aoOriginal[i][j].unique
955                                         } );
956                                 }
957                                 else
958                                 {
959                                         aRow.push( {
960                                                 "cell": aClones[ iCloned ],
961                                                 "unique": aoOriginal[i][j].unique
962                                         } );
963                                 }
964                         }
965
966                         aReturn.push( aRow );
967                 }
968
969                 return aReturn;
970         },
971
972
973         /**
974          * Clone the DataTable nodes and place them in the DOM (sized correctly)
975          *  @returns {void}
976          *  @param   {Object} oClone Object containing the header, footer and body cloned DOM elements
977          *  @param   {Object} oGrid Grid object containing the display grid elements for the cloned
978          *                    column (left or right)
979          *  @param   {Array} aiColumns Column indexes which should be operated on from the DataTable
980          *  @param   {Boolean} bAll Indicate if the header and footer should be updated as well (true)
981          *  @private
982          */
983         "_fnClone": function ( oClone, oGrid, aiColumns, bAll )
984         {
985                 var that = this,
986                         i, iLen, j, jLen, jq, nTarget, iColumn, nClone, iIndex, aoCloneLayout,
987                         jqCloneThead, aoFixedHeader;
988
989                 /*
990                  * Header
991                  */
992                 if ( bAll )
993                 {
994                         if ( oClone.header !== null )
995                         {
996                                 oClone.header.parentNode.removeChild( oClone.header );
997                         }
998                         oClone.header = $(this.dom.header).clone(true, true)[0];
999                         oClone.header.className += " DTFC_Cloned";
1000                         oClone.header.style.width = "100%";
1001                         oGrid.head.appendChild( oClone.header );
1002
1003                         /* Copy the DataTables layout cache for the header for our floating column */
1004                         aoCloneLayout = this._fnCopyLayout( this.s.dt.aoHeader, aiColumns );
1005                         jqCloneThead = $('>thead', oClone.header);
1006                         jqCloneThead.empty();
1007
1008                         /* Add the created cloned TR elements to the table */
1009                         for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
1010                         {
1011                                 jqCloneThead[0].appendChild( aoCloneLayout[i].nTr );
1012                         }
1013
1014                         /* Use the handy _fnDrawHead function in DataTables to do the rowspan/colspan
1015                          * calculations for us
1016                          */
1017                         this.s.dt.oApi._fnDrawHead( this.s.dt, aoCloneLayout, true );
1018                 }
1019                 else
1020                 {
1021                         /* To ensure that we copy cell classes exactly, regardless of colspan, multiple rows
1022                          * etc, we make a copy of the header from the DataTable again, but don't insert the
1023                          * cloned cells, just copy the classes across. To get the matching layout for the
1024                          * fixed component, we use the DataTables _fnDetectHeader method, allowing 1:1 mapping
1025                          */
1026                         aoCloneLayout = this._fnCopyLayout( this.s.dt.aoHeader, aiColumns );
1027                         aoFixedHeader=[];
1028
1029                         this.s.dt.oApi._fnDetectHeader( aoFixedHeader, $('>thead', oClone.header)[0] );
1030
1031                         for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
1032                         {
1033                                 for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
1034                                 {
1035                                         aoFixedHeader[i][j].cell.className = aoCloneLayout[i][j].cell.className;
1036
1037                                         // If jQuery UI theming is used we need to copy those elements as well
1038                                         $('span.DataTables_sort_icon', aoFixedHeader[i][j].cell).each( function () {
1039                                                 this.className = $('span.DataTables_sort_icon', aoCloneLayout[i][j].cell)[0].className;
1040                                         } );
1041                                 }
1042                         }
1043                 }
1044                 this._fnEqualiseHeights( 'thead', this.dom.header, oClone.header );
1045
1046                 /*
1047                  * Body
1048                  */
1049                 if ( this.s.sHeightMatch == 'auto' )
1050                 {
1051                         /* Remove any heights which have been applied already and let the browser figure it out */
1052                         $('>tbody>tr', that.dom.body).css('height', 'auto');
1053                 }
1054
1055                 if ( oClone.body !== null )
1056                 {
1057                         oClone.body.parentNode.removeChild( oClone.body );
1058                         oClone.body = null;
1059                 }
1060
1061                 oClone.body = $(this.dom.body).clone(true)[0];
1062                 oClone.body.className += " DTFC_Cloned";
1063                 oClone.body.style.paddingBottom = this.s.dt.oScroll.iBarWidth+"px";
1064                 oClone.body.style.marginBottom = (this.s.dt.oScroll.iBarWidth*2)+"px"; /* For IE */
1065                 if ( oClone.body.getAttribute('id') !== null )
1066                 {
1067                         oClone.body.removeAttribute('id');
1068                 }
1069
1070                 $('>thead>tr', oClone.body).empty();
1071                 $('>tfoot', oClone.body).remove();
1072
1073                 var nBody = $('tbody', oClone.body)[0];
1074                 $(nBody).empty();
1075                 if ( this.s.dt.aiDisplay.length > 0 )
1076                 {
1077                         /* Copy the DataTables' header elements to force the column width in exactly the
1078                          * same way that DataTables does it - have the header element, apply the width and
1079                          * colapse it down
1080                          */
1081                         var nInnerThead = $('>thead>tr', oClone.body)[0];
1082                         for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
1083                         {
1084                                 iColumn = aiColumns[iIndex];
1085
1086                                 nClone = $(this.s.dt.aoColumns[iColumn].nTh).clone(true)[0];
1087                                 nClone.innerHTML = "";
1088
1089                                 var oStyle = nClone.style;
1090                                 oStyle.paddingTop = "0";
1091                                 oStyle.paddingBottom = "0";
1092                                 oStyle.borderTopWidth = "0";
1093                                 oStyle.borderBottomWidth = "0";
1094                                 oStyle.height = 0;
1095                                 oStyle.width = that.s.aiInnerWidths[iColumn]+"px";
1096
1097                                 nInnerThead.appendChild( nClone );
1098                         }
1099
1100                         /* Add in the tbody elements, cloning form the master table */
1101                         $('>tbody>tr', that.dom.body).each( function (z) {
1102                                 var n = this.cloneNode(false);
1103                                 n.removeAttribute('id');
1104                                 var i = that.s.dt.oFeatures.bServerSide===false ?
1105                                         that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z;
1106                                 var aTds = $(this).children('td, th');
1107
1108                                 for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
1109                                 {
1110                                         iColumn = aiColumns[iIndex];
1111
1112                                         if ( aTds.length > 0 )
1113                                         {
1114                                                 nClone = $( aTds[iColumn] ).clone(true, true)[0];
1115                                                 n.appendChild( nClone );
1116                                         }
1117                                 }
1118                                 nBody.appendChild( n );
1119                         } );
1120                 }
1121                 else
1122                 {
1123                         $('>tbody>tr', that.dom.body).each( function (z) {
1124                                 nClone = this.cloneNode(true);
1125                                 nClone.className += ' DTFC_NoData';
1126                                 $('td', nClone).html('');
1127                                 nBody.appendChild( nClone );
1128                         } );
1129                 }
1130
1131                 oClone.body.style.width = "100%";
1132                 oClone.body.style.margin = "0";
1133                 oClone.body.style.padding = "0";
1134
1135                 if ( bAll )
1136                 {
1137                         if ( typeof this.s.dt.oScroller != 'undefined' )
1138                         {
1139                                 oGrid.liner.appendChild( this.s.dt.oScroller.dom.force.cloneNode(true) );
1140                         }
1141                 }
1142                 oGrid.liner.appendChild( oClone.body );
1143
1144                 this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body );
1145
1146                 /*
1147                  * Footer
1148                  */
1149                 if ( this.s.dt.nTFoot !== null )
1150                 {
1151                         if ( bAll )
1152                         {
1153                                 if ( oClone.footer !== null )
1154                                 {
1155                                         oClone.footer.parentNode.removeChild( oClone.footer );
1156                                 }
1157                                 oClone.footer = $(this.dom.footer).clone(true, true)[0];
1158                                 oClone.footer.className += " DTFC_Cloned";
1159                                 oClone.footer.style.width = "100%";
1160                                 oGrid.foot.appendChild( oClone.footer );
1161
1162                                 /* Copy the footer just like we do for the header */
1163                                 aoCloneLayout = this._fnCopyLayout( this.s.dt.aoFooter, aiColumns );
1164                                 var jqCloneTfoot = $('>tfoot', oClone.footer);
1165                                 jqCloneTfoot.empty();
1166
1167                                 for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
1168                                 {
1169                                         jqCloneTfoot[0].appendChild( aoCloneLayout[i].nTr );
1170                                 }
1171                                 this.s.dt.oApi._fnDrawHead( this.s.dt, aoCloneLayout, true );
1172                         }
1173                         else
1174                         {
1175                                 aoCloneLayout = this._fnCopyLayout( this.s.dt.aoFooter, aiColumns );
1176                                 var aoCurrFooter=[];
1177
1178                                 this.s.dt.oApi._fnDetectHeader( aoCurrFooter, $('>tfoot', oClone.footer)[0] );
1179
1180                                 for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
1181                                 {
1182                                         for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
1183                                         {
1184                                                 aoCurrFooter[i][j].cell.className = aoCloneLayout[i][j].cell.className;
1185                                         }
1186                                 }
1187                         }
1188                         this._fnEqualiseHeights( 'tfoot', this.dom.footer, oClone.footer );
1189                 }
1190
1191                 /* Equalise the column widths between the header footer and body - body get's priority */
1192                 var anUnique = this.s.dt.oApi._fnGetUniqueThs( this.s.dt, $('>thead', oClone.header)[0] );
1193                 $(anUnique).each( function (i) {
1194                         iColumn = aiColumns[i];
1195                         this.style.width = that.s.aiInnerWidths[iColumn]+"px";
1196                 } );
1197
1198                 if ( that.s.dt.nTFoot !== null )
1199                 {
1200                         anUnique = this.s.dt.oApi._fnGetUniqueThs( this.s.dt, $('>tfoot', oClone.footer)[0] );
1201                         $(anUnique).each( function (i) {
1202                                 iColumn = aiColumns[i];
1203                                 this.style.width = that.s.aiInnerWidths[iColumn]+"px";
1204                         } );
1205                 }
1206         },
1207
1208
1209         /**
1210          * From a given table node (THEAD etc), get a list of TR direct child elements
1211          *  @param   {Node} nIn Table element to search for TR elements (THEAD, TBODY or TFOOT element)
1212          *  @returns {Array} List of TR elements found
1213          *  @private
1214          */
1215         "_fnGetTrNodes": function ( nIn )
1216         {
1217                 var aOut = [];
1218                 for ( var i=0, iLen=nIn.childNodes.length ; i<iLen ; i++ )
1219                 {
1220                         if ( nIn.childNodes[i].nodeName.toUpperCase() == "TR" )
1221                         {
1222                                 aOut.push( nIn.childNodes[i] );
1223                         }
1224                 }
1225                 return aOut;
1226         },
1227
1228
1229         /**
1230          * Equalise the heights of the rows in a given table node in a cross browser way
1231          *  @returns {void}
1232          *  @param   {String} nodeName Node type - thead, tbody or tfoot
1233          *  @param   {Node} original Original node to take the heights from
1234          *  @param   {Node} clone Copy the heights to
1235          *  @private
1236          */
1237         "_fnEqualiseHeights": function ( nodeName, original, clone )
1238         {
1239                 if ( this.s.sHeightMatch == 'none' && nodeName !== 'thead' && nodeName !== 'tfoot' )
1240                 {
1241                         return;
1242                 }
1243
1244                 var that = this,
1245                         i, iLen, iHeight, iHeight2, iHeightOriginal, iHeightClone,
1246                         rootOriginal = original.getElementsByTagName(nodeName)[0],
1247                         rootClone    = clone.getElementsByTagName(nodeName)[0],
1248                         jqBoxHack    = $('>'+nodeName+'>tr:eq(0)', original).children(':first'),
1249                         iBoxHack     = jqBoxHack.outerHeight() - jqBoxHack.height(),
1250                         anOriginal   = this._fnGetTrNodes( rootOriginal ),
1251                         anClone      = this._fnGetTrNodes( rootClone ),
1252                         heights      = [];
1253
1254                 for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
1255                 {
1256                         iHeightOriginal = anOriginal[i].offsetHeight;
1257                         iHeightClone = anClone[i].offsetHeight;
1258                         iHeight = iHeightClone > iHeightOriginal ? iHeightClone : iHeightOriginal;
1259
1260                         if ( this.s.sHeightMatch == 'semiauto' )
1261                         {
1262                                 anOriginal[i]._DTTC_iHeight = iHeight;
1263                         }
1264
1265                         heights.push( iHeight );
1266                 }
1267
1268                 for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
1269                 {
1270                         anClone[i].style.height = heights[i]+"px";
1271                         anOriginal[i].style.height = heights[i]+"px";
1272                 }
1273         }
1274 };
1275
1276
1277
1278 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1279  * Statics
1280  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1281
1282 /**
1283  * FixedColumns default settings for initialisation
1284  *  @name FixedColumns.defaults
1285  *  @namespace
1286  *  @static
1287  */
1288 FixedColumns.defaults = /** @lends FixedColumns.defaults */{
1289         /**
1290          * Number of left hand columns to fix in position
1291          *  @type     int
1292          *  @default  1
1293          *  @static
1294          *  @example
1295          *      var  = $('#example').dataTable( {
1296          *          "scrollX": "100%"
1297          *      } );
1298          *      new $.fn.dataTable.fixedColumns( table, {
1299          *          "leftColumns": 2
1300          *      } );
1301          */
1302         "iLeftColumns": 1,
1303
1304         /**
1305          * Number of right hand columns to fix in position
1306          *  @type     int
1307          *  @default  0
1308          *  @static
1309          *  @example
1310          *      var table = $('#example').dataTable( {
1311          *          "scrollX": "100%"
1312          *      } );
1313          *      new $.fn.dataTable.fixedColumns( table, {
1314          *          "rightColumns": 1
1315          *      } );
1316          */
1317         "iRightColumns": 0,
1318
1319         /**
1320          * Draw callback function which is called when FixedColumns has redrawn the fixed assets
1321          *  @type     function(object, object):void
1322          *  @default  null
1323          *  @static
1324          *  @example
1325          *      var table = $('#example').dataTable( {
1326          *          "scrollX": "100%"
1327          *      } );
1328          *      new $.fn.dataTable.fixedColumns( table, {
1329          *          "drawCallback": function () {
1330          *                  alert( "FixedColumns redraw" );
1331          *              }
1332          *      } );
1333          */
1334         "fnDrawCallback": null,
1335
1336         /**
1337          * Height matching algorthim to use. This can be "none" which will result in no height
1338          * matching being applied by FixedColumns (height matching could be forced by CSS in this
1339          * case), "semiauto" whereby the height calculation will be performed once, and the result
1340          * cached to be used again (fnRecalculateHeight can be used to force recalculation), or
1341          * "auto" when height matching is performed on every draw (slowest but must accurate)
1342          *  @type     string
1343          *  @default  semiauto
1344          *  @static
1345          *  @example
1346          *      var table = $('#example').dataTable( {
1347          *          "scrollX": "100%"
1348          *      } );
1349          *      new $.fn.dataTable.fixedColumns( table, {
1350          *          "heightMatch": "auto"
1351          *      } );
1352          */
1353         "sHeightMatch": "semiauto"
1354 };
1355
1356
1357
1358
1359 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1360  * Constants
1361  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1362
1363 /**
1364  * FixedColumns version
1365  *  @name      FixedColumns.version
1366  *  @type      String
1367  *  @default   See code
1368  *  @static
1369  */
1370 FixedColumns.version = "3.0.3";
1371
1372
1373
1374 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1375  * Fired events (for documentation)
1376  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1377
1378
1379 /**
1380  * 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.
1381  * @name FixedColumns#draw.dtfc
1382  * @event
1383  * @param {event} e jQuery event object
1384  * @param {object} o Event parameters from FixedColumns
1385  * @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
1386  * @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
1387  */
1388
1389
1390 // Make FixedColumns accessible from the DataTables instance
1391 $.fn.dataTable.FixedColumns = FixedColumns;
1392 $.fn.DataTable.FixedColumns = FixedColumns;
1393
1394
1395 return FixedColumns;
1396 }; // /factory
1397
1398
1399 // Define as an AMD module if possible
1400 if ( typeof define === 'function' && define.amd ) {
1401         define( ['jquery', 'datatables'], factory );
1402 }
1403 else if ( typeof exports === 'object' ) {
1404     // Node/CommonJS
1405     factory( require('jquery'), require('datatables') );
1406 }
1407 else if ( jQuery && !jQuery.fn.dataTable.FixedColumns ) {
1408         // Otherwise simply initialise as normal, stopping multiple evaluation
1409         factory( jQuery, jQuery.fn.dataTable );
1410 }
1411
1412
1413 })(window, document);