effective_datatables 2.12.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +632 -512
  3. data/app/assets/javascripts/dataTables/buttons/buttons.html5.js +176 -177
  4. data/app/assets/javascripts/dataTables/buttons/buttons.print.js +2 -0
  5. data/app/assets/javascripts/dataTables/buttons/dataTables.buttons.js +14 -14
  6. data/app/assets/javascripts/dataTables/dataTables.bootstrap.js +1 -1
  7. data/app/assets/javascripts/dataTables/jquery.dataTables.js +246 -217
  8. data/app/assets/javascripts/effective_datatables.js +2 -3
  9. data/app/assets/javascripts/effective_datatables/events.js.coffee +7 -0
  10. data/app/assets/javascripts/effective_datatables/filters.js.coffee +6 -0
  11. data/app/assets/javascripts/effective_datatables/initialize.js.coffee +42 -39
  12. data/app/assets/javascripts/effective_datatables/reset.js.coffee +7 -0
  13. data/app/assets/javascripts/vendor/jquery.delayedChange.js +1 -1
  14. data/app/assets/stylesheets/dataTables/dataTables.bootstrap.css +0 -1
  15. data/app/assets/stylesheets/effective_datatables.scss +1 -2
  16. data/app/assets/stylesheets/effective_datatables/{_scopes.scss → _filters.scss} +1 -1
  17. data/app/assets/stylesheets/effective_datatables/_overrides.scss +1 -1
  18. data/app/controllers/effective/datatables_controller.rb +2 -4
  19. data/app/helpers/effective_datatables_helper.rb +56 -91
  20. data/app/helpers/effective_datatables_private_helper.rb +55 -64
  21. data/app/models/effective/datatable.rb +103 -177
  22. data/app/models/effective/datatable_column.rb +28 -0
  23. data/app/models/effective/datatable_column_tool.rb +110 -0
  24. data/app/models/effective/datatable_dsl_tool.rb +28 -0
  25. data/app/models/effective/datatable_value_tool.rb +142 -0
  26. data/app/models/effective/effective_datatable/attributes.rb +25 -0
  27. data/app/models/effective/effective_datatable/collection.rb +38 -0
  28. data/app/models/effective/effective_datatable/compute.rb +154 -0
  29. data/app/models/effective/effective_datatable/cookie.rb +29 -0
  30. data/app/models/effective/effective_datatable/dsl.rb +14 -8
  31. data/app/models/effective/effective_datatable/dsl/bulk_actions.rb +5 -6
  32. data/app/models/effective/effective_datatable/dsl/charts.rb +7 -9
  33. data/app/models/effective/effective_datatable/dsl/datatable.rb +107 -57
  34. data/app/models/effective/effective_datatable/dsl/filters.rb +50 -0
  35. data/app/models/effective/effective_datatable/format.rb +157 -0
  36. data/app/models/effective/effective_datatable/hooks.rb +0 -18
  37. data/app/models/effective/effective_datatable/params.rb +34 -0
  38. data/app/models/effective/effective_datatable/resource.rb +108 -0
  39. data/app/models/effective/effective_datatable/state.rb +178 -0
  40. data/app/views/effective/datatables/_actions_column.html.haml +9 -42
  41. data/app/views/effective/datatables/_bulk_actions_column.html.haml +1 -1
  42. data/app/views/effective/datatables/_bulk_actions_dropdown.html.haml +2 -3
  43. data/app/views/effective/datatables/_chart.html.haml +1 -1
  44. data/app/views/effective/datatables/_datatable.html.haml +7 -25
  45. data/app/views/effective/datatables/_filters.html.haml +21 -0
  46. data/app/views/effective/datatables/_reset.html.haml +2 -0
  47. data/app/views/effective/datatables/_resource_column.html.haml +8 -0
  48. data/app/views/effective/datatables/index.html.haml +0 -1
  49. data/config/effective_datatables.rb +9 -32
  50. data/lib/effective_datatables.rb +2 -6
  51. data/lib/effective_datatables/engine.rb +1 -1
  52. data/lib/effective_datatables/version.rb +1 -1
  53. data/lib/generators/effective_datatables/install_generator.rb +2 -2
  54. metadata +39 -19
  55. data/app/assets/javascripts/dataTables/colreorder/dataTables.colReorder.js +0 -27
  56. data/app/assets/javascripts/dataTables/jszip/jszip.js +0 -9155
  57. data/app/assets/javascripts/effective_datatables/scopes.js.coffee +0 -9
  58. data/app/models/effective/active_record_datatable_tool.rb +0 -242
  59. data/app/models/effective/array_datatable_tool.rb +0 -97
  60. data/app/models/effective/effective_datatable/ajax.rb +0 -101
  61. data/app/models/effective/effective_datatable/charts.rb +0 -20
  62. data/app/models/effective/effective_datatable/dsl/scopes.rb +0 -23
  63. data/app/models/effective/effective_datatable/helpers.rb +0 -24
  64. data/app/models/effective/effective_datatable/options.rb +0 -309
  65. data/app/models/effective/effective_datatable/rendering.rb +0 -365
  66. data/app/views/effective/datatables/_scopes.html.haml +0 -21
@@ -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
 
@@ -58,7 +58,7 @@ function _pdfMake () {
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
@@ -676,6 +607,14 @@ var excelStrings = {
676
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="56">'+
701
+ '<cellXfs count="67">'+
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"/>'+
@@ -828,6 +767,17 @@ var excelStrings = {
828
767
  '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
829
768
  '<alignment wrapText="1"/>'+
830
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"/>'+
779
+ '<xf numFmtId="1" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
780
+ '<xf numFmtId="2" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
831
781
  '</cellXfs>'+
832
782
  '<cellStyles count="1">'+
833
783
  '<cellStyle name="Normal" xfId="0" builtinId="0" />'+
@@ -839,6 +789,24 @@ var excelStrings = {
839
789
  // Note we could use 3 `for` loops for the styles, but when gzipped there is
840
790
  // virtually no difference in size, since the above can be easily compressed
841
791
 
792
+ // Pattern matching for special number formats. Perhaps this should be exposed
793
+ // via an API in future?
794
+ // Ref: section 3.8.30 - built in formatters in open spreadsheet
795
+ // https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf
796
+ var _excelSpecials = [
797
+ { match: /^\-?\d+\.\d%$/, style: 60, fmt: function (d) { return d/100; } }, // Precent with d.p.
798
+ { match: /^\-?\d+\.?\d*%$/, style: 56, fmt: function (d) { return d/100; } }, // Percent
799
+ { match: /^\-?\$[\d,]+.?\d*$/, style: 57 }, // Dollars
800
+ { match: /^\-?£[\d,]+.?\d*$/, style: 58 }, // Pounds
801
+ { match: /^\-?€[\d,]+.?\d*$/, style: 59 }, // Euros
802
+ { match: /^\-?\d+$/, style: 65 }, // Numbers without thousand separators
803
+ { match: /^\-?\d+\.\d{2}$/, style: 66 }, // Numbers 2 d.p. without thousands separators
804
+ { match: /^\([\d,]+\)$/, style: 61, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } }, // Negative numbers indicated by brackets
805
+ { match: /^\([\d,]+\.\d{2}\)$/, style: 62, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } }, // Negative numbers indicated by brackets - 2d.p.
806
+ { match: /^\-?[\d,]+$/, style: 63 }, // Numbers with thousand separators
807
+ { match: /^\-?[\d,]+\.\d{2}$/, style: 64 } // Numbers with 2 d.p. and thousands separators
808
+ ];
809
+
842
810
 
843
811
 
844
812
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
@@ -1021,7 +989,7 @@ DataTable.ext.buttons.excelHtml5 = {
1021
989
  className: 'buttons-excel buttons-html5',
1022
990
 
1023
991
  available: function () {
1024
- return window.FileReader !== undefined && _jsZip() !== undefined && ! _isSafari() && _serialiser;
992
+ return window.FileReader !== undefined && _jsZip() !== undefined && ! _isDuffSafari() && _serialiser;
1025
993
  },
1026
994
 
1027
995
  text: function ( dt ) {
@@ -1067,54 +1035,85 @@ DataTable.ext.buttons.excelHtml5 = {
1067
1035
  for ( var i=0, ien=row.length ; i<ien ; i++ ) {
1068
1036
  // Concat both the Cell Columns as a letter and the Row of the cell.
1069
1037
  var cellId = createCellPos(i) + '' + currentRow;
1070
- var cell;
1038
+ var cell = null;
1071
1039
 
1072
- if ( row[i] === null || row[i] === undefined ) {
1073
- row[i] = '';
1040
+ // For null, undefined of blank cell, continue so it doesn't create the _createNode
1041
+ if ( row[i] === null || row[i] === undefined || row[i] === '' ) {
1042
+ continue;
1074
1043
  }
1075
1044
 
1076
- // Detect numbers - don't match numbers with leading zeros or a negative
1077
- // anywhere but the start
1078
- if ( typeof row[i] === 'number' || (
1079
- row[i].match &&
1080
- $.trim(row[i]).match(/^-?\d+(\.\d+)?$/) &&
1081
- ! $.trim(row[i]).match(/^0\d+/) )
1082
- ) {
1083
- cell = _createNode( rels, 'c', {
1084
- attr: {
1085
- t: 'n',
1086
- r: cellId
1087
- },
1088
- children: [
1089
- _createNode( rels, 'v', { text: row[i] } )
1090
- ]
1091
- } );
1092
- }
1093
- else {
1094
- // Replace non standard characters for text output
1095
- var text = ! row[i].replace ?
1096
- row[i] :
1097
- row[i].replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');
1098
-
1099
- cell = _createNode( rels, 'c', {
1100
- attr: {
1101
- t: 'inlineStr',
1102
- r: cellId
1103
- },
1104
- children:{
1105
- row: _createNode( rels, 'is', {
1106
- children: {
1107
- row: _createNode( rels, 't', {
1108
- text: text
1109
- } )
1110
- }
1111
- } )
1045
+ row[i] = $.trim( row[i] );
1046
+
1047
+ // Special number formatting options
1048
+ for ( var j=0, jen=_excelSpecials.length ; j<jen ; j++ ) {
1049
+ var special = _excelSpecials[j];
1050
+
1051
+ if ( row[i].match && row[i].match( special.match ) ) {
1052
+ var val = row[i].replace(/[^\d\.\-]/g, '');
1053
+
1054
+ if ( special.fmt ) {
1055
+ val = special.fmt( val );
1112
1056
  }
1113
- } );
1057
+
1058
+ cell = _createNode( rels, 'c', {
1059
+ attr: {
1060
+ r: cellId,
1061
+ s: special.style
1062
+ },
1063
+ children: [
1064
+ _createNode( rels, 'v', { text: val } )
1065
+ ]
1066
+ } );
1067
+
1068
+ break;
1069
+ }
1070
+ }
1071
+
1072
+ if ( ! cell ) {
1073
+ if ( typeof row[i] === 'number' || (
1074
+ row[i].match &&
1075
+ row[i].match(/^-?\d+(\.\d+)?$/) &&
1076
+ ! row[i].match(/^0\d+/) )
1077
+ ) {
1078
+ // Detect numbers - don't match numbers with leading zeros
1079
+ // or a negative anywhere but the start
1080
+ cell = _createNode( rels, 'c', {
1081
+ attr: {
1082
+ t: 'n',
1083
+ r: cellId
1084
+ },
1085
+ children: [
1086
+ _createNode( rels, 'v', { text: row[i] } )
1087
+ ]
1088
+ } );
1089
+ }
1090
+ else {
1091
+ // String output - replace non standard characters for text output
1092
+ var text = ! row[i].replace ?
1093
+ row[i] :
1094
+ row[i].replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');
1095
+
1096
+ cell = _createNode( rels, 'c', {
1097
+ attr: {
1098
+ t: 'inlineStr',
1099
+ r: cellId
1100
+ },
1101
+ children:{
1102
+ row: _createNode( rels, 'is', {
1103
+ children: {
1104
+ row: _createNode( rels, 't', {
1105
+ text: text
1106
+ } )
1107
+ }
1108
+ } )
1109
+ }
1110
+ } );
1111
+ }
1114
1112
  }
1115
1113
 
1116
1114
  rowNode.appendChild( cell );
1117
1115
  }
1116
+
1118
1117
  relsGet.appendChild(rowNode);
1119
1118
  rowPos++;
1120
1119
  };
@@ -1305,7 +1304,7 @@ DataTable.ext.buttons.pdfHtml5 = {
1305
1304
 
1306
1305
  var pdf = _pdfMake().createPdf( doc );
1307
1306
 
1308
- if ( config.download === 'open' && ! _isSafari() ) {
1307
+ if ( config.download === 'open' && ! _isDuffSafari() ) {
1309
1308
  pdf.open();
1310
1309
  }
1311
1310
  else {