jquery-datatables-rails 2.2.1 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,11 @@
1
- /*! Scroller 1.2.1
2
- * 2011-2014 SpryMedia Ltd - datatables.net/license
1
+ /*! Scroller 1.2.2
2
+ * ©2011-2014 SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
5
5
  /**
6
6
  * @summary Scroller
7
7
  * @description Virtual rendering for DataTables
8
- * @version 1.2.1
8
+ * @version 1.2.2
9
9
  * @file dataTables.scroller.js
10
10
  * @author SpryMedia Ltd (www.sprymedia.co.uk)
11
11
  * @contact www.sprymedia.co.uk/contact
@@ -126,7 +126,7 @@ var Scroller = function ( oDTSettings, oOpts ) {
126
126
 
127
127
  /**
128
128
  * Pixel location of the boundary for when the next data set should be loaded and drawn
129
- * when scrolling down the way. Note that this is actually caluated as the offset from
129
+ * when scrolling down the way. Note that this is actually calculated as the offset from
130
130
  * the top.
131
131
  * @type int
132
132
  * @default 0
@@ -187,7 +187,8 @@ var Scroller = function ( oDTSettings, oOpts ) {
187
187
  },
188
188
 
189
189
  topRowFloat: 0,
190
- scrollDrawDiff: null
190
+ scrollDrawDiff: null,
191
+ loaderVisible: false
191
192
  };
192
193
 
193
194
  // @todo The defaults should extend a `c` property and the internal settings
@@ -204,9 +205,10 @@ var Scroller = function ( oDTSettings, oOpts ) {
204
205
  *
205
206
  */
206
207
  this.dom = {
207
- "force": document.createElement('div'),
208
+ "force": document.createElement('div'),
208
209
  "scroller": null,
209
- "table": null
210
+ "table": null,
211
+ "loader": null
210
212
  };
211
213
 
212
214
  /* Attach the instance to the DataTables instance so it can be accessed */
@@ -302,7 +304,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
302
304
  /**
303
305
  * Calculate the row number that will be found at the given pixel position (y-scroll)
304
306
  * @param {int} iRow Row index to scroll to
305
- * @param {bool} [bAnimate=true] Animate the transision or not
307
+ * @param {bool} [bAnimate=true] Animate the transition or not
306
308
  * @returns {void}
307
309
  * @example
308
310
  * $(document).ready(function() {
@@ -354,7 +356,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
354
356
  // the final scroll event fired
355
357
  setTimeout( function () {
356
358
  that.s.ani = false;
357
- }, 0 );
359
+ }, 25 );
358
360
  } );
359
361
  }
360
362
  else
@@ -370,7 +372,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
370
372
  * rendering. This can be particularly useful if the table is initially
371
373
  * drawn in a hidden element - for example in a tab.
372
374
  * @param {bool} [bRedraw=true] Redraw the table automatically after the recalculation, with
373
- * the new dimentions forming the basis for the draw.
375
+ * the new dimensions forming the basis for the draw.
374
376
  * @returns {void}
375
377
  * @example
376
378
  * $(document).ready(function() {
@@ -406,7 +408,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
406
408
  this.s.viewportRows = parseInt( heights.viewport / heights.row, 10 )+1;
407
409
  this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer;
408
410
 
409
- if ( typeof bRedraw == 'undefined' || bRedraw )
411
+ if ( bRedraw === undefined || bRedraw )
410
412
  {
411
413
  this.s.dt.oInstance.fnDraw();
412
414
  }
@@ -456,9 +458,12 @@ Scroller.prototype = /** @lends Scroller.prototype */{
456
458
  // Add a 'loading' indicator
457
459
  if ( this.s.loadingIndicator )
458
460
  {
461
+ this.dom.loader = $('<div class="DTS_Loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>')
462
+ .css('display', 'none');
463
+
459
464
  $(this.dom.scroller.parentNode)
460
465
  .css('position', 'relative')
461
- .append('<div class="DTS_Loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>');
466
+ .append( this.dom.loader );
462
467
  }
463
468
 
464
469
  /* Initial size calculations */
@@ -468,12 +473,19 @@ Scroller.prototype = /** @lends Scroller.prototype */{
468
473
  }
469
474
  this.fnMeasure( false );
470
475
 
471
- /* Scrolling callback to see if a page change is needed */
472
- $(this.dom.scroller).on( 'scroll.DTS', function () {
476
+ /* Scrolling callback to see if a page change is needed - use a throttled
477
+ * function for the save save callback so we aren't hitting it on every
478
+ * scroll
479
+ */
480
+ this.s.ingnoreScroll = true;
481
+ this.s.stateSaveThrottle = this.s.dt.oApi._fnThrottle( function () {
482
+ that.s.dt.oApi._fnSaveState( that.s.dt );
483
+ }, 500 );
484
+ $(this.dom.scroller).on( 'scroll.DTS', function (e) {
473
485
  that._fnScroll.call( that );
474
486
  } );
475
487
 
476
- /* In iOS we catch the touchstart event incase the user tries to scroll
488
+ /* In iOS we catch the touchstart event in case the user tries to scroll
477
489
  * while the display is already scrolling
478
490
  */
479
491
  $(this.dom.scroller).on('touchstart.DTS', function () {
@@ -492,6 +504,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
492
504
 
493
505
  /* On resize, update the information element, since the number of rows shown might change */
494
506
  $(window).on( 'resize.DTS', function () {
507
+ that.fnMeasure( false );
495
508
  that._fnInfo();
496
509
  } );
497
510
 
@@ -504,12 +517,18 @@ Scroller.prototype = /** @lends Scroller.prototype */{
504
517
  */
505
518
  if(initialStateSave && that.s.dt.oLoadedState){
506
519
  oData.iScroller = that.s.dt.oLoadedState.iScroller;
520
+ oData.iScrollerTopRow = that.s.dt.oLoadedState.iScrollerTopRow;
507
521
  initialStateSave = false;
508
522
  } else {
509
523
  oData.iScroller = that.dom.scroller.scrollTop;
524
+ oData.iScrollerTopRow = that.s.topRowFloat;
510
525
  }
511
526
  }, "Scroller_State" );
512
527
 
528
+ if ( this.s.dt.oLoadedState ) {
529
+ this.s.topRowFloat = this.s.dt.oLoadedState.iScrollerTopRow || 0;
530
+ }
531
+
513
532
  /* Destructor */
514
533
  this.s.dt.aoDestroyCallback.push( {
515
534
  "sName": "Scroller",
@@ -548,6 +567,10 @@ Scroller.prototype = /** @lends Scroller.prototype */{
548
567
  return;
549
568
  }
550
569
 
570
+ if ( this.s.ingnoreScroll ) {
571
+ return;
572
+ }
573
+
551
574
  /* If the table has been sorted or filtered, then we use the redraw that
552
575
  * DataTables as done, rather than performing our own
553
576
  */
@@ -628,10 +651,16 @@ Scroller.prototype = /** @lends Scroller.prototype */{
628
651
  else {
629
652
  draw();
630
653
  }
654
+
655
+ if ( this.dom.loader && ! this.s.loaderVisible ) {
656
+ this.dom.loader.css( 'display', 'block' );
657
+ this.s.loaderVisible = true;
658
+ }
631
659
  }
632
660
  }
633
661
 
634
662
  this.s.lastScrollTop = iScrollTop;
663
+ this.s.stateSaveThrottle();
635
664
  },
636
665
 
637
666
 
@@ -770,20 +799,15 @@ Scroller.prototype = /** @lends Scroller.prototype */{
770
799
 
771
800
  this.s.skip = false;
772
801
 
773
- // Because of the order of the DT callbacks, the info update will
774
- // take precidence over the one we want here. So a 'thread' break is
775
- // needed
776
- setTimeout( function () {
777
- that._fnInfo.call( that );
778
- }, 0 );
779
-
780
802
  // Restore the scrolling position that was saved by DataTable's state
781
803
  // saving Note that this is done on the second draw when data is Ajax
782
804
  // sourced, and the first draw when DOM soured
783
805
  if ( this.s.dt.oFeatures.bStateSave && this.s.dt.oLoadedState !== null &&
784
806
  typeof this.s.dt.oLoadedState.iScroller != 'undefined' )
785
807
  {
786
- var ajaxSourced = this.s.dt.sAjaxSource || that.s.dt.ajax ?
808
+ // A quirk of DataTables is that the draw callback will occur on an
809
+ // empty set if Ajax sourced, but not if server-side processing.
810
+ var ajaxSourced = (this.s.dt.sAjaxSource || that.s.dt.ajax) && ! this.s.dt.oFeatures.bServerSide ?
787
811
  true :
788
812
  false;
789
813
 
@@ -793,9 +817,31 @@ Scroller.prototype = /** @lends Scroller.prototype */{
793
817
  setTimeout( function () {
794
818
  $(that.dom.scroller).scrollTop( that.s.dt.oLoadedState.iScroller );
795
819
  that.s.redrawTop = that.s.dt.oLoadedState.iScroller - (heights.viewport/2);
820
+
821
+ // In order to prevent layout thrashing we need another
822
+ // small delay
823
+ setTimeout( function () {
824
+ that.s.ingnoreScroll = false;
825
+ }, 0 );
796
826
  }, 0 );
797
827
  }
798
828
  }
829
+ else {
830
+ that.s.ingnoreScroll = false;
831
+ }
832
+
833
+ // Because of the order of the DT callbacks, the info update will
834
+ // take precedence over the one we want here. So a 'thread' break is
835
+ // needed
836
+ setTimeout( function () {
837
+ that._fnInfo.call( that );
838
+ }, 0 );
839
+
840
+ // Hide the loading indicator
841
+ if ( this.dom.loader && this.s.loaderVisible ) {
842
+ this.dom.loader.css( 'display', 'none' );
843
+ this.s.loaderVisible = false;
844
+ }
799
845
  },
800
846
 
801
847
 
@@ -836,13 +882,14 @@ Scroller.prototype = /** @lends Scroller.prototype */{
836
882
  */
837
883
  "_fnCalcRowHeight": function ()
838
884
  {
839
- var origTable = this.s.dt.nTable;
885
+ var dt = this.s.dt;
886
+ var origTable = dt.nTable;
840
887
  var nTable = origTable.cloneNode( false );
841
888
  var tbody = $('<tbody/>').appendTo( nTable );
842
889
  var container = $(
843
- '<div class="'+this.s.dt.oClasses.sWrapper+' DTS">'+
844
- '<div class="'+this.s.dt.oClasses.sScrollWrapper+'">'+
845
- '<div class="'+this.s.dt.oClasses.sScrollBody+'"></div>'+
890
+ '<div class="'+dt.oClasses.sWrapper+' DTS">'+
891
+ '<div class="'+dt.oClasses.sScrollWrapper+'">'+
892
+ '<div class="'+dt.oClasses.sScrollBody+'"></div>'+
846
893
  '</div>'+
847
894
  '</div>'
848
895
  );
@@ -854,9 +901,19 @@ Scroller.prototype = /** @lends Scroller.prototype */{
854
901
  tbody.append( '<tr><td>&nbsp;</td></tr>' );
855
902
  }
856
903
 
857
- $('div.'+this.s.dt.oClasses.sScrollBody, container).append( nTable );
904
+ $('div.'+dt.oClasses.sScrollBody, container).append( nTable );
858
905
 
859
- container.appendTo( this.s.dt.nHolding );
906
+ var appendTo;
907
+ if (dt._bInitComplete) {
908
+ appendTo = origTable.parentNode;
909
+ } else {
910
+ if (!this.s.dt.nHolding) {
911
+ this.s.dt.nHolding = $( '<div></div>' ).insertBefore( this.s.dt.nTable );
912
+ }
913
+ appendTo = this.s.dt.nHolding;
914
+ }
915
+
916
+ container.appendTo( appendTo );
860
917
  this.s.heights.row = $('tr', tbody).eq(1).outerHeight();
861
918
  container.remove();
862
919
  },
@@ -878,6 +935,7 @@ Scroller.prototype = /** @lends Scroller.prototype */{
878
935
 
879
936
  var
880
937
  dt = this.s.dt,
938
+ language = dt.oLanguage,
881
939
  iScrollTop = this.dom.scroller.scrollTop,
882
940
  iStart = Math.floor( this.fnPixelsToRow(iScrollTop, false, this.s.ani)+1 ),
883
941
  iMax = dt.fnRecordsTotal(),
@@ -894,34 +952,45 @@ Scroller.prototype = /** @lends Scroller.prototype */{
894
952
  dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
895
953
  {
896
954
  /* Empty record set */
897
- sOut = dt.oLanguage.sInfoEmpty+ dt.oLanguage.sInfoPostFix;
955
+ sOut = language.sInfoEmpty+ language.sInfoPostFix;
898
956
  }
899
957
  else if ( dt.fnRecordsDisplay() === 0 )
900
958
  {
901
- /* Rmpty record set after filtering */
902
- sOut = dt.oLanguage.sInfoEmpty +' '+
903
- dt.oLanguage.sInfoFiltered.replace('_MAX_', sMax)+
904
- dt.oLanguage.sInfoPostFix;
959
+ /* Empty record set after filtering */
960
+ sOut = language.sInfoEmpty +' '+
961
+ language.sInfoFiltered.replace('_MAX_', sMax)+
962
+ language.sInfoPostFix;
905
963
  }
906
964
  else if ( dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
907
965
  {
908
966
  /* Normal record set */
909
- sOut = dt.oLanguage.sInfo.
967
+ sOut = language.sInfo.
910
968
  replace('_START_', sStart).
911
969
  replace('_END_', sEnd).
970
+ replace('_MAX_', sMax).
912
971
  replace('_TOTAL_', sTotal)+
913
- dt.oLanguage.sInfoPostFix;
972
+ language.sInfoPostFix;
914
973
  }
915
974
  else
916
975
  {
917
976
  /* Record set after filtering */
918
- sOut = dt.oLanguage.sInfo.
977
+ sOut = language.sInfo.
919
978
  replace('_START_', sStart).
920
979
  replace('_END_', sEnd).
980
+ replace('_MAX_', sMax).
921
981
  replace('_TOTAL_', sTotal) +' '+
922
- dt.oLanguage.sInfoFiltered.replace('_MAX_',
923
- dt.fnFormatNumber(dt.fnRecordsTotal()))+
924
- dt.oLanguage.sInfoPostFix;
982
+ language.sInfoFiltered.replace(
983
+ '_MAX_',
984
+ dt.fnFormatNumber(dt.fnRecordsTotal())
985
+ )+
986
+ language.sInfoPostFix;
987
+ }
988
+
989
+ var callback = language.fnInfoCallback;
990
+ if ( callback ) {
991
+ sOut = callback.call( dt.oInstance,
992
+ dt, iStart, iEnd, iMax, iTotal, sOut
993
+ );
925
994
  }
926
995
 
927
996
  var n = dt.aanFeatures.i;
@@ -1086,7 +1155,7 @@ Scroller.oDefaults = Scroller.defaults;
1086
1155
  * @name Scroller.version
1087
1156
  * @static
1088
1157
  */
1089
- Scroller.version = "1.2.1";
1158
+ Scroller.version = "1.2.2";
1090
1159
 
1091
1160
 
1092
1161
 
@@ -1127,6 +1196,10 @@ $.fn.DataTable.Scroller = Scroller;
1127
1196
  if ( $.fn.dataTable.Api ) {
1128
1197
  var Api = $.fn.dataTable.Api;
1129
1198
 
1199
+ Api.register( 'scroller()', function () {
1200
+ return this;
1201
+ } );
1202
+
1130
1203
  Api.register( 'scroller().rowToPixels()', function ( rowIdx, intParse, virtual ) {
1131
1204
  var ctx = this.context;
1132
1205
 
@@ -1173,7 +1246,11 @@ return Scroller;
1173
1246
 
1174
1247
  // Define as an AMD module if possible
1175
1248
  if ( typeof define === 'function' && define.amd ) {
1176
- define( 'datatables-scroller', ['jquery', 'datatables'], factory );
1249
+ define( ['jquery', 'datatables'], factory );
1250
+ }
1251
+ else if ( typeof exports === 'object' ) {
1252
+ // Node/CommonJS
1253
+ factory( require('jquery'), require('datatables') );
1177
1254
  }
1178
1255
  else if ( jQuery && !jQuery.fn.dataTable.Scroller ) {
1179
1256
  // Otherwise simply initialise as normal, stopping multiple evaluation
@@ -1,4 +1,4 @@
1
- /*! TableTools 2.2.1
1
+ /*! TableTools 2.2.2
2
2
  * 2009-2014 SpryMedia Ltd - datatables.net/license
3
3
  *
4
4
  * ZeroClipboard 1.0.4
@@ -8,7 +8,7 @@
8
8
  /**
9
9
  * @summary TableTools
10
10
  * @description Tools and buttons for DataTables
11
- * @version 2.2.1
11
+ * @version 2.2.2
12
12
  * @file dataTables.tableTools.js
13
13
  * @author SpryMedia Ltd (www.sprymedia.co.uk)
14
14
  * @contact www.sprymedia.co.uk/contact
@@ -738,6 +738,8 @@ TableTools = function( oDT, oOpts )
738
738
  oOpts = {};
739
739
  }
740
740
 
741
+
742
+ TableTools._aInstances.push( this );
741
743
  this._fnConstruct( oOpts );
742
744
 
743
745
  return this;
@@ -816,6 +818,49 @@ TableTools.prototype = {
816
818
  },
817
819
 
818
820
 
821
+ /**
822
+ * Get the indexes of the selected rows
823
+ * @returns {array} List of row indexes
824
+ * @param {boolean} [filtered=false] Get only selected rows which are
825
+ * available given the filtering applied to the table. By default
826
+ * this is false - i.e. all rows, regardless of filtering are
827
+ selected.
828
+ */
829
+ "fnGetSelectedIndexes": function ( filtered )
830
+ {
831
+ var
832
+ out = [],
833
+ data = this.s.dt.aoData,
834
+ displayed = this.s.dt.aiDisplay,
835
+ i, iLen;
836
+
837
+ if ( filtered )
838
+ {
839
+ // Only consider filtered rows
840
+ for ( i=0, iLen=displayed.length ; i<iLen ; i++ )
841
+ {
842
+ if ( data[ displayed[i] ]._DTTT_selected )
843
+ {
844
+ out.push( displayed[i] );
845
+ }
846
+ }
847
+ }
848
+ else
849
+ {
850
+ // Use all rows
851
+ for ( i=0, iLen=data.length ; i<iLen ; i++ )
852
+ {
853
+ if ( data[i]._DTTT_selected )
854
+ {
855
+ out.push( i );
856
+ }
857
+ }
858
+ }
859
+
860
+ return out;
861
+ },
862
+
863
+
819
864
  /**
820
865
  * Check to see if a current row is selected or not
821
866
  * @param {Node} n TR node to check if it is currently selected or not
@@ -836,11 +881,9 @@ TableTools.prototype = {
836
881
  */
837
882
  "fnSelectAll": function ( filtered )
838
883
  {
839
- var s = this._fnGetMasterSettings();
840
-
841
- this._fnRowSelect( (filtered === true) ?
842
- s.dt.aiDisplay :
843
- s.dt.aoData
884
+ this._fnRowSelect( filtered ?
885
+ this.s.dt.aiDisplay :
886
+ this.s.dt.aoData
844
887
  );
845
888
  },
846
889
 
@@ -853,9 +896,7 @@ TableTools.prototype = {
853
896
  */
854
897
  "fnSelectNone": function ( filtered )
855
898
  {
856
- var s = this._fnGetMasterSettings();
857
-
858
- this._fnRowDeselect( this.fnGetSelected(filtered) );
899
+ this._fnRowDeselect( this.fnGetSelectedIndexes(filtered) );
859
900
  },
860
901
 
861
902
 
@@ -1212,10 +1253,14 @@ TableTools.prototype = {
1212
1253
  buttonDef = $.extend( o, buttonSet[i], true );
1213
1254
  }
1214
1255
 
1215
- wrapper.appendChild( this._fnCreateButton(
1256
+ var button = this._fnCreateButton(
1216
1257
  buttonDef,
1217
1258
  $(wrapper).hasClass(this.classes.collection.container)
1218
- ) );
1259
+ );
1260
+
1261
+ if ( button ) {
1262
+ wrapper.appendChild( button );
1263
+ }
1219
1264
  }
1220
1265
  },
1221
1266
 
@@ -1233,6 +1278,10 @@ TableTools.prototype = {
1233
1278
 
1234
1279
  if ( oConfig.sAction.match(/flash/) )
1235
1280
  {
1281
+ if ( ! this._fnHasFlash() ) {
1282
+ return false;
1283
+ }
1284
+
1236
1285
  this._fnFlashConfig( nButton, oConfig );
1237
1286
  }
1238
1287
  else if ( oConfig.sAction == "text" )
@@ -1807,6 +1856,34 @@ TableTools.prototype = {
1807
1856
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1808
1857
  * Flash button functions
1809
1858
  */
1859
+
1860
+ /**
1861
+ * Check if the Flash plug-in is available
1862
+ * @method _fnHasFlash
1863
+ * @returns {boolean} `true` if Flash available, `false` otherwise
1864
+ * @private
1865
+ */
1866
+ "_fnHasFlash": function ()
1867
+ {
1868
+ try {
1869
+ var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
1870
+ if (fo) {
1871
+ return true;
1872
+ }
1873
+ }
1874
+ catch (e) {
1875
+ if (
1876
+ navigator.mimeTypes &&
1877
+ navigator.mimeTypes['application/x-shockwave-flash'] !== undefined &&
1878
+ navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin
1879
+ ) {
1880
+ return true;
1881
+ }
1882
+ }
1883
+
1884
+ return false;
1885
+ },
1886
+
1810
1887
 
1811
1888
  /**
1812
1889
  * Configure a flash based button for interaction events
@@ -2068,15 +2145,18 @@ TableTools.prototype = {
2068
2145
  var aSelected = this.fnGetSelected();
2069
2146
  bSelectedOnly = this.s.select.type !== "none" && bSelectedOnly && aSelected.length !== 0;
2070
2147
 
2071
- var aDataIndex = dt.oInstance
2072
- .$('tr', oConfig.oSelectorOpts)
2073
- .map( function (id, row) {
2074
- // If "selected only", then ensure that the row is in the selected list
2075
- return bSelectedOnly && $.inArray( row, aSelected ) === -1 ?
2076
- null :
2077
- dt.oInstance.fnGetPosition( row );
2078
- } )
2079
- .get();
2148
+ var api = $.fn.dataTable.Api;
2149
+ var aDataIndex = api ?
2150
+ new api( dt ).rows( oConfig.oSelectorOpts ).indexes().flatten().toArray() :
2151
+ dt.oInstance
2152
+ .$('tr', oConfig.oSelectorOpts)
2153
+ .map( function (id, row) {
2154
+ // If "selected only", then ensure that the row is in the selected list
2155
+ return bSelectedOnly && $.inArray( row, aSelected ) === -1 ?
2156
+ null :
2157
+ dt.oInstance.fnGetPosition( row );
2158
+ } )
2159
+ .get();
2080
2160
 
2081
2161
  for ( j=0, jLen=aDataIndex.length ; j<jLen ; j++ )
2082
2162
  {
@@ -2226,7 +2306,7 @@ TableTools.prototype = {
2226
2306
 
2227
2307
  var n = document.createElement('div');
2228
2308
 
2229
- return sData.replace( /&([^\s]*);/g, function( match, match2 ) {
2309
+ return sData.replace( /&([^\s]*?);/g, function( match, match2 ) {
2230
2310
  if ( match.substr(1, 1) === '#' )
2231
2311
  {
2232
2312
  return String.fromCharCode( Number(match2.substr(1)) );
@@ -2724,12 +2804,12 @@ TableTools.BUTTONS = {
2724
2804
  this.fnSetText( flash, this.fnGetTableData(oConfig) );
2725
2805
  },
2726
2806
  "fnComplete": function(nButton, oConfig, flash, text) {
2727
- var
2728
- lines = text.split('\n').length,
2729
- len = this.s.dt.nTFoot === null ? lines-1 : lines-2,
2730
- plural = (len==1) ? "" : "s";
2807
+ var lines = text.split('\n').length;
2808
+ if (oConfig.bHeader) lines--;
2809
+ if (this.s.dt.nTFoot !== null && oConfig.bFooter) lines--;
2810
+ var plural = (lines==1) ? "" : "s";
2731
2811
  this.fnInfo( '<h6>Table copied</h6>'+
2732
- '<p>Copied '+len+' row'+plural+' to the clipboard.</p>',
2812
+ '<p>Copied '+lines+' row'+plural+' to the clipboard.</p>',
2733
2813
  1500
2734
2814
  );
2735
2815
  }
@@ -2974,7 +3054,7 @@ TableTools.prototype.CLASS = "TableTools";
2974
3054
  * @type String
2975
3055
  * @default See code
2976
3056
  */
2977
- TableTools.version = "2.2.1";
3057
+ TableTools.version = "2.2.2";
2978
3058
 
2979
3059
 
2980
3060
 
@@ -3017,10 +3097,7 @@ if ( typeof $.fn.dataTable == "function" &&
3017
3097
  init.tableTools || init.oTableTools || {} :
3018
3098
  {};
3019
3099
 
3020
- var oTT = new TableTools( oDTSettings.oInstance, opts );
3021
- TableTools._aInstances.push( oTT );
3022
-
3023
- return oTT.dom.container;
3100
+ return new TableTools( oDTSettings.oInstance, opts ).dom.container;
3024
3101
  },
3025
3102
  "cFeature": "T",
3026
3103
  "sFeature": "TableTools"
@@ -3072,7 +3149,11 @@ return TableTools;
3072
3149
 
3073
3150
  // Define as an AMD module if possible
3074
3151
  if ( typeof define === 'function' && define.amd ) {
3075
- define( 'datatables-tabletools', ['jquery', 'datatables'], factory );
3152
+ define( ['jquery', 'datatables'], factory );
3153
+ }
3154
+ else if ( typeof exports === 'object' ) {
3155
+ // Node/CommonJS
3156
+ factory( require('jquery'), require('datatables') );
3076
3157
  }
3077
3158
  else if ( jQuery && !jQuery.fn.dataTable.TableTools ) {
3078
3159
  // Otherwise simply initialise as normal, stopping multiple evaluation