effective_datatables 4.2.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/app/assets/javascripts/dataTables/buttons/buttons.colVis.js +4 -8
  4. data/app/assets/javascripts/dataTables/buttons/buttons.html5.js +33 -19
  5. data/app/assets/javascripts/dataTables/buttons/buttons.print.js +14 -3
  6. data/app/assets/javascripts/dataTables/buttons/dataTables.buttons.js +128 -61
  7. data/app/assets/javascripts/dataTables/dataTables.bootstrap4.js +5 -5
  8. data/app/assets/javascripts/dataTables/jquery.dataTables.js +2078 -2025
  9. data/app/assets/javascripts/dataTables/responsive/dataTables.responsive.js +46 -22
  10. data/app/assets/javascripts/effective_datatables.js +1 -0
  11. data/app/assets/javascripts/effective_datatables/bulk_actions.js.coffee +40 -41
  12. data/app/assets/javascripts/effective_datatables/events.js.coffee +1 -1
  13. data/app/assets/javascripts/effective_datatables/filters.js.coffee +1 -1
  14. data/app/assets/javascripts/effective_datatables/initialize.js.coffee +11 -33
  15. data/app/assets/javascripts/effective_datatables/inline_crud.js.coffee +158 -0
  16. data/app/assets/javascripts/effective_datatables/overrides.js.coffee +40 -0
  17. data/app/assets/javascripts/effective_datatables/reset.js.coffee +1 -1
  18. data/app/assets/stylesheets/dataTables/dataTables.bootstrap4.css +5 -1
  19. data/app/assets/stylesheets/effective_datatables/_overrides.scss +47 -1
  20. data/app/helpers/effective_datatables_helper.rb +1 -0
  21. data/app/helpers/effective_datatables_private_helper.rb +10 -1
  22. data/app/models/effective/effective_datatable/dsl/bulk_actions.rb +11 -22
  23. data/app/models/effective/effective_datatable/dsl/datatable.rb +2 -1
  24. data/app/models/effective/effective_datatable/format.rb +15 -4
  25. data/app/views/effective/datatables/_bulk_actions_column.html.haml +1 -1
  26. data/config/effective_datatables.rb +0 -13
  27. data/lib/effective_datatables.rb +0 -1
  28. data/lib/effective_datatables/version.rb +1 -1
  29. metadata +5 -5
  30. data/app/assets/javascripts/effective_datatables/overrides.js +0 -12
  31. data/app/views/effective/datatables/_actions_column.html.haml +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eafac19670114b90cdf9feeccfcc14b3e18e402c
4
- data.tar.gz: 3d5bb7ff5c3195dc153b331ea2ee5b19d9448497
3
+ metadata.gz: 49919375fe69c29a7aa70962ac8e4971dd247ee6
4
+ data.tar.gz: 40c8d03b6074458adfa7bed07aeff8cf1e3b1730
5
5
  SHA512:
6
- metadata.gz: 4af14c97a3e1189d23deb9d80f6fdde8d90234e14f7c4c8151de5904feba8c082ae54b7b888a99e38a905364368ee695ffaa183e948ef8eae7f33dd743a8f903
7
- data.tar.gz: 61c2923c00252fdaeed3d7a26b4a97c0a829e99a3a0bb5eae182f29a26ea4a1a727334c3109aa5efeb274e6ce6b9eb5ba43d5e8b43b9b95f4eb70b4cdd8c01fd
6
+ metadata.gz: 8692777bd2e49c6d073454a139d0f5aa7c3505a5724133ce41c0980ffe74851acbbe1f655546ba31ace7511a470cf3a6cee7c478f1ff8aedd19d4a9af40bd08f
7
+ data.tar.gz: e390a4f4e4b05da8a27757f13609198b738909ac2f9d90110cfc4a470df99aae997a2da70376725c9e449a737d1f490a755a1ad669c8b48de72281ece97165d4
data/README.md CHANGED
@@ -526,6 +526,7 @@ edit: true|false
526
526
  destroy: true|false
527
527
  visible: true|false
528
528
  actions_partial: :dropleft
529
+ inline: true|false
529
530
  ```
530
531
 
531
532
  Each object is checked individually for authorization.
@@ -122,14 +122,8 @@ $.extend( DataTable.ext.buttons, {
122
122
  return;
123
123
  }
124
124
 
125
- if ( typeof conf.columns === 'number' ) {
126
- conf.columns = details.mapping[ conf.columns ];
127
- }
128
-
129
- var col = dt.column( conf.columns );
130
-
131
125
  that.text( conf._columnText( dt, conf ) );
132
- that.active( col.visible() );
126
+ that.active( dt.column( conf.columns ).visible() );
133
127
  } );
134
128
 
135
129
  this.active( dt.column( conf.columns ).visible() );
@@ -149,7 +143,9 @@ $.extend( DataTable.ext.buttons, {
149
143
  var title = dt.settings()[0].aoColumns[ idx ].sTitle
150
144
  .replace(/\n/g," ") // remove new lines
151
145
  .replace(/<br\s*\/?>/gi, " ") // replace line breaks with spaces
152
- .replace( /<.*?>/g, "" ) // strip HTML
146
+ .replace(/<select(.*?)<\/select>/g, "") // remove select tags, including options text
147
+ .replace(/<!\-\-.*?\-\->/g, "") // strip HTML comments
148
+ .replace(/<.*?>/g, "") // strip HTML
153
149
  .replace(/^\s+|\s+$/g,""); // trim
154
150
 
155
151
  return conf.columnText ?
@@ -602,7 +602,9 @@ var excelStrings = {
602
602
  '<fill>'+
603
603
  '<patternFill patternType="none" />'+
604
604
  '</fill>'+
605
- '<fill/>'+ // Excel appears to use this as a dotted background regardless of values
605
+ '<fill>'+ // Excel appears to use this as a dotted background regardless of values but
606
+ '<patternFill patternType="none" />'+ // to be valid to the schema, use a patternFill
607
+ '</fill>'+
606
608
  '<fill>'+
607
609
  '<patternFill patternType="solid">'+
608
610
  '<fgColor rgb="FFD9D9D9" />'+
@@ -811,7 +813,7 @@ DataTable.ext.buttons.copyHtml5 = {
811
813
  }
812
814
 
813
815
  if ( config.customize ) {
814
- output = config.customize( output, config );
816
+ output = config.customize( output, config, dt );
815
817
  }
816
818
 
817
819
  var textarea = $('<textarea readonly/>')
@@ -923,7 +925,7 @@ DataTable.ext.buttons.csvHtml5 = {
923
925
  var charset = config.charset;
924
926
 
925
927
  if ( config.customize ) {
926
- output = config.customize( output, config );
928
+ output = config.customize( output, config, dt );
927
929
  }
928
930
 
929
931
  if ( charset !== false ) {
@@ -1031,9 +1033,15 @@ DataTable.ext.buttons.excelHtml5 = {
1031
1033
 
1032
1034
  // For null, undefined of blank cell, continue so it doesn't create the _createNode
1033
1035
  if ( row[i] === null || row[i] === undefined || row[i] === '' ) {
1034
- continue;
1036
+ if ( config.createEmptyCells === true ) {
1037
+ row[i] = '';
1038
+ }
1039
+ else {
1040
+ continue;
1041
+ }
1035
1042
  }
1036
1043
 
1044
+ var originalContent = row[i];
1037
1045
  row[i] = $.trim( row[i] );
1038
1046
 
1039
1047
  // Special number formatting options
@@ -1084,9 +1092,9 @@ DataTable.ext.buttons.excelHtml5 = {
1084
1092
  }
1085
1093
  else {
1086
1094
  // String output - replace non standard characters for text output
1087
- var text = ! row[i].replace ?
1088
- row[i] :
1089
- row[i].replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');
1095
+ var text = ! originalContent.replace ?
1096
+ originalContent :
1097
+ originalContent.replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');
1090
1098
 
1091
1099
  cell = _createNode( rels, 'c', {
1092
1100
  attr: {
@@ -1097,7 +1105,10 @@ DataTable.ext.buttons.excelHtml5 = {
1097
1105
  row: _createNode( rels, 'is', {
1098
1106
  children: {
1099
1107
  row: _createNode( rels, 't', {
1100
- text: text
1108
+ text: text,
1109
+ attr: {
1110
+ 'xml:space': 'preserve'
1111
+ }
1101
1112
  } )
1102
1113
  }
1103
1114
  } )
@@ -1127,7 +1138,7 @@ DataTable.ext.buttons.excelHtml5 = {
1127
1138
  ref: 'A'+row+':'+createCellPos(colspan)+row
1128
1139
  }
1129
1140
  } ) );
1130
- mergeCells.attr( 'count', mergeCells.attr( 'count' )+1 );
1141
+ mergeCells.attr( 'count', parseFloat(mergeCells.attr( 'count' ))+1 );
1131
1142
  $('row:eq('+(row-1)+') c', rels).attr( 's', '51' ); // centre
1132
1143
  };
1133
1144
 
@@ -1181,7 +1192,12 @@ DataTable.ext.buttons.excelHtml5 = {
1181
1192
 
1182
1193
  // Let the developer customise the document if they want to
1183
1194
  if ( config.customize ) {
1184
- config.customize( xlsx );
1195
+ config.customize( xlsx, config, dt );
1196
+ }
1197
+
1198
+ // Excel doesn't like an empty mergeCells tag
1199
+ if ( $('mergeCells', rels).children().length === 0 ) {
1200
+ $('mergeCells', rels).remove();
1185
1201
  }
1186
1202
 
1187
1203
  var jszip = _jsZip();
@@ -1226,7 +1242,9 @@ DataTable.ext.buttons.excelHtml5 = {
1226
1242
 
1227
1243
  messageTop: '*',
1228
1244
 
1229
- messageBottom: '*'
1245
+ messageBottom: '*',
1246
+
1247
+ createEmptyCells: false
1230
1248
  };
1231
1249
 
1232
1250
  //
@@ -1344,23 +1362,19 @@ DataTable.ext.buttons.pdfHtml5 = {
1344
1362
  }
1345
1363
 
1346
1364
  if ( config.customize ) {
1347
- config.customize( doc, config );
1365
+ config.customize( doc, config, dt );
1348
1366
  }
1349
1367
 
1350
1368
  var pdf = _pdfMake().createPdf( doc );
1351
1369
 
1352
1370
  if ( config.download === 'open' && ! _isDuffSafari() ) {
1353
1371
  pdf.open();
1354
- this.processing( false );
1355
1372
  }
1356
1373
  else {
1357
- pdf.getBuffer( function (buffer) {
1358
- var blob = new Blob( [buffer], {type:'application/pdf'} );
1359
-
1360
- _saveAs( blob, info.filename );
1361
- that.processing( false );
1362
- } );
1374
+ pdf.download( info.filename );
1363
1375
  }
1376
+
1377
+ this.processing( false );
1364
1378
  },
1365
1379
 
1366
1380
  title: '*',
@@ -91,12 +91,23 @@ DataTable.ext.buttons.print = {
91
91
  $.extend( {decodeEntities: false}, config.exportOptions ) // XSS protection
92
92
  );
93
93
  var exportInfo = dt.buttons.exportInfo( config );
94
+ var columnClasses = $.map( dt.settings()[0].aoColumns, function (col, key) {
95
+ return col.sClass;
96
+ } );
94
97
 
95
98
  var addRow = function ( d, tag ) {
96
99
  var str = '<tr>';
97
100
 
98
101
  for ( var i=0, ien=d.length ; i<ien ; i++ ) {
99
- str += '<'+tag+'>'+d[i]+'</'+tag+'>';
102
+ // null and undefined aren't useful in the print output
103
+ var dataOut = d[i] === null || d[i] === undefined ?
104
+ '' :
105
+ d[i];
106
+ var classAttr = columnClasses[i] ?
107
+ 'class="'+columnClasses[i]+'"' :
108
+ '';
109
+
110
+ str += '<'+tag+' '+classAttr+'>'+dataOut+'</'+tag+'>';
100
111
  }
101
112
 
102
113
  return str + '</tr>';
@@ -154,11 +165,11 @@ DataTable.ext.buttons.print = {
154
165
  } );
155
166
 
156
167
  if ( config.customize ) {
157
- config.customize( win );
168
+ config.customize( win, config, dt );
158
169
  }
159
170
 
160
171
  // Allow stylesheets time to load
161
- setTimeout( function () {
172
+ win.setTimeout( function () {
162
173
  if ( config.autoPrint ) {
163
174
  win.print(); // blocking - so close will not
164
175
  win.close(); // execute until this is done
@@ -1,5 +1,5 @@
1
- /*! Buttons for DataTables 1.4.2
2
- * ©2016-2017 SpryMedia Ltd - datatables.net/license
1
+ /*! Buttons for DataTables 1.5.2
2
+ * ©2016-2018 SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
5
5
  (function( factory ){
@@ -50,9 +50,9 @@ var Buttons = function( dt, config )
50
50
  {
51
51
  // If there is no config set it to an empty object
52
52
  if ( typeof( config ) === 'undefined' ) {
53
- config = {};
53
+ config = {};
54
54
  }
55
-
55
+
56
56
  // Allow a boolean true for defaults
57
57
  if ( config === true ) {
58
58
  config = {};
@@ -199,7 +199,7 @@ $.extend( Buttons.prototype, {
199
199
  // needed). Take a copy as the array is modified by `remove`
200
200
  var buttons = this.s.buttons.slice();
201
201
  var i, ien;
202
-
202
+
203
203
  for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
204
204
  this.remove( buttons[i].node );
205
205
  }
@@ -379,8 +379,10 @@ $.extend( Buttons.prototype, {
379
379
  this.add( buttons[i] );
380
380
  }
381
381
 
382
- dt.on( 'destroy', function () {
383
- that.destroy();
382
+ dt.on( 'destroy', function ( e, settings ) {
383
+ if ( settings === dtSettings ) {
384
+ that.destroy();
385
+ }
384
386
  } );
385
387
 
386
388
  // Global key event binding to listen for button keys
@@ -540,11 +542,12 @@ $.extend( Buttons.prototype, {
540
542
  config.action.call( dt.button( button ), e, dt, button, config );
541
543
 
542
544
  $(dt.table().node()).triggerHandler( 'buttons-action.dt', [
543
- dt.button( button ), dt, button, config
545
+ dt.button( button ), dt, button, config
544
546
  ] );
545
547
  };
546
548
 
547
- var button = $('<'+buttonDom.tag+'/>')
549
+ var tag = config.tag || buttonDom.tag;
550
+ var button = $('<'+tag+'/>')
548
551
  .addClass( buttonDom.className )
549
552
  .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex )
550
553
  .attr( 'aria-controls', this.s.dt.table().node().id )
@@ -566,10 +569,15 @@ $.extend( Buttons.prototype, {
566
569
  } );
567
570
 
568
571
  // Make `a` tags act like a link
569
- if ( buttonDom.tag.toLowerCase() === 'a' ) {
572
+ if ( tag.toLowerCase() === 'a' ) {
570
573
  button.attr( 'href', '#' );
571
574
  }
572
575
 
576
+ // Button tags should have `type=button` so they don't have any default behaviour
577
+ if ( tag.toLowerCase() === 'button' ) {
578
+ button.attr( 'type', 'button' );
579
+ }
580
+
573
581
  if ( linerDom.tag ) {
574
582
  var liner = $('<'+linerDom.tag+'/>')
575
583
  .html( text( config.text ) )
@@ -597,6 +605,10 @@ $.extend( Buttons.prototype, {
597
605
  button.attr( 'title', text( config.titleAttr ) );
598
606
  }
599
607
 
608
+ if ( config.attr ) {
609
+ button.attr( config.attr );
610
+ }
611
+
600
612
  if ( ! config.namespace ) {
601
613
  config.namespace = '.dt-button-'+(_buttonCounter++);
602
614
  }
@@ -689,12 +701,18 @@ $.extend( Buttons.prototype, {
689
701
  */
690
702
  _keypress: function ( character, e )
691
703
  {
704
+ // Check if this button press already activated on another instance of Buttons
705
+ if ( e._buttonsHandled ) {
706
+ return;
707
+ }
708
+
692
709
  var run = function ( conf, node ) {
693
710
  if ( ! conf.key ) {
694
711
  return;
695
712
  }
696
713
 
697
714
  if ( conf.key === character ) {
715
+ e._buttonsHandled = true;
698
716
  $(node).click();
699
717
  }
700
718
  else if ( $.isPlainObject( conf.key ) ) {
@@ -719,6 +737,7 @@ $.extend( Buttons.prototype, {
719
737
  }
720
738
 
721
739
  // Made it this far - it is good
740
+ e._buttonsHandled = true;
722
741
  $(node).click();
723
742
  }
724
743
  };
@@ -885,7 +904,7 @@ $.extend( Buttons.prototype, {
885
904
  /**
886
905
  * Show / hide a background layer behind a collection
887
906
  * @param {boolean} Flag to indicate if the background should be shown or
888
- * hidden
907
+ * hidden
889
908
  * @param {string} Class to assign to the background
890
909
  * @static
891
910
  */
@@ -963,7 +982,7 @@ Buttons.instanceSelector = function ( group, buttons )
963
982
  ret.push( buttons[ input ].inst );
964
983
  }
965
984
  };
966
-
985
+
967
986
  process( group );
968
987
 
969
988
  return ret;
@@ -1126,7 +1145,7 @@ Buttons.defaults = {
1126
1145
  className: 'dt-button-collection'
1127
1146
  },
1128
1147
  button: {
1129
- tag: 'a',
1148
+ tag: 'button',
1130
1149
  className: 'dt-button',
1131
1150
  active: 'active',
1132
1151
  disabled: 'disabled'
@@ -1143,7 +1162,7 @@ Buttons.defaults = {
1143
1162
  * @type {string}
1144
1163
  * @static
1145
1164
  */
1146
- Buttons.version = '1.4.2';
1165
+ Buttons.version = '1.5.2';
1147
1166
 
1148
1167
 
1149
1168
  $.extend( _dtButtons, {
@@ -1154,22 +1173,30 @@ $.extend( _dtButtons, {
1154
1173
  className: 'buttons-collection',
1155
1174
  action: function ( e, dt, button, config ) {
1156
1175
  var host = button;
1157
- var hostOffset = host.offset();
1176
+ var collectionParent = $(button).parents('div.dt-button-collection');
1177
+ var hostPosition = host.position();
1158
1178
  var tableContainer = $( dt.table().container() );
1159
1179
  var multiLevel = false;
1180
+ var insertPoint = host;
1160
1181
 
1161
1182
  // Remove any old collection
1162
- if ( $('div.dt-button-background').length ) {
1163
- multiLevel = $('.dt-button-collection').offset();
1183
+ if ( collectionParent.length ) {
1184
+ multiLevel = $('.dt-button-collection').position();
1185
+ insertPoint = collectionParent;
1164
1186
  $('body').trigger( 'click.dtb-collection' );
1165
1187
  }
1166
1188
 
1189
+ if ( insertPoint.parents('body')[0] !== document.body ) {
1190
+ insertPoint = document.body.lastChild;
1191
+ }
1192
+
1167
1193
  config._collection
1168
1194
  .addClass( config.collectionLayout )
1169
1195
  .css( 'display', 'none' )
1170
- .appendTo( 'body' )
1196
+ .insertAfter( insertPoint )
1171
1197
  .fadeIn( config.fade );
1172
1198
 
1199
+
1173
1200
  var position = config._collection.css( 'position' );
1174
1201
 
1175
1202
  if ( multiLevel && position === 'absolute' ) {
@@ -1180,29 +1207,36 @@ $.extend( _dtButtons, {
1180
1207
  }
1181
1208
  else if ( position === 'absolute' ) {
1182
1209
  config._collection.css( {
1183
- top: hostOffset.top + host.outerHeight(),
1184
- left: hostOffset.left
1210
+ top: hostPosition.top + host.outerHeight(),
1211
+ left: hostPosition.left
1185
1212
  } );
1186
1213
 
1187
1214
  // calculate overflow when positioned beneath
1188
1215
  var tableBottom = tableContainer.offset().top + tableContainer.height();
1189
- var listBottom = hostOffset.top + host.outerHeight() + config._collection.outerHeight();
1216
+ var listBottom = hostPosition.top + host.outerHeight() + config._collection.outerHeight();
1190
1217
  var bottomOverflow = listBottom - tableBottom;
1191
-
1218
+
1192
1219
  // calculate overflow when positioned above
1193
- var listTop = hostOffset.top - config._collection.outerHeight();
1220
+ var listTop = hostPosition.top - config._collection.outerHeight();
1194
1221
  var tableTop = tableContainer.offset().top;
1195
1222
  var topOverflow = tableTop - listTop;
1196
-
1197
- // if bottom overflow is larger, move to the top because it fits better
1198
- if (bottomOverflow > topOverflow) {
1199
- config._collection.css( 'top', hostOffset.top - config._collection.outerHeight() - 5);
1223
+
1224
+ // if bottom overflow is larger, move to the top because it fits better, or if dropup is requested
1225
+ if (bottomOverflow > topOverflow || config.dropup) {
1226
+ config._collection.css( 'top', hostPosition.top - config._collection.outerHeight() - 5);
1200
1227
  }
1201
1228
 
1202
- var listRight = hostOffset.left + config._collection.outerWidth();
1229
+ // Right alignment in table container
1230
+ var listRight = hostPosition.left + config._collection.outerWidth();
1203
1231
  var tableRight = tableContainer.offset().left + tableContainer.width();
1204
1232
  if ( listRight > tableRight ) {
1205
- config._collection.css( 'left', hostOffset.left - ( listRight - tableRight ) );
1233
+ config._collection.css( 'left', hostPosition.left - ( listRight - tableRight ) );
1234
+ }
1235
+
1236
+ // Right alignment to window
1237
+ var listOffsetRight = host.offset().left + config._collection.outerWidth();
1238
+ if ( listOffsetRight > $(window).width() ) {
1239
+ config._collection.css( 'left', hostPosition.left - (listOffsetRight-$(window).width()) );
1206
1240
  }
1207
1241
  }
1208
1242
  else {
@@ -1219,6 +1253,19 @@ $.extend( _dtButtons, {
1219
1253
  Buttons.background( true, config.backgroundClassName, config.fade );
1220
1254
  }
1221
1255
 
1256
+ var close = function () {
1257
+ config._collection
1258
+ .fadeOut( config.fade, function () {
1259
+ config._collection.detach();
1260
+ } );
1261
+
1262
+ $('div.dt-button-background').off( 'click.dtb-collection' );
1263
+ Buttons.background( false, config.backgroundClassName, config.fade );
1264
+
1265
+ $('body').off( '.dtb-collection' );
1266
+ dt.off( 'buttons-action.b-internal' );
1267
+ };
1268
+
1222
1269
  // Need to break the 'thread' for the collection button being
1223
1270
  // activated by a click - it would also trigger this event
1224
1271
  setTimeout( function () {
@@ -1228,36 +1275,36 @@ $.extend( _dtButtons, {
1228
1275
  // required to make it work...
1229
1276
  $('div.dt-button-background').on( 'click.dtb-collection', function () {} );
1230
1277
 
1231
- $('body').on( 'click.dtb-collection', function (e) {
1232
- // andSelf is deprecated in jQ1.8, but we want 1.7 compat
1233
- var back = $.fn.addBack ? 'addBack' : 'andSelf';
1234
-
1235
- if ( ! $(e.target).parents()[back]().filter( config._collection ).length ) {
1236
- config._collection
1237
- .fadeOut( config.fade, function () {
1238
- config._collection.detach();
1239
- } );
1240
-
1241
- $('div.dt-button-background').off( 'click.dtb-collection' );
1242
- Buttons.background( false, config.backgroundClassName, config.fade );
1278
+ $('body')
1279
+ .on( 'click.dtb-collection', function (e) {
1280
+ // andSelf is deprecated in jQ1.8, but we want 1.7 compat
1281
+ var back = $.fn.addBack ? 'addBack' : 'andSelf';
1282
+
1283
+ if ( ! $(e.target).parents()[back]().filter( config._collection ).length ) {
1284
+ close();
1285
+ }
1286
+ } )
1287
+ .on( 'keyup.dtb-collection', function (e) {
1288
+ if ( e.keyCode === 27 ) {
1289
+ close();
1290
+ }
1291
+ } );
1243
1292
 
1244
- $('body').off( 'click.dtb-collection' );
1245
- dt.off( 'buttons-action.b-internal' );
1246
- }
1247
- } );
1293
+ if ( config.autoClose ) {
1294
+ dt.on( 'buttons-action.b-internal', function () {
1295
+ close();
1296
+ } );
1297
+ }
1248
1298
  }, 10 );
1249
-
1250
- if ( config.autoClose ) {
1251
- dt.on( 'buttons-action.b-internal', function () {
1252
- $('div.dt-button-background').click();
1253
- } );
1254
- }
1255
1299
  },
1256
1300
  background: true,
1257
1301
  collectionLayout: '',
1258
1302
  backgroundClassName: 'dt-button-background',
1259
1303
  autoClose: false,
1260
- fade: 400
1304
+ fade: 400,
1305
+ attr: {
1306
+ 'aria-haspopup': true
1307
+ }
1261
1308
  },
1262
1309
  copy: function ( dt, conf ) {
1263
1310
  if ( _dtButtons.copyHtml5 ) {
@@ -1585,7 +1632,7 @@ DataTable.Api.register( 'buttons.exportInfo()', function ( conf ) {
1585
1632
  return {
1586
1633
  filename: _filename( conf ),
1587
1634
  title: _title( conf ),
1588
- messageTop: _message(this, conf.messageTop || conf.message, 'top'),
1635
+ messageTop: _message(this, conf.message || conf.messageTop, 'top'),
1589
1636
  messageBottom: _message(this, conf.messageBottom, 'bottom')
1590
1637
  };
1591
1638
  } );
@@ -1601,7 +1648,7 @@ DataTable.Api.register( 'buttons.exportInfo()', function ( conf ) {
1601
1648
  var _filename = function ( config )
1602
1649
  {
1603
1650
  // Backwards compatibility
1604
- var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined ?
1651
+ var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined && config.title !== null && config.title !== '' ?
1605
1652
  config.title :
1606
1653
  config.filename;
1607
1654
 
@@ -1614,7 +1661,7 @@ var _filename = function ( config )
1614
1661
  }
1615
1662
 
1616
1663
  if ( filename.indexOf( '*' ) !== -1 ) {
1617
- filename = $.trim( filename.replace( '*', $('title').text() ) );
1664
+ filename = $.trim( filename.replace( '*', $('head > title').text() ) );
1618
1665
  }
1619
1666
 
1620
1667
  // Strip characters which the OS will object to
@@ -1656,7 +1703,7 @@ var _title = function ( config )
1656
1703
 
1657
1704
  return title === null ?
1658
1705
  null : title.indexOf( '*' ) !== -1 ?
1659
- title.replace( '*', $('title').text() || 'Exported data' ) :
1706
+ title.replace( '*', $('head > title').text() || 'Exported data' ) :
1660
1707
  title;
1661
1708
  };
1662
1709
 
@@ -1713,7 +1760,8 @@ var _exportData = function ( dt, inOpts )
1713
1760
  body: function ( d ) {
1714
1761
  return strip( d );
1715
1762
  }
1716
- }
1763
+ },
1764
+ customizeData: null
1717
1765
  }, inOpts );
1718
1766
 
1719
1767
  var strip = function ( str ) {
@@ -1724,6 +1772,9 @@ var _exportData = function ( dt, inOpts )
1724
1772
  // Always remove script tags
1725
1773
  str = str.replace( /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '' );
1726
1774
 
1775
+ // Always remove comments
1776
+ str = str.replace( /<!\-\-.*?\-\->/g, '' );
1777
+
1727
1778
  if ( config.stripHtml ) {
1728
1779
  str = str.replace( /<[^>]*>/g, '' );
1729
1780
  }
@@ -1757,7 +1808,17 @@ var _exportData = function ( dt, inOpts )
1757
1808
  } ).toArray() :
1758
1809
  null;
1759
1810
 
1760
- var rowIndexes = dt.rows( config.rows, config.modifier ).indexes().toArray();
1811
+ // If Select is available on this table, and any rows are selected, limit the export
1812
+ // to the selected rows. If no rows are selected, all rows will be exported. Specify
1813
+ // a `selected` modifier to control directly.
1814
+ var modifier = $.extend( {}, config.modifier );
1815
+ if ( dt.select && typeof dt.select.info === 'function' && modifier.selected === undefined ) {
1816
+ if ( dt.rows( config.rows, $.extend( { selected: true }, modifier ) ).any() ) {
1817
+ $.extend( modifier, { selected: true } )
1818
+ }
1819
+ }
1820
+
1821
+ var rowIndexes = dt.rows( config.rows, modifier ).indexes().toArray();
1761
1822
  var selectedCells = dt.cells( rowIndexes, config.columns );
1762
1823
  var cells = selectedCells
1763
1824
  .render( config.orthogonal )
@@ -1768,11 +1829,11 @@ var _exportData = function ( dt, inOpts )
1768
1829
 
1769
1830
  var columns = header.length;
1770
1831
  var rows = columns > 0 ? cells.length / columns : 0;
1771
- var body = new Array( rows );
1832
+ var body = [];
1772
1833
  var cellCounter = 0;
1773
1834
 
1774
1835
  for ( var i=0, ien=rows ; i<ien ; i++ ) {
1775
- var row = new Array( columns );
1836
+ var row = [ columns ];
1776
1837
 
1777
1838
  for ( var j=0 ; j<columns ; j++ ) {
1778
1839
  row[j] = config.format.body( cells[ cellCounter ], i, j, cellNodes[ cellCounter ] );
@@ -1782,11 +1843,17 @@ var _exportData = function ( dt, inOpts )
1782
1843
  body[i] = row;
1783
1844
  }
1784
1845
 
1785
- return {
1846
+ var data = {
1786
1847
  header: header,
1787
1848
  footer: footer,
1788
1849
  body: body
1789
1850
  };
1851
+
1852
+ if ( config.customizeData ) {
1853
+ config.customizeData( data );
1854
+ }
1855
+
1856
+ return data;
1790
1857
  };
1791
1858
 
1792
1859