effective_datatables 4.2.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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