jquery-datatables 1.10.12 → 1.10.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/app/assets/javascripts/datatables/dataTables.bootstrap.js +2 -2
  4. data/app/assets/javascripts/datatables/dataTables.bootstrap4.js +5 -5
  5. data/app/assets/javascripts/datatables/dataTables.foundation.js +1 -1
  6. data/app/assets/javascripts/datatables/dataTables.material.js +1 -1
  7. data/app/assets/javascripts/datatables/dataTables.semanticui.js +1 -1
  8. data/app/assets/javascripts/datatables/extensions/AutoFill/dataTables.autoFill.js +63 -31
  9. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.colVis.js +2 -3
  10. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.flash.js +145 -55
  11. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.html5.js +224 -206
  12. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.print.js +14 -8
  13. data/app/assets/javascripts/datatables/extensions/Buttons/dataTables.buttons.js +54 -24
  14. data/app/assets/javascripts/datatables/extensions/KeyTable/dataTables.keyTable.js +137 -68
  15. data/app/assets/javascripts/datatables/extensions/Responsive/dataTables.responsive.js +30 -7
  16. data/app/assets/javascripts/datatables/extensions/Responsive/responsive.bootstrap.js +6 -2
  17. data/app/assets/javascripts/datatables/extensions/Responsive/responsive.bootstrap4.js +6 -2
  18. data/app/assets/javascripts/datatables/extensions/RowReorder/dataTables.rowReorder.js +64 -6
  19. data/app/assets/javascripts/datatables/extensions/Select/dataTables.select.js +40 -15
  20. data/app/assets/javascripts/datatables/jquery.dataTables.js +246 -217
  21. data/app/assets/javascripts/datatables/plugins/api/average.js +32 -0
  22. data/app/assets/javascripts/datatables/plugins/api/sum.js +51 -0
  23. data/app/assets/javascripts/datatables/plugins/pagination/input.js +224 -0
  24. data/app/assets/javascripts/datatables/plugins/search/alphabetSearch.js +368 -0
  25. data/app/assets/javascripts/datatables/plugins/sorting/file-size.js +43 -0
  26. data/app/assets/javascripts/datatables/plugins/sorting/ip-address.js +103 -0
  27. data/app/assets/stylesheets/datatables/dataTables.bootstrap.css +0 -1
  28. data/app/assets/stylesheets/datatables/dataTables.semanticui.css +0 -1
  29. data/app/assets/stylesheets/datatables/extensions/Responsive/responsive.bootstrap.css +4 -4
  30. data/app/assets/stylesheets/datatables/extensions/Responsive/responsive.bootstrap4.css +4 -4
  31. data/app/assets/stylesheets/datatables/extensions/Responsive/responsive.dataTables.css +4 -4
  32. data/app/assets/stylesheets/datatables/extensions/Responsive/responsive.foundation.css +4 -4
  33. data/app/assets/stylesheets/datatables/extensions/Responsive/responsive.jqueryui.css +4 -4
  34. data/app/assets/stylesheets/datatables/extensions/Responsive/responsive.semanticui.css +4 -4
  35. data/app/assets/stylesheets/datatables/extensions/Select/select.bootstrap.css +9 -4
  36. data/app/assets/stylesheets/datatables/extensions/Select/select.bootstrap4.css +9 -4
  37. data/app/assets/stylesheets/datatables/extensions/Select/select.dataTables.css +9 -4
  38. data/app/assets/stylesheets/datatables/extensions/Select/select.foundation.css +9 -4
  39. data/app/assets/stylesheets/datatables/extensions/Select/select.jqueryui.css +9 -4
  40. data/app/assets/stylesheets/datatables/extensions/Select/select.semanticui.css +9 -4
  41. data/app/assets/stylesheets/datatables/plugins/search/alphabetSearch.css +43 -0
  42. data/lib/jquery-datatables/version.rb +1 -1
  43. metadata +10 -3
@@ -2,7 +2,7 @@
2
2
  * HTML5 export buttons for Buttons and DataTables.
3
3
  * 2016 SpryMedia Ltd - datatables.net/license
4
4
  *
5
- * FileSaver.js (1.1.20160328) - MIT license
5
+ * FileSaver.js (1.3.3) - MIT license
6
6
  * Copyright © 2016 Eli Grey - http://eligrey.com
7
7
  */
8
8
 
@@ -35,17 +35,17 @@
35
35
  // Browser
36
36
  factory( jQuery, window, document );
37
37
  }
38
- }(function( $, window, document, jsZip, pdfMake, undefined ) {
38
+ }(function( $, window, document, jszip, pdfmake, undefined ) {
39
39
  'use strict';
40
40
  var DataTable = $.fn.dataTable;
41
41
 
42
42
  // Allow the constructor to pass in JSZip and PDFMake from external requires.
43
43
  // Otherwise, use globally defined variables, if they are available.
44
- if ( jsZip === undefined ) {
45
- jsZip = window.JSZip;
44
+ function _jsZip () {
45
+ return jszip || window.JSZip;
46
46
  }
47
- if ( pdfMake === undefined ) {
48
- pdfMake = window.pdfMake;
47
+ function _pdfMake () {
48
+ return pdfmake || window.pdfMake;
49
49
  }
50
50
 
51
51
 
@@ -58,7 +58,7 @@ if ( pdfMake === undefined ) {
58
58
  var _saveAs = (function(view) {
59
59
  "use strict";
60
60
  // IE <10 is explicitly unsupported
61
- if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
61
+ if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
62
62
  return;
63
63
  }
64
64
  var
@@ -73,16 +73,14 @@ var _saveAs = (function(view) {
73
73
  var event = new MouseEvent("click");
74
74
  node.dispatchEvent(event);
75
75
  }
76
- , is_safari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent)
77
- , webkit_req_fs = view.webkitRequestFileSystem
78
- , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
76
+ , is_safari = /constructor/i.test(view.HTMLElement) || view.safari
77
+ , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
79
78
  , throw_outside = function(ex) {
80
79
  (view.setImmediate || view.setTimeout)(function() {
81
80
  throw ex;
82
81
  }, 0);
83
82
  }
84
83
  , force_saveable_type = "application/octet-stream"
85
- , fs_min_size = 0
86
84
  // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
87
85
  , arbitrary_revoke_timeout = 1000 * 40 // in ms
88
86
  , revoke = function(file) {
@@ -93,22 +91,6 @@ var _saveAs = (function(view) {
93
91
  file.remove();
94
92
  }
95
93
  };
96
- /* // Take note W3C:
97
- var
98
- uri = typeof file === "string" ? file : file.toURL()
99
- , revoker = function(evt) {
100
- // idealy DownloadFinishedEvent.data would be the URL requested
101
- if (evt.data === uri) {
102
- if (typeof file === "string") { // file is an object URL
103
- get_URL().revokeObjectURL(file);
104
- } else { // file is a File
105
- file.remove();
106
- }
107
- }
108
- }
109
- ;
110
- view.addEventListener("downloadfinished", revoker);
111
- */
112
94
  setTimeout(revoker, arbitrary_revoke_timeout);
113
95
  }
114
96
  , dispatch = function(filesaver, event_types, event) {
@@ -127,8 +109,9 @@ var _saveAs = (function(view) {
127
109
  }
128
110
  , auto_bom = function(blob) {
129
111
  // prepend BOM for UTF-8 XML and text/* types (including HTML)
112
+ // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
130
113
  if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
131
- return new Blob(["\ufeff", blob], {type: blob.type});
114
+ return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
132
115
  }
133
116
  return blob;
134
117
  }
@@ -140,20 +123,21 @@ var _saveAs = (function(view) {
140
123
  var
141
124
  filesaver = this
142
125
  , type = blob.type
143
- , blob_changed = false
126
+ , force = type === force_saveable_type
144
127
  , object_url
145
- , target_view
146
128
  , dispatch_all = function() {
147
129
  dispatch(filesaver, "writestart progress write writeend".split(" "));
148
130
  }
149
131
  // on any filesys errors revert to saving with object URLs
150
132
  , fs_error = function() {
151
- if (target_view && is_safari && typeof FileReader !== "undefined") {
133
+ if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
152
134
  // Safari doesn't allow downloading of blob urls
153
135
  var reader = new FileReader();
154
136
  reader.onloadend = function() {
155
- var base64Data = reader.result;
156
- target_view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/));
137
+ var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
138
+ var popup = view.open(url, '_blank');
139
+ if(!popup) view.location.href = url;
140
+ url=undefined; // release reference before dispatching
157
141
  filesaver.readyState = filesaver.DONE;
158
142
  dispatch_all();
159
143
  };
@@ -162,36 +146,25 @@ var _saveAs = (function(view) {
162
146
  return;
163
147
  }
164
148
  // don't create more object URLs than needed
165
- if (blob_changed || !object_url) {
149
+ if (!object_url) {
166
150
  object_url = get_URL().createObjectURL(blob);
167
151
  }
168
- if (target_view) {
169
- target_view.location.href = object_url;
152
+ if (force) {
153
+ view.location.href = object_url;
170
154
  } else {
171
- var new_tab = view.open(object_url, "_blank");
172
- if (new_tab === undefined && is_safari) {
173
- //Apple do not allow window.open, see http://bit.ly/1kZffRI
174
- view.location.href = object_url
155
+ var opened = view.open(object_url, "_blank");
156
+ if (!opened) {
157
+ // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
158
+ view.location.href = object_url;
175
159
  }
176
160
  }
177
161
  filesaver.readyState = filesaver.DONE;
178
162
  dispatch_all();
179
163
  revoke(object_url);
180
164
  }
181
- , abortable = function(func) {
182
- return function() {
183
- if (filesaver.readyState !== filesaver.DONE) {
184
- return func.apply(this, arguments);
185
- }
186
- };
187
- }
188
- , create_if_not_found = {create: true, exclusive: false}
189
- , slice
190
165
  ;
191
166
  filesaver.readyState = filesaver.INIT;
192
- if (!name) {
193
- name = "download";
194
- }
167
+
195
168
  if (can_use_save_link) {
196
169
  object_url = get_URL().createObjectURL(blob);
197
170
  setTimeout(function() {
@@ -204,93 +177,27 @@ var _saveAs = (function(view) {
204
177
  });
205
178
  return;
206
179
  }
207
- // Object and web filesystem URLs have a problem saving in Google Chrome when
208
- // viewed in a tab, so I force save with application/octet-stream
209
- // http://code.google.com/p/chromium/issues/detail?id=91158
210
- // Update: Google errantly closed 91158, I submitted it again:
211
- // https://code.google.com/p/chromium/issues/detail?id=389642
212
- if (view.chrome && type && type !== force_saveable_type) {
213
- slice = blob.slice || blob.webkitSlice;
214
- blob = slice.call(blob, 0, blob.size, force_saveable_type);
215
- blob_changed = true;
216
- }
217
- // Since I can't be sure that the guessed media type will trigger a download
218
- // in WebKit, I append .download to the filename.
219
- // https://bugs.webkit.org/show_bug.cgi?id=65440
220
- if (webkit_req_fs && name !== "download") {
221
- name += ".download";
222
- }
223
- if (type === force_saveable_type || webkit_req_fs) {
224
- target_view = view;
225
- }
226
- if (!req_fs) {
227
- fs_error();
228
- return;
229
- }
230
- fs_min_size += blob.size;
231
- req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
232
- fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
233
- var save = function() {
234
- dir.getFile(name, create_if_not_found, abortable(function(file) {
235
- file.createWriter(abortable(function(writer) {
236
- writer.onwriteend = function(event) {
237
- target_view.location.href = file.toURL();
238
- filesaver.readyState = filesaver.DONE;
239
- dispatch(filesaver, "writeend", event);
240
- revoke(file);
241
- };
242
- writer.onerror = function() {
243
- var error = writer.error;
244
- if (error.code !== error.ABORT_ERR) {
245
- fs_error();
246
- }
247
- };
248
- "writestart progress write abort".split(" ").forEach(function(event) {
249
- writer["on" + event] = filesaver["on" + event];
250
- });
251
- writer.write(blob);
252
- filesaver.abort = function() {
253
- writer.abort();
254
- filesaver.readyState = filesaver.DONE;
255
- };
256
- filesaver.readyState = filesaver.WRITING;
257
- }), fs_error);
258
- }), fs_error);
259
- };
260
- dir.getFile(name, {create: false}, abortable(function(file) {
261
- // delete file if it already exists
262
- file.remove();
263
- save();
264
- }), abortable(function(ex) {
265
- if (ex.code === ex.NOT_FOUND_ERR) {
266
- save();
267
- } else {
268
- fs_error();
269
- }
270
- }));
271
- }), fs_error);
272
- }), fs_error);
180
+
181
+ fs_error();
273
182
  }
274
183
  , FS_proto = FileSaver.prototype
275
184
  , saveAs = function(blob, name, no_auto_bom) {
276
- return new FileSaver(blob, name, no_auto_bom);
185
+ return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
277
186
  }
278
187
  ;
279
188
  // IE 10+ (native saveAs)
280
189
  if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
281
190
  return function(blob, name, no_auto_bom) {
191
+ name = name || blob.name || "download";
192
+
282
193
  if (!no_auto_bom) {
283
194
  blob = auto_bom(blob);
284
195
  }
285
- return navigator.msSaveOrOpenBlob(blob, name || "download");
196
+ return navigator.msSaveOrOpenBlob(blob, name);
286
197
  };
287
198
  }
288
199
 
289
- FS_proto.abort = function() {
290
- var filesaver = this;
291
- filesaver.readyState = filesaver.DONE;
292
- dispatch(filesaver, "abort");
293
- };
200
+ FS_proto.abort = function(){};
294
201
  FS_proto.readyState = FS_proto.INIT = 0;
295
202
  FS_proto.WRITING = 1;
296
203
  FS_proto.DONE = 2;
@@ -450,17 +357,27 @@ var _exportData = function ( dt, config )
450
357
  };
451
358
 
452
359
  /**
453
- * Safari's data: support for creating and downloading files is really poor, so
454
- * various options need to be disabled in it. See
455
- * https://bugs.webkit.org/show_bug.cgi?id=102914
360
+ * Older versions of Safari (prior to tech preview 18) don't support the
361
+ * download option required.
456
362
  *
457
- * @return {Boolean} `true` if Safari
363
+ * @return {Boolean} `true` if old Safari
458
364
  */
459
- var _isSafari = function ()
365
+ var _isDuffSafari = function ()
460
366
  {
461
- return navigator.userAgent.indexOf('Safari') !== -1 &&
367
+ var safari = navigator.userAgent.indexOf('Safari') !== -1 &&
462
368
  navigator.userAgent.indexOf('Chrome') === -1 &&
463
369
  navigator.userAgent.indexOf('Opera') === -1;
370
+
371
+ if ( ! safari ) {
372
+ return false;
373
+ }
374
+
375
+ var version = navigator.userAgent.match( /AppleWebKit\/(\d+\.\d+)/ );
376
+ if ( version && version.length > 1 && version[1]*1 < 603.1 ) {
377
+ return true;
378
+ }
379
+
380
+ return false;
464
381
  };
465
382
 
466
383
  /**
@@ -554,11 +471,9 @@ function _addToZip( zip, obj ) {
554
471
  str = str.replace( /_dt_b_namespace_token_/g, ':' );
555
472
  }
556
473
 
557
- // Both IE and Edge will put empty name space attributes onto the
558
- // rows and columns making them useless
559
- str = str
560
- .replace( /<row xmlns="" /g, '<row ' )
561
- .replace( /<cols xmlns="">/g, '<cols>' );
474
+ // Safari, IE and Edge will put empty name space attributes onto
475
+ // various elements making them useless. This strips them out
476
+ str = str.replace( /<(.*?) xmlns=""(.*?)>/g, '<$1 $2>' );
562
477
 
563
478
  zip.file( name, str );
564
479
  }
@@ -605,14 +520,28 @@ function _createNode( doc, nodeName, opts ) {
605
520
  */
606
521
  function _excelColWidth( data, col ) {
607
522
  var max = data.header[col].length;
608
- var len;
523
+ var len, lineSplit, str;
609
524
 
610
525
  if ( data.footer && data.footer[col].length > max ) {
611
526
  max = data.footer[col].length;
612
527
  }
613
528
 
614
529
  for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
615
- len = data.body[i][col].toString().length;
530
+ str = data.body[i][col].toString();
531
+
532
+ // If there is a newline character, workout the width of the column
533
+ // based on the longest line in the string
534
+ if ( str.indexOf('\n') !== -1 ) {
535
+ lineSplit = str.split('\n');
536
+ lineSplit.sort( function (a, b) {
537
+ return b.length - a.length;
538
+ } );
539
+
540
+ len = lineSplit[0].length;
541
+ }
542
+ else {
543
+ len = str.length;
544
+ }
616
545
 
617
546
  if ( len > max ) {
618
547
  max = len;
@@ -624,8 +553,10 @@ function _excelColWidth( data, col ) {
624
553
  }
625
554
  }
626
555
 
556
+ max *= 1.3;
557
+
627
558
  // And a min width
628
- return max > 5 ? max : 5;
559
+ return max > 6 ? max : 6;
629
560
  }
630
561
 
631
562
  // Excel - Pre-defined strings to build a basic XLSX file
@@ -673,9 +604,17 @@ var excelStrings = {
673
604
  '<sheetData/>'+
674
605
  '</worksheet>',
675
606
 
676
- "xl/styles.xml":
607
+ "xl/styles.xml":
677
608
  '<?xml version="1.0" encoding="UTF-8"?>'+
678
609
  '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">'+
610
+ '<numFmts count="6">'+
611
+ '<numFmt numFmtId="164" formatCode="#,##0.00_-\ [$$-45C]"/>'+
612
+ '<numFmt numFmtId="165" formatCode="&quot;£&quot;#,##0.00"/>'+
613
+ '<numFmt numFmtId="166" formatCode="[$€-2]\ #,##0.00"/>'+
614
+ '<numFmt numFmtId="167" formatCode="0.0%"/>'+
615
+ '<numFmt numFmtId="168" formatCode="#,##0;(#,##0)"/>'+
616
+ '<numFmt numFmtId="169" formatCode="#,##0.00;(#,##0.00)"/>'+
617
+ '</numFmts>'+
679
618
  '<fonts count="5" x14ac:knownFonts="1">'+
680
619
  '<font>'+
681
620
  '<sz val="11" />'+
@@ -759,7 +698,7 @@ var excelStrings = {
759
698
  '<cellStyleXfs count="1">'+
760
699
  '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />'+
761
700
  '</cellStyleXfs>'+
762
- '<cellXfs count="2">'+
701
+ '<cellXfs count="65">'+
763
702
  '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
764
703
  '<xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
765
704
  '<xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
@@ -810,6 +749,33 @@ var excelStrings = {
810
749
  '<xf numFmtId="0" fontId="2" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
811
750
  '<xf numFmtId="0" fontId="3" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
812
751
  '<xf numFmtId="0" fontId="4" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
752
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
753
+ '<alignment horizontal="left"/>'+
754
+ '</xf>'+
755
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
756
+ '<alignment horizontal="center"/>'+
757
+ '</xf>'+
758
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
759
+ '<alignment horizontal="right"/>'+
760
+ '</xf>'+
761
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
762
+ '<alignment horizontal="fill"/>'+
763
+ '</xf>'+
764
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
765
+ '<alignment textRotation="90"/>'+
766
+ '</xf>'+
767
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
768
+ '<alignment wrapText="1"/>'+
769
+ '</xf>'+
770
+ '<xf numFmtId="9" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
771
+ '<xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
772
+ '<xf numFmtId="165" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
773
+ '<xf numFmtId="166" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
774
+ '<xf numFmtId="167" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
775
+ '<xf numFmtId="168" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
776
+ '<xf numFmtId="169" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
777
+ '<xf numFmtId="3" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
778
+ '<xf numFmtId="4" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
813
779
  '</cellXfs>'+
814
780
  '<cellStyles count="1">'+
815
781
  '<cellStyle name="Normal" xfId="0" builtinId="0" />'+
@@ -821,6 +787,22 @@ var excelStrings = {
821
787
  // Note we could use 3 `for` loops for the styles, but when gzipped there is
822
788
  // virtually no difference in size, since the above can be easily compressed
823
789
 
790
+ // Pattern matching for special number formats. Perhaps this should be exposed
791
+ // via an API in future?
792
+ // Ref: section 3.8.30 - built in formatters in open spreadsheet
793
+ // https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf
794
+ var _excelSpecials = [
795
+ { match: /^\-?\d+\.\d%$/, style: 60, fmt: function (d) { return d/100; } }, // Precent with d.p.
796
+ { match: /^\-?\d+\.?\d*%$/, style: 56, fmt: function (d) { return d/100; } }, // Percent
797
+ { match: /^\-?\$[\d,]+.?\d*$/, style: 57 }, // Dollars
798
+ { match: /^\-?£[\d,]+.?\d*$/, style: 58 }, // Pounds
799
+ { match: /^\-?€[\d,]+.?\d*$/, style: 59 }, // Euros
800
+ { match: /^\([\d,]+\)$/, style: 61, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } }, // Negative numbers indicated by brackets
801
+ { match: /^\([\d,]+\.\d{2}\)$/, style: 62, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } }, // Negative numbers indicated by brackets - 2d.p.
802
+ { match: /^[\d,]+$/, style: 63 }, // Numbers with thousand separators
803
+ { match: /^[\d,]+\.\d{2}$/, style: 64 } // Numbers with 2d.p. and thousands separators
804
+ ];
805
+
824
806
 
825
807
 
826
808
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
@@ -865,19 +847,20 @@ DataTable.ext.buttons.copyHtml5 = {
865
847
  textarea[0].select();
866
848
 
867
849
  try {
868
- document.execCommand( 'copy' );
850
+ var successful = document.execCommand( 'copy' );
869
851
  hiddenDiv.remove();
870
852
 
871
- dt.buttons.info(
872
- dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ),
873
- dt.i18n( 'buttons.copySuccess', {
874
- 1: "Copied one row to clipboard",
875
- _: "Copied %d rows to clipboard"
853
+ if (successful) {
854
+ dt.buttons.info(
855
+ dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ),
856
+ dt.i18n( 'buttons.copySuccess', {
857
+ 1: 'Copied one row to clipboard',
858
+ _: 'Copied %d rows to clipboard'
876
859
  }, exportData.rows ),
877
- 2000
878
- );
879
-
880
- return;
860
+ 2000
861
+ );
862
+ return;
863
+ }
881
864
  }
882
865
  catch (t) {}
883
866
  }
@@ -931,6 +914,8 @@ DataTable.ext.buttons.copyHtml5 = {
931
914
  // CSV export
932
915
  //
933
916
  DataTable.ext.buttons.csvHtml5 = {
917
+ bom: false,
918
+
934
919
  className: 'buttons-csv buttons-html5',
935
920
 
936
921
  available: function () {
@@ -963,9 +948,14 @@ DataTable.ext.buttons.csvHtml5 = {
963
948
  charset = '';
964
949
  }
965
950
 
951
+ if ( config.bom ) {
952
+ output = '\ufeff' + output;
953
+ }
954
+
966
955
  _saveAs(
967
956
  new Blob( [output], {type: 'text/csv'+charset} ),
968
- _filename( config )
957
+ _filename( config ),
958
+ true
969
959
  );
970
960
  },
971
961
 
@@ -995,7 +985,7 @@ DataTable.ext.buttons.excelHtml5 = {
995
985
  className: 'buttons-excel buttons-html5',
996
986
 
997
987
  available: function () {
998
- return window.FileReader !== undefined && jsZip !== undefined && ! _isSafari() && _serialiser;
988
+ return window.FileReader !== undefined && _jsZip() !== undefined && ! _isDuffSafari() && _serialiser;
999
989
  },
1000
990
 
1001
991
  text: function ( dt ) {
@@ -1041,58 +1031,85 @@ DataTable.ext.buttons.excelHtml5 = {
1041
1031
  for ( var i=0, ien=row.length ; i<ien ; i++ ) {
1042
1032
  // Concat both the Cell Columns as a letter and the Row of the cell.
1043
1033
  var cellId = createCellPos(i) + '' + currentRow;
1044
- var cell;
1034
+ var cell = null;
1045
1035
 
1046
- if ( row[i] === null || row[i] === undefined ) {
1047
- row[i] = '';
1036
+ // For null, undefined of blank cell, continue so it doesn't create the _createNode
1037
+ if ( row[i] === null || row[i] === undefined || row[i] === '' ) {
1038
+ continue;
1048
1039
  }
1049
1040
 
1050
- // Detect numbers - don't match numbers with leading zeros or a negative
1051
- // anywhere but the start
1052
- if ( typeof row[i] === 'number' || (
1053
- row[i].match &&
1054
- $.trim(row[i]).match(/^-?\d+(\.\d+)?$/) &&
1055
- ! $.trim(row[i]).match(/^0\d+/) )
1056
- ) {
1057
- cell = _createNode( rels, 'c', {
1058
- attr: {
1059
- t: 'n',
1060
- r: cellId
1061
- },
1062
- children: [
1063
- _createNode( rels, 'v', { text: row[i] } )
1064
- ]
1065
- } );
1066
- }
1067
- else {
1068
- // Replace non standard characters for text output
1069
- var text = ! row[i].replace ?
1070
- row[i] :
1071
- row[i]
1072
- .replace(/&(?!amp;)/g, '&amp;')
1073
- .replace(/</g, '&lt;')
1074
- .replace(/>/g, '&gt;')
1075
- .replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');
1076
-
1077
- cell = _createNode( rels, 'c', {
1078
- attr: {
1079
- t: 'inlineStr',
1080
- r: cellId
1081
- },
1082
- children:{
1083
- row: _createNode( rels, 'is', {
1084
- children: {
1085
- row: _createNode( rels, 't', {
1086
- text: text
1087
- } )
1088
- }
1089
- } )
1041
+ row[i] = $.trim( row[i] );
1042
+
1043
+ // Special number formatting options
1044
+ for ( var j=0, jen=_excelSpecials.length ; j<jen ; j++ ) {
1045
+ var special = _excelSpecials[j];
1046
+
1047
+ if ( row[i].match && row[i].match( special.match ) ) {
1048
+ var val = row[i].replace(/[^\d\.\-]/g, '');
1049
+
1050
+ if ( special.fmt ) {
1051
+ val = special.fmt( val );
1090
1052
  }
1091
- } );
1053
+
1054
+ cell = _createNode( rels, 'c', {
1055
+ attr: {
1056
+ r: cellId,
1057
+ s: special.style
1058
+ },
1059
+ children: [
1060
+ _createNode( rels, 'v', { text: val } )
1061
+ ]
1062
+ } );
1063
+
1064
+ break;
1065
+ }
1066
+ }
1067
+
1068
+ if ( ! cell ) {
1069
+ if ( typeof row[i] === 'number' || (
1070
+ row[i].match &&
1071
+ row[i].match(/^-?\d+(\.\d+)?$/) &&
1072
+ ! row[i].match(/^0\d+/) )
1073
+ ) {
1074
+ // Detect numbers - don't match numbers with leading zeros
1075
+ // or a negative anywhere but the start
1076
+ cell = _createNode( rels, 'c', {
1077
+ attr: {
1078
+ t: 'n',
1079
+ r: cellId
1080
+ },
1081
+ children: [
1082
+ _createNode( rels, 'v', { text: row[i] } )
1083
+ ]
1084
+ } );
1085
+ }
1086
+ else {
1087
+ // String output - replace non standard characters for text output
1088
+ var text = ! row[i].replace ?
1089
+ row[i] :
1090
+ row[i].replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');
1091
+
1092
+ cell = _createNode( rels, 'c', {
1093
+ attr: {
1094
+ t: 'inlineStr',
1095
+ r: cellId
1096
+ },
1097
+ children:{
1098
+ row: _createNode( rels, 'is', {
1099
+ children: {
1100
+ row: _createNode( rels, 't', {
1101
+ text: text
1102
+ } )
1103
+ }
1104
+ } )
1105
+ }
1106
+ } );
1107
+ }
1092
1108
  }
1093
1109
 
1094
1110
  rowNode.appendChild( cell );
1095
1111
  }
1112
+
1096
1113
  relsGet.appendChild(rowNode);
1097
1114
  rowPos++;
1098
1115
  };
@@ -1137,7 +1154,8 @@ DataTable.ext.buttons.excelHtml5 = {
1137
1154
  config.customize( xlsx );
1138
1155
  }
1139
1156
 
1140
- var zip = new jsZip();
1157
+ var jszip = _jsZip();
1158
+ var zip = new jszip();
1141
1159
  var zipConfig = {
1142
1160
  type: 'blob',
1143
1161
  mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
@@ -1180,7 +1198,7 @@ DataTable.ext.buttons.pdfHtml5 = {
1180
1198
  className: 'buttons-pdf buttons-html5',
1181
1199
 
1182
1200
  available: function () {
1183
- return window.FileReader !== undefined && pdfMake;
1201
+ return window.FileReader !== undefined && _pdfMake();
1184
1202
  },
1185
1203
 
1186
1204
  text: function ( dt ) {
@@ -1261,11 +1279,11 @@ DataTable.ext.buttons.pdfHtml5 = {
1261
1279
  };
1262
1280
 
1263
1281
  if ( config.message ) {
1264
- doc.content.unshift( {
1265
- text: config.message,
1266
- style: 'message',
1267
- margin: [ 0, 0, 0, 12 ]
1268
- } );
1282
+ doc.content.unshift( {
1283
+ text: typeof config.message == 'function' ? config.message(dt, button, config) : config.message,
1284
+ style: 'message',
1285
+ margin: [ 0, 0, 0, 12 ]
1286
+ } );
1269
1287
  }
1270
1288
 
1271
1289
  if ( config.title ) {
@@ -1280,9 +1298,9 @@ DataTable.ext.buttons.pdfHtml5 = {
1280
1298
  config.customize( doc, config );
1281
1299
  }
1282
1300
 
1283
- var pdf = pdfMake.createPdf( doc );
1301
+ var pdf = _pdfMake().createPdf( doc );
1284
1302
 
1285
- if ( config.download === 'open' && ! _isSafari() ) {
1303
+ if ( config.download === 'open' && ! _isDuffSafari() ) {
1286
1304
  pdf.open();
1287
1305
  }
1288
1306
  else {