jquery-datatables-rails 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/dataTables/bootstrap/3/jquery.dataTables.bootstrap.js +64 -29
  3. data/app/assets/javascripts/dataTables/extras/dataTables.autoFill.js +806 -648
  4. data/app/assets/javascripts/dataTables/extras/dataTables.buttons.js +1607 -0
  5. data/app/assets/javascripts/dataTables/extras/dataTables.colReorder.js +220 -267
  6. data/app/assets/javascripts/dataTables/extras/dataTables.fixedColumns.js +164 -69
  7. data/app/assets/javascripts/dataTables/extras/dataTables.fixedHeader.js +469 -870
  8. data/app/assets/javascripts/dataTables/extras/dataTables.keyTable.js +636 -972
  9. data/app/assets/javascripts/dataTables/extras/dataTables.responsive.js +472 -187
  10. data/app/assets/javascripts/dataTables/extras/dataTables.rowReorder.js +619 -0
  11. data/app/assets/javascripts/dataTables/extras/dataTables.scroller.js +146 -111
  12. data/app/assets/javascripts/dataTables/extras/dataTables.select.js +1038 -0
  13. data/app/assets/javascripts/dataTables/jquery.dataTables.api.fnGetColumnData.js +0 -0
  14. data/app/assets/javascripts/dataTables/jquery.dataTables.api.fnReloadAjax.js +0 -0
  15. data/app/assets/javascripts/dataTables/jquery.dataTables.foundation.js +37 -61
  16. data/app/assets/javascripts/dataTables/jquery.dataTables.js +720 -387
  17. data/app/assets/javascripts/dataTables/jquery.dataTables.sorting.ipAddress.js +44 -0
  18. data/app/assets/javascripts/dataTables/jquery.dataTables.sorting.numbersHtml.js +0 -0
  19. data/app/assets/javascripts/dataTables/jquery.dataTables.typeDetection.numbersHtml.js +0 -0
  20. data/app/assets/stylesheets/dataTables/jquery.dataTables.scss +34 -66
  21. data/app/assets/stylesheets/dataTables/src/demo_table.css +1 -1
  22. data/app/assets/stylesheets/dataTables/src/demo_table_jui.css.scss +4 -4
  23. data/lib/jquery/datatables/rails/version.rb +1 -1
  24. metadata +24 -19
@@ -1,11 +1,11 @@
1
- /*! FixedColumns 3.0.4
1
+ /*! FixedColumns 3.2.0
2
2
  * ©2010-2014 SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
5
5
  /**
6
6
  * @summary FixedColumns
7
7
  * @description Freeze columns in place on a scrolling DataTable
8
- * @version 3.0.4
8
+ * @version 3.2.0
9
9
  * @file dataTables.fixedColumns.js
10
10
  * @author SpryMedia Ltd (www.sprymedia.co.uk)
11
11
  * @contact www.sprymedia.co.uk/contact
@@ -20,13 +20,35 @@
20
20
  *
21
21
  * For details please refer to: http://www.datatables.net
22
22
  */
23
+ (function( factory ){
24
+ if ( typeof define === 'function' && define.amd ) {
25
+ // AMD
26
+ define( ['jquery', 'datatables.net'], function ( $ ) {
27
+ return factory( $, window, document );
28
+ } );
29
+ }
30
+ else if ( typeof exports === 'object' ) {
31
+ // CommonJS
32
+ module.exports = function (root, $) {
33
+ if ( ! root ) {
34
+ root = window;
35
+ }
23
36
 
37
+ if ( ! $ || ! $.fn.dataTable ) {
38
+ $ = require('datatables.net')(root, $).$;
39
+ }
24
40
 
25
- (function(window, document, undefined) {
26
-
41
+ return factory( $, root, root.document );
42
+ };
43
+ }
44
+ else {
45
+ // Browser
46
+ factory( jQuery, window, document );
47
+ }
48
+ }(function( $, window, document, undefined ) {
49
+ 'use strict';
50
+ var DataTable = $.fn.dataTable;
27
51
 
28
- var factory = function( $, DataTable ) {
29
- "use strict";
30
52
 
31
53
  /**
32
54
  * When making use of DataTables' x-axis scrolling feature, you may wish to
@@ -62,14 +84,12 @@ var FixedColumns = function ( dt, init ) {
62
84
  var that = this;
63
85
 
64
86
  /* Sanity check - you just know it will happen */
65
- if ( ! ( this instanceof FixedColumns ) )
66
- {
87
+ if ( ! ( this instanceof FixedColumns ) ) {
67
88
  alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
68
89
  return;
69
90
  }
70
91
 
71
- if ( typeof init == 'undefined' )
72
- {
92
+ if ( init === undefined || init === true ) {
73
93
  init = {};
74
94
  }
75
95
 
@@ -82,9 +102,7 @@ var FixedColumns = function ( dt, init ) {
82
102
  }
83
103
 
84
104
  // 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();
105
+ var dtSettings = new $.fn.dataTable.Api( dt ).settings()[0];
88
106
 
89
107
  /**
90
108
  * Settings object which contains customisable information for FixedColumns instance
@@ -265,6 +283,10 @@ var FixedColumns = function ( dt, init ) {
265
283
  }
266
284
  };
267
285
 
286
+ if ( dtSettings._oFixedColumns ) {
287
+ throw 'FixedColumns already initialised on this table';
288
+ }
289
+
268
290
  /* Attach the instance to the DataTables instance so it can be accessed easily */
269
291
  dtSettings._oFixedColumns = this;
270
292
 
@@ -283,7 +305,7 @@ var FixedColumns = function ( dt, init ) {
283
305
 
284
306
 
285
307
 
286
- FixedColumns.prototype = /** @lends FixedColumns.prototype */{
308
+ $.extend( FixedColumns.prototype , {
287
309
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
288
310
  * Public methods
289
311
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -474,7 +496,11 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
474
496
  .on( 'mouseover.DTFC touchstart.DTFC', function () {
475
497
  mouseController = 'main';
476
498
  } )
477
- .on( 'scroll.DTFC', function () {
499
+ .on( 'scroll.DTFC', function (e) {
500
+ if ( ! mouseController && e.originalEvent ) {
501
+ mouseController = 'main';
502
+ }
503
+
478
504
  if ( mouseController === 'main' ) {
479
505
  if ( that.s.iLeftColumns > 0 ) {
480
506
  that.dom.grid.left.liner.scrollTop = that.dom.scroller.scrollTop;
@@ -495,7 +521,11 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
495
521
  .on( 'mouseover.DTFC touchstart.DTFC', function () {
496
522
  mouseController = 'left';
497
523
  } )
498
- .on( 'scroll.DTFC', function () {
524
+ .on( 'scroll.DTFC', function ( e ) {
525
+ if ( ! mouseController && e.originalEvent ) {
526
+ mouseController = 'left';
527
+ }
528
+
499
529
  if ( mouseController === 'left' ) {
500
530
  that.dom.scroller.scrollTop = that.dom.grid.left.liner.scrollTop;
501
531
  if ( that.s.iRightColumns > 0 ) {
@@ -503,7 +533,7 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
503
533
  }
504
534
  }
505
535
  } )
506
- .on( wheelType, function(e) { // xxx update the destroy as well
536
+ .on( wheelType, function(e) {
507
537
  // Pass horizontal scrolling through
508
538
  var xDelta = e.type === 'wheel' ?
509
539
  -e.originalEvent.deltaX :
@@ -518,7 +548,11 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
518
548
  .on( 'mouseover.DTFC touchstart.DTFC', function () {
519
549
  mouseController = 'right';
520
550
  } )
521
- .on( 'scroll.DTFC', function () {
551
+ .on( 'scroll.DTFC', function ( e ) {
552
+ if ( ! mouseController && e.originalEvent ) {
553
+ mouseController = 'right';
554
+ }
555
+
522
556
  if ( mouseController === 'right' ) {
523
557
  that.dom.scroller.scrollTop = that.dom.grid.right.liner.scrollTop;
524
558
  if ( that.s.iLeftColumns > 0 ) {
@@ -551,21 +585,23 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
551
585
  that._fnColCalc();
552
586
  that._fnGridLayout( that );
553
587
  } )
554
- .on( 'column-visibility.dt.DTFC', function () {
555
- that._fnColCalc();
556
- that._fnGridLayout( that );
557
- that._fnDraw( true );
588
+ .on( 'column-visibility.dt.DTFC', function ( e, settings, column, vis, recalc ) {
589
+ if ( recalc === undefined || recalc ) {
590
+ that._fnColCalc();
591
+ that._fnGridLayout( that );
592
+ that._fnDraw( true );
593
+ }
558
594
  } )
559
595
  .on( 'destroy.dt.DTFC', function () {
560
- jqTable.off( 'column-sizing.dt.DTFC destroy.dt.DTFC draw.dt.DTFC' );
596
+ jqTable.off( 'column-sizing.dt.DTFC column-visibility.dt.DTFC destroy.dt.DTFC draw.dt.DTFC' );
561
597
 
562
- $(that.dom.scroller).off( 'scroll.DTFC mouseover.DTFC' );
598
+ $(that.dom.scroller).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC' );
563
599
  $(window).off( 'resize.DTFC' );
564
600
 
565
- $(that.dom.grid.left.liner).off( 'scroll.DTFC mouseover.DTFC '+wheelType );
601
+ $(that.dom.grid.left.liner).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC '+wheelType );
566
602
  $(that.dom.grid.left.wrapper).remove();
567
603
 
568
- $(that.dom.grid.right.liner).off( 'scroll.DTFC mouseover.DTFC '+wheelType );
604
+ $(that.dom.grid.right.liner).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC '+wheelType );
569
605
  $(that.dom.grid.right.wrapper).remove();
570
606
  } );
571
607
 
@@ -922,9 +958,10 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
922
958
  * @returns {Array} Copy of the layout array
923
959
  * @param {Object} aoOriginal Layout array from DataTables (aoHeader or aoFooter)
924
960
  * @param {Object} aiColumns Columns to copy
961
+ * @param {boolean} events Copy cell events or not
925
962
  * @private
926
963
  */
927
- "_fnCopyLayout": function ( aoOriginal, aiColumns )
964
+ "_fnCopyLayout": function ( aoOriginal, aiColumns, events )
928
965
  {
929
966
  var aReturn = [];
930
967
  var aClones = [];
@@ -933,7 +970,7 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
933
970
  for ( var i=0, iLen=aoOriginal.length ; i<iLen ; i++ )
934
971
  {
935
972
  var aRow = [];
936
- aRow.nTr = $(aoOriginal[i].nTr).clone(true, true)[0];
973
+ aRow.nTr = $(aoOriginal[i].nTr).clone(events, false)[0];
937
974
 
938
975
  for ( var j=0, jLen=this.s.iTableColumns ; j<jLen ; j++ )
939
976
  {
@@ -945,7 +982,7 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
945
982
  var iCloned = $.inArray( aoOriginal[i][j].cell, aCloned );
946
983
  if ( iCloned === -1 )
947
984
  {
948
- var nClone = $(aoOriginal[i][j].cell).clone(true, true)[0];
985
+ var nClone = $(aoOriginal[i][j].cell).clone(events, false)[0];
949
986
  aClones.push( nClone );
950
987
  aCloned.push( aoOriginal[i][j].cell );
951
988
 
@@ -992,17 +1029,15 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
992
1029
  */
993
1030
  if ( bAll )
994
1031
  {
995
- if ( oClone.header !== null )
996
- {
997
- oClone.header.parentNode.removeChild( oClone.header );
998
- }
999
- oClone.header = $(this.dom.header).clone(true, true)[0];
1032
+ $(oClone.header).remove();
1033
+
1034
+ oClone.header = $(this.dom.header).clone(true, false)[0];
1000
1035
  oClone.header.className += " DTFC_Cloned";
1001
1036
  oClone.header.style.width = "100%";
1002
1037
  oGrid.head.appendChild( oClone.header );
1003
1038
 
1004
1039
  /* Copy the DataTables layout cache for the header for our floating column */
1005
- aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns );
1040
+ aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, true );
1006
1041
  jqCloneThead = $('>thead', oClone.header);
1007
1042
  jqCloneThead.empty();
1008
1043
 
@@ -1024,7 +1059,7 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
1024
1059
  * cloned cells, just copy the classes across. To get the matching layout for the
1025
1060
  * fixed component, we use the DataTables _fnDetectHeader method, allowing 1:1 mapping
1026
1061
  */
1027
- aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns );
1062
+ aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, false );
1028
1063
  aoFixedHeader=[];
1029
1064
 
1030
1065
  dt.oApi._fnDetectHeader( aoFixedHeader, $('>thead', oClone.header)[0] );
@@ -1055,7 +1090,7 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
1055
1090
 
1056
1091
  if ( oClone.body !== null )
1057
1092
  {
1058
- oClone.body.parentNode.removeChild( oClone.body );
1093
+ $(oClone.body).remove();
1059
1094
  oClone.body = null;
1060
1095
  }
1061
1096
 
@@ -1169,7 +1204,7 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
1169
1204
  oGrid.foot.appendChild( oClone.footer );
1170
1205
 
1171
1206
  /* Copy the footer just like we do for the header */
1172
- aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns );
1207
+ aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, true );
1173
1208
  var jqCloneTfoot = $('>tfoot', oClone.footer);
1174
1209
  jqCloneTfoot.empty();
1175
1210
 
@@ -1181,7 +1216,7 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
1181
1216
  }
1182
1217
  else
1183
1218
  {
1184
- aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns );
1219
+ aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, false );
1185
1220
  var aoCurrFooter=[];
1186
1221
 
1187
1222
  dt.oApi._fnDetectHeader( aoCurrFooter, $('>tfoot', oClone.footer)[0] );
@@ -1280,7 +1315,7 @@ FixedColumns.prototype = /** @lends FixedColumns.prototype */{
1280
1315
  anOriginal[i].style.height = heights[i]+"px";
1281
1316
  }
1282
1317
  }
1283
- };
1318
+ } );
1284
1319
 
1285
1320
 
1286
1321
 
@@ -1376,48 +1411,108 @@ FixedColumns.defaults = /** @lends FixedColumns.defaults */{
1376
1411
  * @default See code
1377
1412
  * @static
1378
1413
  */
1379
- FixedColumns.version = "3.0.4";
1414
+ FixedColumns.version = "3.2.0";
1380
1415
 
1381
1416
 
1382
1417
 
1383
1418
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1384
- * Fired events (for documentation)
1419
+ * DataTables API integration
1385
1420
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1386
1421
 
1422
+ DataTable.Api.register( 'fixedColumns()', function () {
1423
+ return this;
1424
+ } );
1387
1425
 
1388
- /**
1389
- * 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.
1390
- * @name FixedColumns#draw.dtfc
1391
- * @event
1392
- * @param {event} e jQuery event object
1393
- * @param {object} o Event parameters from FixedColumns
1394
- * @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
1395
- * @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
1396
- */
1426
+ DataTable.Api.register( 'fixedColumns().update()', function () {
1427
+ return this.iterator( 'table', function ( ctx ) {
1428
+ if ( ctx._oFixedColumns ) {
1429
+ ctx._oFixedColumns.fnUpdate();
1430
+ }
1431
+ } );
1432
+ } );
1397
1433
 
1434
+ DataTable.Api.register( 'fixedColumns().relayout()', function () {
1435
+ return this.iterator( 'table', function ( ctx ) {
1436
+ if ( ctx._oFixedColumns ) {
1437
+ ctx._oFixedColumns.fnRedrawLayout();
1438
+ }
1439
+ } );
1440
+ } );
1398
1441
 
1399
- // Make FixedColumns accessible from the DataTables instance
1400
- $.fn.dataTable.FixedColumns = FixedColumns;
1401
- $.fn.DataTable.FixedColumns = FixedColumns;
1442
+ DataTable.Api.register( 'rows().recalcHeight()', function () {
1443
+ return this.iterator( 'row', function ( ctx, idx ) {
1444
+ if ( ctx._oFixedColumns ) {
1445
+ ctx._oFixedColumns.fnRecalculateHeight( this.row(idx).node() );
1446
+ }
1447
+ } );
1448
+ } );
1449
+
1450
+ DataTable.Api.register( 'fixedColumns().rowIndex()', function ( row ) {
1451
+ row = $(row);
1452
+
1453
+ return row.parents('.DTFC_Cloned').length ?
1454
+ this.rows( { page: 'current' } ).indexes()[ row.index() ] :
1455
+ this.row( row ).index();
1456
+ } );
1457
+
1458
+ DataTable.Api.register( 'fixedColumns().cellIndex()', function ( cell ) {
1459
+ cell = $(cell);
1460
+
1461
+ if ( cell.parents('.DTFC_Cloned').length ) {
1462
+ var rowClonedIdx = cell.parent().index();
1463
+ var rowIdx = this.rows( { page: 'current' } ).indexes()[ rowClonedIdx ];
1464
+ var columnIdx;
1465
+
1466
+ if ( cell.parents('.DTFC_LeftWrapper').length ) {
1467
+ columnIdx = cell.index();
1468
+ }
1469
+ else {
1470
+ var columns = this.columns().flatten().length;
1471
+ columnIdx = columns - this.context[0]._oFixedColumns.s.iRightColumns + cell.index();
1472
+ }
1473
+
1474
+ return {
1475
+ row: rowIdx,
1476
+ column: this.column.index( 'toData', columnIdx ),
1477
+ columnVisible: columnIdx
1478
+ };
1479
+ }
1480
+ else {
1481
+ return this.cell( cell ).index();
1482
+ }
1483
+ } );
1402
1484
 
1403
1485
 
1404
- return FixedColumns;
1405
- }; // /factory
1406
1486
 
1407
1487
 
1408
- // Define as an AMD module if possible
1409
- if ( typeof define === 'function' && define.amd ) {
1410
- define( ['jquery', 'datatables'], factory );
1411
- }
1412
- else if ( typeof exports === 'object' ) {
1413
- // Node/CommonJS
1414
- factory( require('jquery'), require('datatables') );
1415
- }
1416
- else if ( jQuery && !jQuery.fn.dataTable.FixedColumns ) {
1417
- // Otherwise simply initialise as normal, stopping multiple evaluation
1418
- factory( jQuery, jQuery.fn.dataTable );
1419
- }
1488
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1489
+ * Initialisation
1490
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1491
+
1492
+ // Attach a listener to the document which listens for DataTables initialisation
1493
+ // events so we can automatically initialise
1494
+ $(document).on( 'init.dt.fixedColumns', function (e, settings) {
1495
+ if ( e.namespace !== 'dt' ) {
1496
+ return;
1497
+ }
1498
+
1499
+ var init = settings.oInit.fixedColumns;
1500
+ var defaults = DataTable.defaults.fixedColumns;
1420
1501
 
1502
+ if ( init || defaults ) {
1503
+ var opts = $.extend( {}, init, defaults );
1421
1504
 
1422
- })(window, document);
1505
+ if ( init !== false ) {
1506
+ new FixedColumns( settings, opts );
1507
+ }
1508
+ }
1509
+ } );
1423
1510
 
1511
+
1512
+
1513
+ // Make FixedColumns accessible from the DataTables instance
1514
+ $.fn.dataTable.FixedColumns = FixedColumns;
1515
+ $.fn.DataTable.FixedColumns = FixedColumns;
1516
+
1517
+ return FixedColumns;
1518
+ }));
@@ -1,16 +1,16 @@
1
- /*! FixedHeader 2.1.2
2
- * ©2010-2014 SpryMedia Ltd - datatables.net/license
1
+ /*! FixedHeader 3.1.0
2
+ * ©2009-2015 SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
5
5
  /**
6
6
  * @summary FixedHeader
7
7
  * @description Fix a table's header or footer, so it is always visible while
8
- * Scrolling
9
- * @version 2.1.2
8
+ * scrolling
9
+ * @version 3.1.0
10
10
  * @file dataTables.fixedHeader.js
11
11
  * @author SpryMedia Ltd (www.sprymedia.co.uk)
12
12
  * @contact www.sprymedia.co.uk/contact
13
- * @copyright Copyright 2009-2014 SpryMedia Ltd.
13
+ * @copyright Copyright 2009-2015 SpryMedia Ltd.
14
14
  *
15
15
  * This source file is free software, available under the following license:
16
16
  * MIT license - http://datatables.net/license/mit
@@ -22,125 +22,106 @@
22
22
  * For details please refer to: http://www.datatables.net
23
23
  */
24
24
 
25
- /* Global scope for FixedColumns for backwards compatibility - will be removed
26
- * in future. Not documented in 1.1.x.
27
- */
25
+ (function( factory ){
26
+ if ( typeof define === 'function' && define.amd ) {
27
+ // AMD
28
+ define( ['jquery', 'datatables.net'], function ( $ ) {
29
+ return factory( $, window, document );
30
+ } );
31
+ }
32
+ else if ( typeof exports === 'object' ) {
33
+ // CommonJS
34
+ module.exports = function (root, $) {
35
+ if ( ! root ) {
36
+ root = window;
37
+ }
28
38
 
29
- /* Global scope for FixedColumns */
30
- var FixedHeader;
39
+ if ( ! $ || ! $.fn.dataTable ) {
40
+ $ = require('datatables.net')(root, $).$;
41
+ }
31
42
 
32
- (function(window, document, undefined) {
43
+ return factory( $, root, root.document );
44
+ };
45
+ }
46
+ else {
47
+ // Browser
48
+ factory( jQuery, window, document );
49
+ }
50
+ }(function( $, window, document, undefined ) {
51
+ 'use strict';
52
+ var DataTable = $.fn.dataTable;
33
53
 
34
54
 
35
- var factory = function( $, DataTable ) {
36
- "use strict";
55
+ var _instCounter = 0;
37
56
 
38
- /*
39
- * Function: FixedHeader
40
- * Purpose: Provide 'fixed' header, footer and columns for a DataTable
41
- * Returns: object:FixedHeader - must be called with 'new'
42
- * Inputs: mixed:mTable - target table
43
- * @param {object} dt DataTables instance or HTML table node. With DataTables
44
- * 1.10 this can also be a jQuery collection (with just a single table in its
45
- * result set), a jQuery selector, DataTables API instance or settings
46
- * object.
47
- * @param {object} [oInit] initialisation settings, with the following
48
- * properties (each optional)
49
- * * bool:top - fix the header (default true)
50
- * * bool:bottom - fix the footer (default false)
51
- * * int:left - fix the left column(s) (default 0)
52
- * * int:right - fix the right column(s) (default 0)
53
- * * int:zTop - fixed header zIndex
54
- * * int:zBottom - fixed footer zIndex
55
- * * int:zLeft - fixed left zIndex
56
- * * int:zRight - fixed right zIndex
57
- */
58
- FixedHeader = function ( mTable, oInit ) {
59
- /* Sanity check - you just know it will happen */
60
- if ( ! this instanceof FixedHeader )
61
- {
62
- alert( "FixedHeader warning: FixedHeader must be initialised with the 'new' keyword." );
63
- return;
57
+ var FixedHeader = function ( dt, config ) {
58
+ // Sanity check - you just know it will happen
59
+ if ( ! (this instanceof FixedHeader) ) {
60
+ throw "FixedHeader must be initialised with the 'new' keyword.";
64
61
  }
65
62
 
66
- var that = this;
67
- var oSettings = {
68
- "aoCache": [],
69
- "oSides": {
70
- "top": true,
71
- "bottom": false,
72
- "left": 0,
73
- "right": 0
74
- },
75
- "oZIndexes": {
76
- "top": 104,
77
- "bottom": 103,
78
- "left": 102,
79
- "right": 101
80
- },
81
- "oCloneOnDraw": {
82
- "top": false,
83
- "bottom": false,
84
- "left": true,
85
- "right": true
86
- },
87
- "oMes": {
88
- "iTableWidth": 0,
89
- "iTableHeight": 0,
90
- "iTableLeft": 0,
91
- "iTableRight": 0, /* note this is left+width, not actually "right" */
92
- "iTableTop": 0,
93
- "iTableBottom": 0 /* note this is top+height, not actually "bottom" */
63
+ // Allow a boolean true for defaults
64
+ if ( config === true ) {
65
+ config = {};
66
+ }
67
+
68
+ dt = new DataTable.Api( dt );
69
+
70
+ this.c = $.extend( true, {}, FixedHeader.defaults, config );
71
+
72
+ this.s = {
73
+ dt: dt,
74
+ position: {
75
+ theadTop: 0,
76
+ tbodyTop: 0,
77
+ tfootTop: 0,
78
+ tfootBottom: 0,
79
+ width: 0,
80
+ left: 0,
81
+ tfootHeight: 0,
82
+ theadHeight: 0,
83
+ windowHeight: $(window).height(),
84
+ visible: true
94
85
  },
95
- "oOffset": {
96
- "top": 0
86
+ headerMode: null,
87
+ footerMode: null,
88
+ autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
89
+ namespace: '.dtfc'+(_instCounter++),
90
+ scrollLeft: {
91
+ header: -1,
92
+ footer: -1
97
93
  },
98
- "nTable": null,
99
- "bFooter": false,
100
- "bInitComplete": false
101
- };
102
-
103
- /*
104
- * Function: fnGetSettings
105
- * Purpose: Get the settings for this object
106
- * Returns: object: - settings object
107
- * Inputs: -
108
- */
109
- this.fnGetSettings = function () {
110
- return oSettings;
111
- };
112
-
113
- /*
114
- * Function: fnUpdate
115
- * Purpose: Update the positioning and copies of the fixed elements
116
- * Returns: -
117
- * Inputs: -
118
- */
119
- this.fnUpdate = function () {
120
- this._fnUpdateClones();
121
- this._fnUpdatePositions();
94
+ enable: true
122
95
  };
123
96
 
124
- /*
125
- * Function: fnPosition
126
- * Purpose: Update the positioning of the fixed elements
127
- * Returns: -
128
- * Inputs: -
129
- */
130
- this.fnPosition = function () {
131
- this._fnUpdatePositions();
97
+ this.dom = {
98
+ floatingHeader: null,
99
+ thead: $(dt.table().header()),
100
+ tbody: $(dt.table().body()),
101
+ tfoot: $(dt.table().footer()),
102
+ header: {
103
+ host: null,
104
+ floating: null,
105
+ placeholder: null
106
+ },
107
+ footer: {
108
+ host: null,
109
+ floating: null,
110
+ placeholder: null
111
+ }
132
112
  };
133
113
 
114
+ this.dom.header.host = this.dom.thead.parent();
115
+ this.dom.footer.host = this.dom.tfoot.parent();
134
116
 
135
- var dt = $.fn.dataTable.Api ?
136
- new $.fn.dataTable.Api( mTable ).settings()[0] :
137
- mTable.fnSettings();
138
-
139
- dt._oPluginFixedHeader = this;
117
+ var dtSettings = dt.settings()[0];
118
+ if ( dtSettings._fixedHeader ) {
119
+ throw "FixedHeader already initialised on table "+dtSettings.nTable.id;
120
+ }
140
121
 
141
- /* Let's do it */
142
- this.fnInit( dt, oInit );
122
+ dtSettings._fixedHeader = this;
143
123
 
124
+ this._constructor();
144
125
  };
145
126
 
146
127
 
@@ -149,880 +130,498 @@ FixedHeader = function ( mTable, oInit ) {
149
130
  * Purpose: Prototype for FixedHeader
150
131
  * Scope: global
151
132
  */
152
- FixedHeader.prototype = {
153
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
154
- * Initialisation
133
+ $.extend( FixedHeader.prototype, {
134
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
135
+ * API methods
155
136
  */
156
-
157
- /*
158
- * Function: fnInit
159
- * Purpose: The "constructor"
160
- * Returns: -
161
- * Inputs: {as FixedHeader function}
137
+
138
+ /**
139
+ * Enable / disable the fixed elements
140
+ *
141
+ * @param {boolean} enable `true` to enable, `false` to disable
162
142
  */
163
- fnInit: function ( oDtSettings, oInit )
143
+ enable: function ( enable )
164
144
  {
165
- var s = this.fnGetSettings();
166
- var that = this;
167
-
168
- /* Record the user definable settings */
169
- this.fnInitSettings( s, oInit );
145
+ this.s.enable = enable;
170
146
 
171
- if ( oDtSettings.oScroll.sX !== "" || oDtSettings.oScroll.sY !== "" )
172
- {
173
- alert( "FixedHeader 2 is not supported with DataTables' scrolling mode at this time" );
174
- return;
147
+ if ( this.c.header ) {
148
+ this._modeChange( 'in-place', 'header', true );
175
149
  }
176
150
 
177
- s.nTable = oDtSettings.nTable;
178
- oDtSettings.aoDrawCallback.unshift( {
179
- "fn": function () {
180
- FixedHeader.fnMeasure();
181
- that._fnUpdateClones.call(that);
182
- that._fnUpdatePositions.call(that);
183
- },
184
- "sName": "FixedHeader"
185
- } );
186
-
187
- s.bFooter = ($('>tfoot', s.nTable).length > 0) ? true : false;
188
-
189
- /* Add the 'sides' that are fixed */
190
- if ( s.oSides.top )
191
- {
192
- s.aoCache.push( that._fnCloneTable( "fixedHeader", "FixedHeader_Header", that._fnCloneThead ) );
193
- }
194
- if ( s.oSides.bottom )
195
- {
196
- s.aoCache.push( that._fnCloneTable( "fixedFooter", "FixedHeader_Footer", that._fnCloneTfoot ) );
197
- }
198
- if ( s.oSides.left )
199
- {
200
- s.aoCache.push( that._fnCloneTable( "fixedLeft", "FixedHeader_Left", that._fnCloneTLeft, s.oSides.left ) );
151
+ if ( this.c.footer && this.dom.tfoot.length ) {
152
+ this._modeChange( 'in-place', 'footer', true );
201
153
  }
202
- if ( s.oSides.right )
203
- {
204
- s.aoCache.push( that._fnCloneTable( "fixedRight", "FixedHeader_Right", that._fnCloneTRight, s.oSides.right ) );
205
- }
206
-
207
- /* Event listeners for window movement */
208
- FixedHeader.afnScroll.push( function () {
209
- that._fnUpdatePositions.call(that);
210
- } );
211
154
 
212
- $(window).resize( function () {
213
- FixedHeader.fnMeasure();
214
- that._fnUpdateClones.call(that);
215
- that._fnUpdatePositions.call(that);
216
- } );
217
-
218
- $(s.nTable)
219
- .on('column-reorder.dt', function () {
220
- FixedHeader.fnMeasure();
221
- that._fnUpdateClones( true );
222
- that._fnUpdatePositions();
223
- } )
224
- .on('column-visibility.dt', function () {
225
- FixedHeader.fnMeasure();
226
- that._fnUpdateClones( true );
227
- that._fnUpdatePositions();
228
- } );
229
-
230
- /* Get things right to start with */
231
- FixedHeader.fnMeasure();
232
- that._fnUpdateClones();
233
- that._fnUpdatePositions();
234
-
235
- s.bInitComplete = true;
155
+ this.update();
236
156
  },
237
-
238
-
239
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
240
- * Support functions
241
- */
242
-
243
- /*
244
- * Function: fnInitSettings
245
- * Purpose: Take the user's settings and copy them to our local store
246
- * Returns: -
247
- * Inputs: object:s - the local settings object
248
- * object:oInit - the user's settings object
157
+
158
+ /**
159
+ * Set header offset
160
+ *
161
+ * @param {int} new value for headerOffset
249
162
  */
250
- fnInitSettings: function ( s, oInit )
163
+ headerOffset: function ( offset )
251
164
  {
252
- if ( oInit !== undefined )
253
- {
254
- if ( oInit.top !== undefined ) {
255
- s.oSides.top = oInit.top;
256
- }
257
- if ( oInit.bottom !== undefined ) {
258
- s.oSides.bottom = oInit.bottom;
259
- }
260
- if ( typeof oInit.left == 'boolean' ) {
261
- s.oSides.left = oInit.left ? 1 : 0;
262
- }
263
- else if ( oInit.left !== undefined ) {
264
- s.oSides.left = oInit.left;
265
- }
266
- if ( typeof oInit.right == 'boolean' ) {
267
- s.oSides.right = oInit.right ? 1 : 0;
268
- }
269
- else if ( oInit.right !== undefined ) {
270
- s.oSides.right = oInit.right;
271
- }
272
-
273
- if ( oInit.zTop !== undefined ) {
274
- s.oZIndexes.top = oInit.zTop;
275
- }
276
- if ( oInit.zBottom !== undefined ) {
277
- s.oZIndexes.bottom = oInit.zBottom;
278
- }
279
- if ( oInit.zLeft !== undefined ) {
280
- s.oZIndexes.left = oInit.zLeft;
281
- }
282
- if ( oInit.zRight !== undefined ) {
283
- s.oZIndexes.right = oInit.zRight;
284
- }
285
-
286
- if ( oInit.offsetTop !== undefined ) {
287
- s.oOffset.top = oInit.offsetTop;
288
- }
289
- if ( oInit.alwaysCloneTop !== undefined ) {
290
- s.oCloneOnDraw.top = oInit.alwaysCloneTop;
291
- }
292
- if ( oInit.alwaysCloneBottom !== undefined ) {
293
- s.oCloneOnDraw.bottom = oInit.alwaysCloneBottom;
294
- }
295
- if ( oInit.alwaysCloneLeft !== undefined ) {
296
- s.oCloneOnDraw.left = oInit.alwaysCloneLeft;
297
- }
298
- if ( oInit.alwaysCloneRight !== undefined ) {
299
- s.oCloneOnDraw.right = oInit.alwaysCloneRight;
300
- }
165
+ if ( offset !== undefined ) {
166
+ this.c.headerOffset = offset;
167
+ this.update();
301
168
  }
302
- },
303
169
 
304
- /*
305
- * Function: _fnCloneTable
306
- * Purpose: Clone the table node and do basic initialisation
307
- * Returns: -
308
- * Inputs: -
170
+ return this.c.headerOffset;
171
+ },
172
+
173
+ /**
174
+ * Set footer offset
175
+ *
176
+ * @param {int} new value for footerOffset
309
177
  */
310
- _fnCloneTable: function ( sType, sClass, fnClone, iCells )
178
+ footerOffset: function ( offset )
311
179
  {
312
- var s = this.fnGetSettings();
313
- var nCTable;
314
-
315
- /* We know that the table _MUST_ has a DIV wrapped around it, because this is simply how
316
- * DataTables works. Therefore, we can set this to be relatively position (if it is not
317
- * alreadu absolute, and use this as the base point for the cloned header
318
- */
319
- if ( $(s.nTable.parentNode).css('position') != "absolute" )
320
- {
321
- s.nTable.parentNode.style.position = "relative";
322
- }
323
-
324
- /* Just a shallow clone will do - we only want the table node */
325
- nCTable = s.nTable.cloneNode( false );
326
- nCTable.removeAttribute( 'id' );
327
-
328
- var nDiv = document.createElement( 'div' );
329
- nDiv.style.position = "absolute";
330
- nDiv.style.top = "0px";
331
- nDiv.style.left = "0px";
332
- nDiv.className += " FixedHeader_Cloned "+sType+" "+sClass;
333
-
334
- /* Set the zIndexes */
335
- if ( sType == "fixedHeader" )
336
- {
337
- nDiv.style.zIndex = s.oZIndexes.top;
338
- }
339
- if ( sType == "fixedFooter" )
340
- {
341
- nDiv.style.zIndex = s.oZIndexes.bottom;
342
- }
343
- if ( sType == "fixedLeft" )
344
- {
345
- nDiv.style.zIndex = s.oZIndexes.left;
346
- }
347
- else if ( sType == "fixedRight" )
348
- {
349
- nDiv.style.zIndex = s.oZIndexes.right;
180
+ if ( offset !== undefined ) {
181
+ this.c.footerOffset = offset;
182
+ this.update();
350
183
  }
351
184
 
352
- /* remove margins since we are going to position it absolutely */
353
- nCTable.style.margin = "0";
354
-
355
- /* Insert the newly cloned table into the DOM, on top of the "real" header */
356
- nDiv.appendChild( nCTable );
357
- document.body.appendChild( nDiv );
358
-
359
- return {
360
- "nNode": nCTable,
361
- "nWrapper": nDiv,
362
- "sType": sType,
363
- "sPosition": "",
364
- "sTop": "",
365
- "sLeft": "",
366
- "fnClone": fnClone,
367
- "iCells": iCells
368
- };
185
+ return this.c.footerOffset;
369
186
  },
370
187
 
371
- /*
372
- * Function: _fnMeasure
373
- * Purpose: Get the current positioning of the table in the DOM
374
- * Returns: -
375
- * Inputs: -
188
+
189
+ /**
190
+ * Recalculate the position of the fixed elements and force them into place
376
191
  */
377
- _fnMeasure: function ()
192
+ update: function ()
378
193
  {
379
- var
380
- s = this.fnGetSettings(),
381
- m = s.oMes,
382
- jqTable = $(s.nTable),
383
- oOffset = jqTable.offset(),
384
- iParentScrollTop = this._fnSumScroll( s.nTable.parentNode, 'scrollTop' ),
385
- iParentScrollLeft = this._fnSumScroll( s.nTable.parentNode, 'scrollLeft' );
386
-
387
- m.iTableWidth = jqTable.outerWidth();
388
- m.iTableHeight = jqTable.outerHeight();
389
- m.iTableLeft = oOffset.left + s.nTable.parentNode.scrollLeft;
390
- m.iTableTop = oOffset.top + iParentScrollTop;
391
- m.iTableRight = m.iTableLeft + m.iTableWidth;
392
- m.iTableRight = FixedHeader.oDoc.iWidth - m.iTableLeft - m.iTableWidth;
393
- m.iTableBottom = FixedHeader.oDoc.iHeight - m.iTableTop - m.iTableHeight;
194
+ this._positions();
195
+ this._scroll( true );
394
196
  },
395
197
 
396
- /*
397
- * Function: _fnSumScroll
398
- * Purpose: Sum node parameters all the way to the top
399
- * Returns: int: sum
400
- * Inputs: node:n - node to consider
401
- * string:side - scrollTop or scrollLeft
402
- */
403
- _fnSumScroll: function ( n, side )
404
- {
405
- var i = n[side];
406
- while ( n = n.parentNode )
407
- {
408
- if ( n.nodeName == 'HTML' || n.nodeName == 'BODY' )
409
- {
410
- break;
411
- }
412
- i = n[side];
413
- }
414
- return i;
415
- },
416
198
 
417
- /*
418
- * Function: _fnUpdatePositions
419
- * Purpose: Loop over the fixed elements for this table and update their positions
420
- * Returns: -
421
- * Inputs: -
199
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
200
+ * Constructor
422
201
  */
423
- _fnUpdatePositions: function ()
424
- {
425
- var s = this.fnGetSettings();
426
- this._fnMeasure();
427
-
428
- for ( var i=0, iLen=s.aoCache.length ; i<iLen ; i++ )
429
- {
430
- if ( s.aoCache[i].sType == "fixedHeader" )
431
- {
432
- this._fnScrollFixedHeader( s.aoCache[i] );
433
- }
434
- else if ( s.aoCache[i].sType == "fixedFooter" )
435
- {
436
- this._fnScrollFixedFooter( s.aoCache[i] );
437
- }
438
- else if ( s.aoCache[i].sType == "fixedLeft" )
439
- {
440
- this._fnScrollHorizontalLeft( s.aoCache[i] );
441
- }
442
- else
443
- {
444
- this._fnScrollHorizontalRight( s.aoCache[i] );
445
- }
446
- }
447
- },
448
-
449
- /*
450
- * Function: _fnUpdateClones
451
- * Purpose: Loop over the fixed elements for this table and call their cloning functions
452
- * Returns: -
453
- * Inputs: -
202
+
203
+ /**
204
+ * FixedHeader constructor - adding the required event listeners and
205
+ * simple initialisation
206
+ *
207
+ * @private
454
208
  */
455
- _fnUpdateClones: function ( full )
209
+ _constructor: function ()
456
210
  {
457
- var s = this.fnGetSettings();
211
+ var that = this;
212
+ var dt = this.s.dt;
458
213
 
459
- if ( full ) {
460
- // This is a little bit of a hack to force a full clone draw. When
461
- // `full` is set to true, we want to reclone the source elements,
462
- // regardless of the clone-on-draw settings
463
- s.bInitComplete = false;
464
- }
214
+ $(window)
215
+ .on( 'scroll'+this.s.namespace, function () {
216
+ that._scroll();
217
+ } )
218
+ .on( 'resize'+this.s.namespace, function () {
219
+ that.s.position.windowHeight = $(window).height();
220
+ that.update();
221
+ } );
465
222
 
466
- for ( var i=0, iLen=s.aoCache.length ; i<iLen ; i++ )
467
- {
468
- s.aoCache[i].fnClone.call( this, s.aoCache[i] );
469
- }
223
+ dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc', function () {
224
+ that.update();
225
+ } );
470
226
 
471
- if ( full ) {
472
- s.bInitComplete = true;
473
- }
227
+ dt.on( 'destroy.dtfc', function () {
228
+ dt.off( '.dtfc' );
229
+ $(window).off( that.s.namespace );
230
+ } );
231
+
232
+ this._positions();
233
+ this._scroll();
474
234
  },
475
235
 
476
236
 
477
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
478
- * Scrolling functions
237
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
238
+ * Private methods
479
239
  */
480
240
 
481
- /*
482
- * Function: _fnScrollHorizontalLeft
483
- * Purpose: Update the positioning of the scrolling elements
484
- * Returns: -
485
- * Inputs: object:oCache - the cached values for this fixed element
241
+ /**
242
+ * Clone a fixed item to act as a place holder for the original element
243
+ * which is moved into a clone of the table element, and moved around the
244
+ * document to give the fixed effect.
245
+ *
246
+ * @param {string} item 'header' or 'footer'
247
+ * @param {boolean} force Force the clone to happen, or allow automatic
248
+ * decision (reuse existing if available)
249
+ * @private
486
250
  */
487
- _fnScrollHorizontalRight: function ( oCache )
251
+ _clone: function ( item, force )
488
252
  {
489
- var
490
- s = this.fnGetSettings(),
491
- oMes = s.oMes,
492
- oWin = FixedHeader.oWin,
493
- oDoc = FixedHeader.oDoc,
494
- nTable = oCache.nWrapper,
495
- iFixedWidth = $(nTable).outerWidth();
496
-
497
- if ( oWin.iScrollRight < oMes.iTableRight )
498
- {
499
- /* Fully right aligned */
500
- this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
501
- this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
502
- this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft+oMes.iTableWidth-iFixedWidth)+"px", 'left', nTable.style );
503
- }
504
- else if ( oMes.iTableLeft < oDoc.iWidth-oWin.iScrollRight-iFixedWidth )
505
- {
506
- /* Middle */
507
- this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
508
- this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop-oWin.iScrollTop)+"px", 'top', nTable.style );
509
- this._fnUpdateCache( oCache, 'sLeft', (oWin.iWidth-iFixedWidth)+"px", 'left', nTable.style );
510
- }
511
- else
512
- {
513
- /* Fully left aligned */
514
- this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
515
- this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
516
- this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
517
- }
518
- },
253
+ var dt = this.s.dt;
254
+ var itemDom = this.dom[ item ];
255
+ var itemElement = item === 'header' ?
256
+ this.dom.thead :
257
+ this.dom.tfoot;
258
+
259
+ if ( ! force && itemDom.floating ) {
260
+ // existing floating element - reuse it
261
+ itemDom.floating.removeClass( 'fixedHeader-floating fixedHeader-locked' );
262
+ }
263
+ else {
264
+ if ( itemDom.floating ) {
265
+ itemDom.placeholder.remove();
266
+ itemDom.floating.children().detach();
267
+ itemDom.floating.remove();
268
+ }
519
269
 
520
- /*
521
- * Function: _fnScrollHorizontalLeft
522
- * Purpose: Update the positioning of the scrolling elements
523
- * Returns: -
524
- * Inputs: object:oCache - the cached values for this fixed element
525
- */
526
- _fnScrollHorizontalLeft: function ( oCache )
527
- {
528
- var
529
- s = this.fnGetSettings(),
530
- oMes = s.oMes,
531
- oWin = FixedHeader.oWin,
532
- oDoc = FixedHeader.oDoc,
533
- nTable = oCache.nWrapper,
534
- iCellWidth = $(nTable).outerWidth();
535
-
536
- if ( oWin.iScrollLeft < oMes.iTableLeft )
537
- {
538
- /* Fully left align */
539
- this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
540
- this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
541
- this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
542
- }
543
- else if ( oWin.iScrollLeft < oMes.iTableLeft+oMes.iTableWidth-iCellWidth )
544
- {
545
- this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
546
- this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop-oWin.iScrollTop)+"px", 'top', nTable.style );
547
- this._fnUpdateCache( oCache, 'sLeft', "0px", 'left', nTable.style );
548
- }
549
- else
550
- {
551
- /* Fully right align */
552
- this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
553
- this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
554
- this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft+oMes.iTableWidth-iCellWidth)+"px", 'left', nTable.style );
555
- }
556
- },
270
+ itemDom.floating = $( dt.table().node().cloneNode( false ) )
271
+ .removeAttr( 'id' )
272
+ .append( itemElement )
273
+ .appendTo( 'body' );
557
274
 
558
- /*
559
- * Function: _fnScrollFixedFooter
560
- * Purpose: Update the positioning of the scrolling elements
561
- * Returns: -
562
- * Inputs: object:oCache - the cached values for this fixed element
563
- */
564
- _fnScrollFixedFooter: function ( oCache )
565
- {
566
- var
567
- s = this.fnGetSettings(),
568
- oMes = s.oMes,
569
- oWin = FixedHeader.oWin,
570
- oDoc = FixedHeader.oDoc,
571
- nTable = oCache.nWrapper,
572
- iTheadHeight = $("thead", s.nTable).outerHeight(),
573
- iCellHeight = $(nTable).outerHeight();
574
-
575
- if ( oWin.iScrollBottom < oMes.iTableBottom )
576
- {
577
- /* Below */
578
- this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
579
- this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+oMes.iTableHeight-iCellHeight)+"px", 'top', nTable.style );
580
- this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
581
- }
582
- else if ( oWin.iScrollBottom < oMes.iTableBottom+oMes.iTableHeight-iCellHeight-iTheadHeight )
583
- {
584
- this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
585
- this._fnUpdateCache( oCache, 'sTop', (oWin.iHeight-iCellHeight)+"px", 'top', nTable.style );
586
- this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style );
587
- }
588
- else
589
- {
590
- /* Above */
591
- this._fnUpdateCache( oCache, 'sPosition', 'absolute', 'position', nTable.style );
592
- this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iCellHeight)+"px", 'top', nTable.style );
593
- this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
275
+ // Insert a fake thead/tfoot into the DataTable to stop it jumping around
276
+ itemDom.placeholder = itemElement.clone( false );
277
+ itemDom.host.append( itemDom.placeholder );
278
+
279
+ // Clone widths
280
+ this._matchWidths( itemDom.placeholder, itemDom.floating );
594
281
  }
595
282
  },
596
283
 
597
- /*
598
- * Function: _fnScrollFixedHeader
599
- * Purpose: Update the positioning of the scrolling elements
600
- * Returns: -
601
- * Inputs: object:oCache - the cached values for this fixed element
284
+ /**
285
+ * Copy widths from the cells in one element to another. This is required
286
+ * for the footer as the footer in the main table takes its sizes from the
287
+ * header columns. That isn't present in the footer so to have it still
288
+ * align correctly, the sizes need to be copied over. It is also required
289
+ * for the header when auto width is not enabled
290
+ *
291
+ * @param {jQuery} from Copy widths from
292
+ * @param {jQuery} to Copy widths to
293
+ * @private
602
294
  */
603
- _fnScrollFixedHeader: function ( oCache )
604
- {
605
- var
606
- s = this.fnGetSettings(),
607
- oMes = s.oMes,
608
- oWin = FixedHeader.oWin,
609
- oDoc = FixedHeader.oDoc,
610
- nTable = oCache.nWrapper,
611
- iTbodyHeight = 0,
612
- anTbodies = s.nTable.getElementsByTagName('tbody');
613
-
614
- for (var i = 0; i < anTbodies.length; ++i) {
615
- iTbodyHeight += anTbodies[i].offsetHeight;
616
- }
295
+ _matchWidths: function ( from, to ) {
296
+ var type = function ( name ) {
297
+ var toWidths = $(name, from)
298
+ .map( function () {
299
+ return $(this).width();
300
+ } ).toArray();
301
+
302
+ $(name, to).each( function ( i ) {
303
+ $(this).width( toWidths[i] ).css("min-width", toWidths[i] );
304
+ } );
305
+ };
617
306
 
618
- if ( oMes.iTableTop > oWin.iScrollTop + s.oOffset.top )
619
- {
620
- /* Above the table */
621
- this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
622
- this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style );
623
- this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
624
- }
625
- else if ( oWin.iScrollTop + s.oOffset.top > oMes.iTableTop+iTbodyHeight )
626
- {
627
- /* At the bottom of the table */
628
- this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style );
629
- this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iTbodyHeight)+"px", 'top', nTable.style );
630
- this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style );
631
- }
632
- else
633
- {
634
- /* In the middle of the table */
635
- this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style );
636
- this._fnUpdateCache( oCache, 'sTop', s.oOffset.top+"px", 'top', nTable.style );
637
- this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style );
638
- }
307
+ type( 'th' );
308
+ type( 'td' );
639
309
  },
640
310
 
641
- /*
642
- * Function: _fnUpdateCache
643
- * Purpose: Check the cache and update cache and value if needed
644
- * Returns: -
645
- * Inputs: object:oCache - local cache object
646
- * string:sCache - cache property
647
- * string:sSet - value to set
648
- * string:sProperty - object property to set
649
- * object:oObj - object to update
311
+ /**
312
+ * Remove assigned widths from the cells in an element. This is required
313
+ * when inserting the footer back into the main table so the size is defined
314
+ * by the header columns and also when auto width is disabled in the
315
+ * DataTable.
316
+ *
317
+ * @param {string} item The `header` or `footer`
318
+ * @private
650
319
  */
651
- _fnUpdateCache: function ( oCache, sCache, sSet, sProperty, oObj )
652
- {
653
- if ( oCache[sCache] != sSet )
654
- {
655
- oObj[sProperty] = sSet;
656
- oCache[sCache] = sSet;
320
+ _unsize: function ( item ) {
321
+ var el = this.dom[ item ].floating;
322
+
323
+ if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) {
324
+ $('th, td', el).css( 'width', '' );
657
325
  }
658
326
  },
659
327
 
660
-
661
-
662
328
  /**
663
- * Copy the classes of all child nodes from one element to another. This implies
664
- * that the two have identical structure - no error checking is performed to that
665
- * fact.
666
- * @param {element} source Node to copy classes from
667
- * @param {element} dest Node to copy classes too
329
+ * Reposition the floating elements to take account of horizontal page
330
+ * scroll
331
+ *
332
+ * @param {string} item The `header` or `footer`
333
+ * @param {int} scrollLeft Document scrollLeft
334
+ * @private
668
335
  */
669
- _fnClassUpdate: function ( source, dest )
336
+ _horizontal: function ( item, scrollLeft )
670
337
  {
671
- var that = this;
338
+ var itemDom = this.dom[ item ];
339
+ var position = this.s.position;
340
+ var lastScrollLeft = this.s.scrollLeft;
672
341
 
673
- if ( source.nodeName.toUpperCase() === "TR" || source.nodeName.toUpperCase() === "TH" ||
674
- source.nodeName.toUpperCase() === "TD" || source.nodeName.toUpperCase() === "SPAN" )
675
- {
676
- dest.className = source.className;
677
- }
342
+ if ( itemDom.floating && lastScrollLeft[ item ] !== scrollLeft ) {
343
+ itemDom.floating.css( 'left', position.left - scrollLeft );
678
344
 
679
- $(source).children().each( function (i) {
680
- that._fnClassUpdate( $(source).children()[i], $(dest).children()[i] );
681
- } );
345
+ lastScrollLeft[ item ] = scrollLeft;
346
+ }
682
347
  },
683
348
 
684
-
685
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
686
- * Cloning functions
687
- */
688
-
689
- /*
690
- * Function: _fnCloneThead
691
- * Purpose: Clone the thead element
692
- * Returns: -
693
- * Inputs: object:oCache - the cached values for this fixed element
349
+ /**
350
+ * Change from one display mode to another. Each fixed item can be in one
351
+ * of:
352
+ *
353
+ * * `in-place` - In the main DataTable
354
+ * * `in` - Floating over the DataTable
355
+ * * `below` - (Header only) Fixed to the bottom of the table body
356
+ * * `above` - (Footer only) Fixed to the top of the table body
357
+ *
358
+ * @param {string} mode Mode that the item should be shown in
359
+ * @param {string} item 'header' or 'footer'
360
+ * @param {boolean} forceChange Force a redraw of the mode, even if already
361
+ * in that mode.
362
+ * @private
694
363
  */
695
- _fnCloneThead: function ( oCache )
364
+ _modeChange: function ( mode, item, forceChange )
696
365
  {
697
- var s = this.fnGetSettings();
698
- var nTable = oCache.nNode;
366
+ var dt = this.s.dt;
367
+ var itemDom = this.dom[ item ];
368
+ var position = this.s.position;
369
+
370
+ if ( mode === 'in-place' ) {
371
+ // Insert the header back into the table's real header
372
+ if ( itemDom.placeholder ) {
373
+ itemDom.placeholder.remove();
374
+ itemDom.placeholder = null;
375
+ }
699
376
 
700
- if ( s.bInitComplete && !s.oCloneOnDraw.top )
701
- {
702
- this._fnClassUpdate( $('thead', s.nTable)[0], $('thead', nTable)[0] );
703
- return;
704
- }
377
+ this._unsize( item );
705
378
 
706
- /* Set the wrapper width to match that of the cloned table */
707
- var iDtWidth = $(s.nTable).outerWidth();
708
- oCache.nWrapper.style.width = iDtWidth+"px";
709
- nTable.style.width = iDtWidth+"px";
379
+ itemDom.host.append( item === 'header' ?
380
+ this.dom.thead :
381
+ this.dom.tfoot
382
+ );
710
383
 
711
- /* Remove any children the cloned table has */
712
- while ( nTable.childNodes.length > 0 )
713
- {
714
- $('thead th', nTable).unbind( 'click' );
715
- nTable.removeChild( nTable.childNodes[0] );
384
+ if ( itemDom.floating ) {
385
+ itemDom.floating.remove();
386
+ itemDom.floating = null;
387
+ }
716
388
  }
389
+ else if ( mode === 'in' ) {
390
+ // Remove the header from the read header and insert into a fixed
391
+ // positioned floating table clone
392
+ this._clone( item, forceChange );
717
393
 
718
- /* Clone the DataTables header */
719
- var nThead = $('thead', s.nTable).clone(true)[0];
720
- nTable.appendChild( nThead );
721
-
722
- /* Copy the widths across - apparently a clone isn't good enough for this */
723
- var a = [];
724
- var b = [];
394
+ itemDom.floating
395
+ .addClass( 'fixedHeader-floating' )
396
+ .css( item === 'header' ? 'top' : 'bottom', this.c[item+'Offset'] )
397
+ .css( 'left', position.left+'px' )
398
+ .css( 'width', position.width+'px' );
725
399
 
726
- $("thead>tr th", s.nTable).each( function (i) {
727
- a.push( $(this).width() );
728
- } );
729
-
730
- $("thead>tr td", s.nTable).each( function (i) {
731
- b.push( $(this).width() );
732
- } );
733
-
734
- $("thead>tr th", s.nTable).each( function (i) {
735
- $("thead>tr th:eq("+i+")", nTable).width( a[i] );
736
- $(this).width( a[i] );
737
- } );
738
-
739
- $("thead>tr td", s.nTable).each( function (i) {
740
- $("thead>tr td:eq("+i+")", nTable).width( b[i] );
741
- $(this).width( b[i] );
742
- } );
743
-
744
- // Stop DataTables 1.9 from putting a focus ring on the headers when
745
- // clicked to sort
746
- $('th.sorting, th.sorting_desc, th.sorting_asc', nTable).bind( 'click', function () {
747
- this.blur();
748
- } );
749
- },
750
-
751
- /*
752
- * Function: _fnCloneTfoot
753
- * Purpose: Clone the tfoot element
754
- * Returns: -
755
- * Inputs: object:oCache - the cached values for this fixed element
756
- */
757
- _fnCloneTfoot: function ( oCache )
758
- {
759
- var s = this.fnGetSettings();
760
- var nTable = oCache.nNode;
761
-
762
- /* Set the wrapper width to match that of the cloned table */
763
- oCache.nWrapper.style.width = $(s.nTable).outerWidth()+"px";
764
-
765
- /* Remove any children the cloned table has */
766
- while ( nTable.childNodes.length > 0 )
767
- {
768
- nTable.removeChild( nTable.childNodes[0] );
400
+ if ( item === 'footer' ) {
401
+ itemDom.floating.css( 'top', '' );
402
+ }
769
403
  }
404
+ else if ( mode === 'below' ) { // only used for the header
405
+ // Fix the position of the floating header at base of the table body
406
+ this._clone( item, forceChange );
770
407
 
771
- /* Clone the DataTables footer */
772
- var nTfoot = $('tfoot', s.nTable).clone(true)[0];
773
- nTable.appendChild( nTfoot );
408
+ itemDom.floating
409
+ .addClass( 'fixedHeader-locked' )
410
+ .css( 'top', position.tfootTop - position.theadHeight )
411
+ .css( 'left', position.left+'px' )
412
+ .css( 'width', position.width+'px' );
413
+ }
414
+ else if ( mode === 'above' ) { // only used for the footer
415
+ // Fix the position of the floating footer at top of the table body
416
+ this._clone( item, forceChange );
774
417
 
775
- /* Copy the widths across - apparently a clone isn't good enough for this */
776
- $("tfoot:eq(0)>tr th", s.nTable).each( function (i) {
777
- $("tfoot:eq(0)>tr th:eq("+i+")", nTable).width( $(this).width() );
778
- } );
418
+ itemDom.floating
419
+ .addClass( 'fixedHeader-locked' )
420
+ .css( 'top', position.tbodyTop )
421
+ .css( 'left', position.left+'px' )
422
+ .css( 'width', position.width+'px' );
423
+ }
779
424
 
780
- $("tfoot:eq(0)>tr td", s.nTable).each( function (i) {
781
- $("tfoot:eq(0)>tr td:eq("+i+")", nTable).width( $(this).width() );
782
- } );
425
+ this.s.scrollLeft.header = -1;
426
+ this.s.scrollLeft.footer = -1;
427
+ this.s[item+'Mode'] = mode;
783
428
  },
784
429
 
785
- /*
786
- * Function: _fnCloneTLeft
787
- * Purpose: Clone the left column(s)
788
- * Returns: -
789
- * Inputs: object:oCache - the cached values for this fixed element
430
+ /**
431
+ * Cache the positional information that is required for the mode
432
+ * calculations that FixedHeader performs.
433
+ *
434
+ * @private
790
435
  */
791
- _fnCloneTLeft: function ( oCache )
436
+ _positions: function ()
792
437
  {
793
- var s = this.fnGetSettings();
794
- var nTable = oCache.nNode;
795
- var nBody = $('tbody', s.nTable)[0];
796
-
797
- /* Remove any children the cloned table has */
798
- while ( nTable.childNodes.length > 0 )
799
- {
800
- nTable.removeChild( nTable.childNodes[0] );
801
- }
802
-
803
- /* Is this the most efficient way to do this - it looks horrible... */
804
- nTable.appendChild( $("thead", s.nTable).clone(true)[0] );
805
- nTable.appendChild( $("tbody", s.nTable).clone(true)[0] );
806
- if ( s.bFooter )
807
- {
808
- nTable.appendChild( $("tfoot", s.nTable).clone(true)[0] );
809
- }
810
-
811
- /* Remove unneeded cells */
812
- var sSelector = 'gt(' + (oCache.iCells - 1) + ')';
813
- $('thead tr', nTable).each( function (k) {
814
- $('th:' + sSelector, this).remove();
815
- } );
816
-
817
- $('tfoot tr', nTable).each( function (k) {
818
- $('th:' + sSelector, this).remove();
819
- } );
820
-
821
- $('tbody tr', nTable).each( function (k) {
822
- $('td:' + sSelector, this).remove();
823
- } );
824
-
825
- this.fnEqualiseHeights( 'thead', nBody.parentNode, nTable );
826
- this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable );
827
- this.fnEqualiseHeights( 'tfoot', nBody.parentNode, nTable );
828
-
829
- var iWidth = 0;
830
- for (var i = 0; i < oCache.iCells; i++) {
831
- iWidth += $('thead tr th:eq(' + i + ')', s.nTable).outerWidth();
438
+ var dt = this.s.dt;
439
+ var table = dt.table();
440
+ var position = this.s.position;
441
+ var dom = this.dom;
442
+ var tableNode = $(table.node());
443
+
444
+ // Need to use the header and footer that are in the main table,
445
+ // regardless of if they are clones, since they hold the positions we
446
+ // want to measure from
447
+ var thead = tableNode.children('thead');
448
+ var tfoot = tableNode.children('tfoot');
449
+ var tbody = dom.tbody;
450
+
451
+ position.visible = tableNode.is(':visible');
452
+ position.width = tableNode.outerWidth();
453
+ position.left = tableNode.offset().left;
454
+ position.theadTop = thead.offset().top;
455
+ position.tbodyTop = tbody.offset().top;
456
+ position.theadHeight = position.tbodyTop - position.theadTop;
457
+
458
+ if ( tfoot.length ) {
459
+ position.tfootTop = tfoot.offset().top;
460
+ position.tfootBottom = position.tfootTop + tfoot.outerHeight();
461
+ position.tfootHeight = position.tfootBottom - position.tfootTop;
462
+ }
463
+ else {
464
+ position.tfootTop = position.tbodyTop + tbody.outerHeight();
465
+ position.tfootBottom = position.tfootTop;
466
+ position.tfootHeight = position.tfootTop;
832
467
  }
833
- nTable.style.width = iWidth+"px";
834
- oCache.nWrapper.style.width = iWidth+"px";
835
468
  },
836
469
 
837
- /*
838
- * Function: _fnCloneTRight
839
- * Purpose: Clone the right most column(s)
840
- * Returns: -
841
- * Inputs: object:oCache - the cached values for this fixed element
470
+
471
+ /**
472
+ * Mode calculation - determine what mode the fixed items should be placed
473
+ * into.
474
+ *
475
+ * @param {boolean} forceChange Force a redraw of the mode, even if already
476
+ * in that mode.
477
+ * @private
842
478
  */
843
- _fnCloneTRight: function ( oCache )
479
+ _scroll: function ( forceChange )
844
480
  {
845
- var s = this.fnGetSettings();
846
- var nBody = $('tbody', s.nTable)[0];
847
- var nTable = oCache.nNode;
848
- var iCols = $('tbody tr:eq(0) td', s.nTable).length;
849
-
850
- /* Remove any children the cloned table has */
851
- while ( nTable.childNodes.length > 0 )
852
- {
853
- nTable.removeChild( nTable.childNodes[0] );
854
- }
481
+ var windowTop = $(document).scrollTop();
482
+ var windowLeft = $(document).scrollLeft();
483
+ var position = this.s.position;
484
+ var headerMode, footerMode;
855
485
 
856
- /* Is this the most efficient way to do this - it looks horrible... */
857
- nTable.appendChild( $("thead", s.nTable).clone(true)[0] );
858
- nTable.appendChild( $("tbody", s.nTable).clone(true)[0] );
859
- if ( s.bFooter )
860
- {
861
- nTable.appendChild( $("tfoot", s.nTable).clone(true)[0] );
486
+ if ( ! this.s.enable ) {
487
+ return;
862
488
  }
863
- $('thead tr th:lt('+(iCols-oCache.iCells)+')', nTable).remove();
864
- $('tfoot tr th:lt('+(iCols-oCache.iCells)+')', nTable).remove();
865
489
 
866
- /* Remove unneeded cells */
867
- $('tbody tr', nTable).each( function (k) {
868
- $('td:lt('+(iCols-oCache.iCells)+')', this).remove();
869
- } );
490
+ if ( this.c.header ) {
491
+ if ( ! position.visible || windowTop <= position.theadTop - this.c.headerOffset ) {
492
+ headerMode = 'in-place';
493
+ }
494
+ else if ( windowTop <= position.tfootTop - position.theadHeight - this.c.headerOffset ) {
495
+ headerMode = 'in';
496
+ }
497
+ else {
498
+ headerMode = 'below';
499
+ }
870
500
 
871
- this.fnEqualiseHeights( 'thead', nBody.parentNode, nTable );
872
- this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable );
873
- this.fnEqualiseHeights( 'tfoot', nBody.parentNode, nTable );
501
+ if ( forceChange || headerMode !== this.s.headerMode ) {
502
+ this._modeChange( headerMode, 'header', forceChange );
503
+ }
874
504
 
875
- var iWidth = 0;
876
- for (var i = 0; i < oCache.iCells; i++) {
877
- iWidth += $('thead tr th:eq('+(iCols-1-i)+')', s.nTable).outerWidth();
505
+ this._horizontal( 'header', windowLeft );
878
506
  }
879
- nTable.style.width = iWidth+"px";
880
- oCache.nWrapper.style.width = iWidth+"px";
881
- },
882
507
 
883
-
884
- /**
885
- * Equalise the heights of the rows in a given table node in a cross browser way. Note that this
886
- * is more or less lifted as is from FixedColumns
887
- * @method fnEqualiseHeights
888
- * @returns void
889
- * @param {string} parent Node type - thead, tbody or tfoot
890
- * @param {element} original Original node to take the heights from
891
- * @param {element} clone Copy the heights to
892
- * @private
893
- */
894
- "fnEqualiseHeights": function ( parent, original, clone )
895
- {
896
- var that = this;
897
- var originals = $(parent +' tr', original);
898
- var height;
899
-
900
- $(parent+' tr', clone).each( function (k) {
901
- height = originals.eq( k ).css('height');
902
-
903
- // This is nasty :-(. IE has a sub-pixel error even when setting
904
- // the height below (the Firefox fix) which causes the fixed column
905
- // to go out of alignment. Need to add a pixel before the assignment
906
- // Can this be feature detected? Not sure how...
907
- if ( navigator.appName == 'Microsoft Internet Explorer' ) {
908
- height = parseInt( height, 10 ) + 1;
508
+ if ( this.c.footer && this.dom.tfoot.length ) {
509
+ if ( ! position.visible || windowTop + position.windowHeight >= position.tfootBottom + this.c.footerOffset ) {
510
+ footerMode = 'in-place';
511
+ }
512
+ else if ( position.windowHeight + windowTop > position.tbodyTop + position.tfootHeight + this.c.footerOffset ) {
513
+ footerMode = 'in';
514
+ }
515
+ else {
516
+ footerMode = 'above';
909
517
  }
910
518
 
911
- $(this).css( 'height', height );
519
+ if ( forceChange || footerMode !== this.s.footerMode ) {
520
+ this._modeChange( footerMode, 'footer', forceChange );
521
+ }
912
522
 
913
- // For Firefox to work, we need to also set the height of the
914
- // original row, to the value that we read from it! Otherwise there
915
- // is a sub-pixel rounding error
916
- originals.eq( k ).css( 'height', height );
917
- } );
523
+ this._horizontal( 'footer', windowLeft );
524
+ }
918
525
  }
919
- };
526
+ } );
920
527
 
921
528
 
922
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
923
- * Static properties and methods
924
- * We use these for speed! This information is common to all instances of FixedHeader, so no
925
- * point if having them calculated and stored for each different instance.
529
+ /**
530
+ * Version
531
+ * @type {String}
532
+ * @static
926
533
  */
534
+ FixedHeader.version = "3.1.0";
927
535
 
928
- /*
929
- * Variable: oWin
930
- * Purpose: Store information about the window positioning
931
- * Scope: FixedHeader
536
+ /**
537
+ * Defaults
538
+ * @type {Object}
539
+ * @static
932
540
  */
933
- FixedHeader.oWin = {
934
- "iScrollTop": 0,
935
- "iScrollRight": 0,
936
- "iScrollBottom": 0,
937
- "iScrollLeft": 0,
938
- "iHeight": 0,
939
- "iWidth": 0
541
+ FixedHeader.defaults = {
542
+ header: true,
543
+ footer: false,
544
+ headerOffset: 0,
545
+ footerOffset: 0
940
546
  };
941
547
 
942
- /*
943
- * Variable: oDoc
944
- * Purpose: Store information about the document size
945
- * Scope: FixedHeader
946
- */
947
- FixedHeader.oDoc = {
948
- "iHeight": 0,
949
- "iWidth": 0
950
- };
951
548
 
952
- /*
953
- * Variable: afnScroll
954
- * Purpose: Array of functions that are to be used for the scrolling components
955
- * Scope: FixedHeader
549
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
550
+ * DataTables interfaces
956
551
  */
957
- FixedHeader.afnScroll = [];
958
552
 
959
- /*
960
- * Function: fnMeasure
961
- * Purpose: Update the measurements for the window and document
962
- * Returns: -
963
- * Inputs: -
964
- */
965
- FixedHeader.fnMeasure = function ()
966
- {
967
- var
968
- jqWin = $(window),
969
- jqDoc = $(document),
970
- oWin = FixedHeader.oWin,
971
- oDoc = FixedHeader.oDoc;
972
-
973
- oDoc.iHeight = jqDoc.height();
974
- oDoc.iWidth = jqDoc.width();
975
-
976
- oWin.iHeight = jqWin.height();
977
- oWin.iWidth = jqWin.width();
978
- oWin.iScrollTop = jqWin.scrollTop();
979
- oWin.iScrollLeft = jqWin.scrollLeft();
980
- oWin.iScrollRight = oDoc.iWidth - oWin.iScrollLeft - oWin.iWidth;
981
- oWin.iScrollBottom = oDoc.iHeight - oWin.iScrollTop - oWin.iHeight;
982
- };
553
+ // Attach for constructor access
554
+ $.fn.dataTable.FixedHeader = FixedHeader;
555
+ $.fn.DataTable.FixedHeader = FixedHeader;
983
556
 
984
557
 
985
- FixedHeader.version = "2.1.2";
558
+ // DataTables creation - check if the FixedHeader option has been defined on the
559
+ // table and if so, initialise
560
+ $(document).on( 'init.dt.dtb', function (e, settings, json) {
561
+ if ( e.namespace !== 'dt' ) {
562
+ return;
563
+ }
986
564
 
565
+ var opts = settings.oInit.fixedHeader || DataTable.defaults.fixedHeader;
987
566
 
988
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
989
- * Global processing
990
- */
567
+ if ( opts && ! settings._fixedHeader ) {
568
+ new FixedHeader( settings, opts );
569
+ }
570
+ } );
991
571
 
992
- /*
993
- * Just one 'scroll' event handler in FixedHeader, which calls the required components. This is
994
- * done as an optimisation, to reduce calculation and proagation time
995
- */
996
- $(window).scroll( function () {
997
- FixedHeader.fnMeasure();
572
+ // DataTables API methods
573
+ DataTable.Api.register( 'fixedHeader()', function () {} );
998
574
 
999
- for ( var i=0, iLen=FixedHeader.afnScroll.length ; i<iLen ; i++ ) {
1000
- FixedHeader.afnScroll[i]();
1001
- }
575
+ DataTable.Api.register( 'fixedHeader.adjust()', function () {
576
+ return this.iterator( 'table', function ( ctx ) {
577
+ var fh = ctx._fixedHeader;
578
+
579
+ if ( fh ) {
580
+ fh.update();
581
+ }
582
+ } );
1002
583
  } );
1003
584
 
585
+ DataTable.Api.register( 'fixedHeader.enable()', function ( flag ) {
586
+ return this.iterator( 'table', function ( ctx ) {
587
+ var fh = ctx._fixedHeader;
1004
588
 
1005
- $.fn.dataTable.FixedHeader = FixedHeader;
1006
- $.fn.DataTable.FixedHeader = FixedHeader;
589
+ if ( fh ) {
590
+ fh.enable( flag !== undefined ? flag : true );
591
+ }
592
+ } );
593
+ } );
1007
594
 
595
+ DataTable.Api.register( 'fixedHeader.disable()', function ( ) {
596
+ return this.iterator( 'table', function ( ctx ) {
597
+ var fh = ctx._fixedHeader;
1008
598
 
1009
- return FixedHeader;
1010
- }; // /factory
599
+ if ( fh ) {
600
+ fh.enable( false );
601
+ }
602
+ } );
603
+ } );
1011
604
 
605
+ $.each( ['header', 'footer'], function ( i, el ) {
606
+ DataTable.Api.register( 'fixedHeader.'+el+'Offset()', function ( offset ) {
607
+ var ctx = this.context;
1012
608
 
1013
- // Define as an AMD module if possible
1014
- if ( typeof define === 'function' && define.amd ) {
1015
- define( ['jquery', 'datatables'], factory );
1016
- }
1017
- else if ( typeof exports === 'object' ) {
1018
- // Node/CommonJS
1019
- factory( require('jquery'), require('datatables') );
1020
- }
1021
- else if ( jQuery && !jQuery.fn.dataTable.FixedHeader ) {
1022
- // Otherwise simply initialise as normal, stopping multiple evaluation
1023
- factory( jQuery, jQuery.fn.dataTable );
1024
- }
609
+ if ( offset === undefined ) {
610
+ return ctx.length && ctx[0]._fixedHeader ?
611
+ ctx[0]._fixedHeader[el +'Offset']() :
612
+ undefined;
613
+ }
1025
614
 
615
+ return this.iterator( 'table', function ( ctx ) {
616
+ var fh = ctx._fixedHeader;
617
+
618
+ if ( fh ) {
619
+ fh[ el +'Offset' ]( offset );
620
+ }
621
+ } );
622
+ } );
623
+ } );
1026
624
 
1027
- })(window, document);
1028
625
 
626
+ return FixedHeader;
627
+ }));