cherrypy-foundation 1.0.0__py3-none-any.whl

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 (136) hide show
  1. cherrypy_foundation/__init__.py +0 -0
  2. cherrypy_foundation/components/ColorModes.jinja +70 -0
  3. cherrypy_foundation/components/Datatable.css +47 -0
  4. cherrypy_foundation/components/Datatable.jinja +63 -0
  5. cherrypy_foundation/components/Datatable.js +358 -0
  6. cherrypy_foundation/components/Field.css +10 -0
  7. cherrypy_foundation/components/Field.jinja +66 -0
  8. cherrypy_foundation/components/Field.js +56 -0
  9. cherrypy_foundation/components/Fields.jinja +4 -0
  10. cherrypy_foundation/components/Flash.jinja +13 -0
  11. cherrypy_foundation/components/Icon.jinja +3 -0
  12. cherrypy_foundation/components/LocaleSelection.jinja +13 -0
  13. cherrypy_foundation/components/LocaleSelection.js +26 -0
  14. cherrypy_foundation/components/SideBySideMultiSelect.css +25 -0
  15. cherrypy_foundation/components/SideBySideMultiSelect.jinja +9 -0
  16. cherrypy_foundation/components/SideBySideMultiSelect.js +9 -0
  17. cherrypy_foundation/components/Typeahead.css +55 -0
  18. cherrypy_foundation/components/Typeahead.jinja +106 -0
  19. cherrypy_foundation/components/Typeahead.js +8 -0
  20. cherrypy_foundation/components/__init__.py +51 -0
  21. cherrypy_foundation/components/tests/__init__.py +0 -0
  22. cherrypy_foundation/components/tests/test_static.py +90 -0
  23. cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.css +2106 -0
  24. cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.min.css +5 -0
  25. cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
  26. cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
  27. cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.css +9262 -0
  28. cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.css.map +95 -0
  29. cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.min.css +6 -0
  30. cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.min.css.map +7 -0
  31. cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.js +4846 -0
  32. cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.js.map +1 -0
  33. cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.min.js +7 -0
  34. cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.min.js.map +7 -0
  35. cherrypy_foundation/components/vendor/bootstrap5/js/color-modes.js +80 -0
  36. cherrypy_foundation/components/vendor/datatables/css/dataTables.dataTables.css +849 -0
  37. cherrypy_foundation/components/vendor/datatables/css/dataTables.dataTables.min.css +1 -0
  38. cherrypy_foundation/components/vendor/datatables/images/sort_asc.png +0 -0
  39. cherrypy_foundation/components/vendor/datatables/images/sort_asc_disabled.png +0 -0
  40. cherrypy_foundation/components/vendor/datatables/images/sort_both.png +0 -0
  41. cherrypy_foundation/components/vendor/datatables/images/sort_desc.png +0 -0
  42. cherrypy_foundation/components/vendor/datatables/images/sort_desc_disabled.png +0 -0
  43. cherrypy_foundation/components/vendor/datatables/js/dataTables.js +14073 -0
  44. cherrypy_foundation/components/vendor/datatables/js/dataTables.min.js +4 -0
  45. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/css/buttons.dataTables.css +556 -0
  46. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/css/buttons.dataTables.min.css +1 -0
  47. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/buttons.html5.js +1700 -0
  48. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/buttons.html5.min.js +8 -0
  49. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/dataTables.buttons.js +2944 -0
  50. cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/dataTables.buttons.min.js +4 -0
  51. cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.css +13 -0
  52. cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.min.css +1 -0
  53. cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.js +1202 -0
  54. cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.min.js +4 -0
  55. cherrypy_foundation/components/vendor/datatables-extensions/JSZip/jszip.js +11577 -0
  56. cherrypy_foundation/components/vendor/datatables-extensions/JSZip/jszip.min.js +13 -0
  57. cherrypy_foundation/components/vendor/datatables-extensions/Responsive/css/responsive.dataTables.css +194 -0
  58. cherrypy_foundation/components/vendor/datatables-extensions/Responsive/css/responsive.dataTables.min.css +1 -0
  59. cherrypy_foundation/components/vendor/datatables-extensions/Responsive/js/dataTables.responsive.js +1861 -0
  60. cherrypy_foundation/components/vendor/datatables-extensions/Responsive/js/dataTables.responsive.min.js +4 -0
  61. cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/pdfmake.js +75023 -0
  62. cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/pdfmake.min.js +3 -0
  63. cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/vfs_fonts.js +6 -0
  64. cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.css +53 -0
  65. cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.min.css +1 -0
  66. cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.js +485 -0
  67. cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.min.js +4 -0
  68. cherrypy_foundation/components/vendor/jquery/jquery.min.js +2 -0
  69. cherrypy_foundation/components/vendor/multi/LICENSE +7 -0
  70. cherrypy_foundation/components/vendor/multi/README.md +109 -0
  71. cherrypy_foundation/components/vendor/multi/multi.css +95 -0
  72. cherrypy_foundation/components/vendor/multi/multi.js +328 -0
  73. cherrypy_foundation/components/vendor/popper/popper.js +1825 -0
  74. cherrypy_foundation/components/vendor/popper/popper.min.js +6 -0
  75. cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.css +1 -0
  76. cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.js +10 -0
  77. cherrypy_foundation/error_page.py +94 -0
  78. cherrypy_foundation/flash.py +50 -0
  79. cherrypy_foundation/form.py +119 -0
  80. cherrypy_foundation/logging.py +103 -0
  81. cherrypy_foundation/passwd.py +65 -0
  82. cherrypy_foundation/plugins/__init__.py +0 -0
  83. cherrypy_foundation/plugins/db.py +286 -0
  84. cherrypy_foundation/plugins/ldap.py +257 -0
  85. cherrypy_foundation/plugins/restapi.py +74 -0
  86. cherrypy_foundation/plugins/scheduler.py +287 -0
  87. cherrypy_foundation/plugins/smtp.py +223 -0
  88. cherrypy_foundation/plugins/tests/__init__.py +0 -0
  89. cherrypy_foundation/plugins/tests/test_db.py +118 -0
  90. cherrypy_foundation/plugins/tests/test_ldap.py +451 -0
  91. cherrypy_foundation/plugins/tests/test_scheduler.py +100 -0
  92. cherrypy_foundation/plugins/tests/test_scheduler_db.py +107 -0
  93. cherrypy_foundation/plugins/tests/test_smtp.py +140 -0
  94. cherrypy_foundation/sessions.py +93 -0
  95. cherrypy_foundation/tests/__init__.py +72 -0
  96. cherrypy_foundation/tests/templates/test_flash.html +9 -0
  97. cherrypy_foundation/tests/templates/test_form.html +16 -0
  98. cherrypy_foundation/tests/templates/test_url.html +15 -0
  99. cherrypy_foundation/tests/test_error_page.py +78 -0
  100. cherrypy_foundation/tests/test_flash.py +61 -0
  101. cherrypy_foundation/tests/test_form.py +148 -0
  102. cherrypy_foundation/tests/test_logging.py +78 -0
  103. cherrypy_foundation/tests/test_passwd.py +51 -0
  104. cherrypy_foundation/tests/test_sessions.py +89 -0
  105. cherrypy_foundation/tests/test_url.py +161 -0
  106. cherrypy_foundation/tools/__init__.py +0 -0
  107. cherrypy_foundation/tools/auth.py +263 -0
  108. cherrypy_foundation/tools/auth_mfa.py +249 -0
  109. cherrypy_foundation/tools/i18n.py +529 -0
  110. cherrypy_foundation/tools/jinja2.py +158 -0
  111. cherrypy_foundation/tools/ratelimit.py +265 -0
  112. cherrypy_foundation/tools/secure_headers.py +119 -0
  113. cherrypy_foundation/tools/sessions_timeout.py +167 -0
  114. cherrypy_foundation/tools/tests/__init__.py +0 -0
  115. cherrypy_foundation/tools/tests/components/Button.jinja +2 -0
  116. cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.mo +0 -0
  117. cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.po +15 -0
  118. cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.mo +0 -0
  119. cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.po +15 -0
  120. cherrypy_foundation/tools/tests/locales/messages.pot +2 -0
  121. cherrypy_foundation/tools/tests/templates/test_jinja2.html +11 -0
  122. cherrypy_foundation/tools/tests/templates/test_jinjax.html +9 -0
  123. cherrypy_foundation/tools/tests/templates/test_jinjax_i18n.html +22 -0
  124. cherrypy_foundation/tools/tests/test_auth.py +110 -0
  125. cherrypy_foundation/tools/tests/test_auth_mfa.py +369 -0
  126. cherrypy_foundation/tools/tests/test_i18n.py +247 -0
  127. cherrypy_foundation/tools/tests/test_jinja2.py +153 -0
  128. cherrypy_foundation/tools/tests/test_ratelimit.py +109 -0
  129. cherrypy_foundation/tools/tests/test_secure_headers.py +200 -0
  130. cherrypy_foundation/url.py +66 -0
  131. cherrypy_foundation/widgets.py +48 -0
  132. cherrypy_foundation-1.0.0.dist-info/METADATA +71 -0
  133. cherrypy_foundation-1.0.0.dist-info/RECORD +136 -0
  134. cherrypy_foundation-1.0.0.dist-info/WHEEL +5 -0
  135. cherrypy_foundation-1.0.0.dist-info/licenses/LICENSE.md +674 -0
  136. cherrypy_foundation-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1700 @@
1
+ /*!
2
+ * HTML5 export buttons for Buttons and DataTables.
3
+ * © SpryMedia Ltd - datatables.net/license
4
+ *
5
+ * FileSaver.js (1.3.3) - MIT license
6
+ * Copyright © 2016 Eli Grey - http://eligrey.com
7
+ */
8
+
9
+ (function( factory ){
10
+ if ( typeof define === 'function' && define.amd ) {
11
+ // AMD
12
+ define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
13
+ return factory( $, window, document );
14
+ } );
15
+ }
16
+ else if ( typeof exports === 'object' ) {
17
+ // CommonJS
18
+ var jq = require('jquery');
19
+ var cjsRequires = function (root, $) {
20
+ if ( ! $.fn.dataTable ) {
21
+ require('datatables.net')(root, $);
22
+ }
23
+
24
+ if ( ! $.fn.dataTable.Buttons ) {
25
+ require('datatables.net-buttons')(root, $);
26
+ }
27
+ };
28
+
29
+ if (typeof window === 'undefined') {
30
+ module.exports = function (root, $) {
31
+ if ( ! root ) {
32
+ // CommonJS environments without a window global must pass a
33
+ // root. This will give an error otherwise
34
+ root = window;
35
+ }
36
+
37
+ if ( ! $ ) {
38
+ $ = jq( root );
39
+ }
40
+
41
+ cjsRequires( root, $ );
42
+ return factory( $, root, root.document );
43
+ };
44
+ }
45
+ else {
46
+ cjsRequires( window, jq );
47
+ module.exports = factory( jq, window, window.document );
48
+ }
49
+ }
50
+ else {
51
+ // Browser
52
+ factory( jQuery, window, document );
53
+ }
54
+ }(function( $, window, document ) {
55
+ 'use strict';
56
+ var DataTable = $.fn.dataTable;
57
+
58
+
59
+
60
+ // Allow the constructor to pass in JSZip and PDFMake from external requires.
61
+ // Otherwise, use globally defined variables, if they are available.
62
+ var useJszip;
63
+ var usePdfmake;
64
+
65
+ function _jsZip() {
66
+ return useJszip || window.JSZip;
67
+ }
68
+ function _pdfMake() {
69
+ return usePdfmake || window.pdfMake;
70
+ }
71
+
72
+ DataTable.Buttons.pdfMake = function (_) {
73
+ if (!_) {
74
+ return _pdfMake();
75
+ }
76
+ usePdfmake = _;
77
+ };
78
+
79
+ DataTable.Buttons.jszip = function (_) {
80
+ if (!_) {
81
+ return _jsZip();
82
+ }
83
+ useJszip = _;
84
+ };
85
+
86
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
87
+ * FileSaver.js dependency
88
+ */
89
+
90
+ /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
91
+
92
+ var _saveAs = (function (view) {
93
+ 'use strict';
94
+ // IE <10 is explicitly unsupported
95
+ if (
96
+ typeof view === 'undefined' ||
97
+ (typeof navigator !== 'undefined' &&
98
+ /MSIE [1-9]\./.test(navigator.userAgent))
99
+ ) {
100
+ return;
101
+ }
102
+ var doc = view.document,
103
+ // only get URL when necessary in case Blob.js hasn't overridden it yet
104
+ get_URL = function () {
105
+ return view.URL || view.webkitURL || view;
106
+ },
107
+ save_link = doc.createElementNS('http://www.w3.org/1999/xhtml', 'a'),
108
+ can_use_save_link = 'download' in save_link,
109
+ click = function (node) {
110
+ var event = new MouseEvent('click');
111
+ node.dispatchEvent(event);
112
+ },
113
+ is_safari = /constructor/i.test(view.HTMLElement) || view.safari,
114
+ is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent),
115
+ throw_outside = function (ex) {
116
+ (view.setImmediate || view.setTimeout)(function () {
117
+ throw ex;
118
+ }, 0);
119
+ },
120
+ force_saveable_type = 'application/octet-stream',
121
+ // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
122
+ arbitrary_revoke_timeout = 1000 * 40, // in ms
123
+ revoke = function (file) {
124
+ var revoker = function () {
125
+ if (typeof file === 'string') {
126
+ // file is an object URL
127
+ get_URL().revokeObjectURL(file);
128
+ }
129
+ else {
130
+ // file is a File
131
+ file.remove();
132
+ }
133
+ };
134
+ setTimeout(revoker, arbitrary_revoke_timeout);
135
+ },
136
+ dispatch = function (filesaver, event_types, event) {
137
+ event_types = [].concat(event_types);
138
+ var i = event_types.length;
139
+ while (i--) {
140
+ var listener = filesaver['on' + event_types[i]];
141
+ if (typeof listener === 'function') {
142
+ try {
143
+ listener.call(filesaver, event || filesaver);
144
+ } catch (ex) {
145
+ throw_outside(ex);
146
+ }
147
+ }
148
+ }
149
+ },
150
+ auto_bom = function (blob) {
151
+ // prepend BOM for UTF-8 XML and text/* types (including HTML)
152
+ // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
153
+ if (
154
+ /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(
155
+ blob.type
156
+ )
157
+ ) {
158
+ return new Blob([String.fromCharCode(0xfeff), blob], {
159
+ type: blob.type
160
+ });
161
+ }
162
+ return blob;
163
+ },
164
+ FileSaver = function (blob, name, no_auto_bom) {
165
+ if (!no_auto_bom) {
166
+ blob = auto_bom(blob);
167
+ }
168
+ // First try a.download, then web filesystem, then object URLs
169
+ var filesaver = this,
170
+ type = blob.type,
171
+ force = type === force_saveable_type,
172
+ object_url,
173
+ dispatch_all = function () {
174
+ dispatch(
175
+ filesaver,
176
+ 'writestart progress write writeend'.split(' ')
177
+ );
178
+ },
179
+ // on any filesys errors revert to saving with object URLs
180
+ fs_error = function () {
181
+ if (
182
+ (is_chrome_ios || (force && is_safari)) &&
183
+ view.FileReader
184
+ ) {
185
+ // Safari doesn't allow downloading of blob urls
186
+ var reader = new FileReader();
187
+ reader.onloadend = function () {
188
+ var url = is_chrome_ios
189
+ ? reader.result
190
+ : reader.result.replace(
191
+ /^data:[^;]*;/,
192
+ 'data:attachment/file;'
193
+ );
194
+ var popup = view.open(url, '_blank');
195
+ if (!popup) view.location.href = url;
196
+ url = undefined; // release reference before dispatching
197
+ filesaver.readyState = filesaver.DONE;
198
+ dispatch_all();
199
+ };
200
+ reader.readAsDataURL(blob);
201
+ filesaver.readyState = filesaver.INIT;
202
+ return;
203
+ }
204
+ // don't create more object URLs than needed
205
+ if (!object_url) {
206
+ object_url = get_URL().createObjectURL(blob);
207
+ }
208
+ if (force) {
209
+ view.location.href = object_url;
210
+ }
211
+ else {
212
+ var opened = view.open(object_url, '_blank');
213
+ if (!opened) {
214
+ // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
215
+ view.location.href = object_url;
216
+ }
217
+ }
218
+ filesaver.readyState = filesaver.DONE;
219
+ dispatch_all();
220
+ revoke(object_url);
221
+ };
222
+ filesaver.readyState = filesaver.INIT;
223
+
224
+ if (can_use_save_link) {
225
+ object_url = get_URL().createObjectURL(blob);
226
+ setTimeout(function () {
227
+ save_link.href = object_url;
228
+ save_link.download = name;
229
+ click(save_link);
230
+ dispatch_all();
231
+ revoke(object_url);
232
+ filesaver.readyState = filesaver.DONE;
233
+ });
234
+ return;
235
+ }
236
+
237
+ fs_error();
238
+ },
239
+ FS_proto = FileSaver.prototype,
240
+ saveAs = function (blob, name, no_auto_bom) {
241
+ return new FileSaver(
242
+ blob,
243
+ name || blob.name || 'download',
244
+ no_auto_bom
245
+ );
246
+ };
247
+ // IE 10+ (native saveAs)
248
+ if (typeof navigator !== 'undefined' && navigator.msSaveOrOpenBlob) {
249
+ return function (blob, name, no_auto_bom) {
250
+ name = name || blob.name || 'download';
251
+
252
+ if (!no_auto_bom) {
253
+ blob = auto_bom(blob);
254
+ }
255
+ return navigator.msSaveOrOpenBlob(blob, name);
256
+ };
257
+ }
258
+
259
+ FS_proto.abort = function () {};
260
+ FS_proto.readyState = FS_proto.INIT = 0;
261
+ FS_proto.WRITING = 1;
262
+ FS_proto.DONE = 2;
263
+
264
+ FS_proto.error =
265
+ FS_proto.onwritestart =
266
+ FS_proto.onprogress =
267
+ FS_proto.onwrite =
268
+ FS_proto.onabort =
269
+ FS_proto.onerror =
270
+ FS_proto.onwriteend =
271
+ null;
272
+
273
+ return saveAs;
274
+ })(
275
+ (typeof self !== 'undefined' && self) ||
276
+ (typeof window !== 'undefined' && window) ||
277
+ this.content
278
+ );
279
+
280
+ // Expose file saver on the DataTables API. Can't attach to `DataTables.Buttons`
281
+ // since this file can be loaded before Button's core!
282
+ DataTable.fileSave = _saveAs;
283
+
284
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
285
+ * Local (private) functions
286
+ */
287
+
288
+ /**
289
+ * Get the sheet name for Excel exports.
290
+ *
291
+ * @param {object} config Button configuration
292
+ */
293
+ var _sheetname = function (config) {
294
+ var sheetName = 'Sheet1';
295
+
296
+ if (config.sheetName) {
297
+ sheetName = config.sheetName.replace(/[\[\]\*\/\\\?\:]/g, '');
298
+ }
299
+
300
+ return sheetName;
301
+ };
302
+
303
+ /**
304
+ * Get the newline character(s)
305
+ *
306
+ * @param {object} config Button configuration
307
+ * @return {string} Newline character
308
+ */
309
+ var _newLine = function (config) {
310
+ return config.newline
311
+ ? config.newline
312
+ : navigator.userAgent.match(/Windows/)
313
+ ? '\r\n'
314
+ : '\n';
315
+ };
316
+
317
+ /**
318
+ * Combine the data from the `buttons.exportData` method into a string that
319
+ * will be used in the export file.
320
+ *
321
+ * @param {DataTable.Api} dt DataTables API instance
322
+ * @param {object} config Button configuration
323
+ * @return {object} The data to export
324
+ */
325
+ var _exportData = function (dt, config) {
326
+ var newLine = _newLine(config);
327
+ var data = dt.buttons.exportData(config.exportOptions);
328
+ var boundary = config.fieldBoundary;
329
+ var separator = config.fieldSeparator;
330
+ var reBoundary = new RegExp(boundary, 'g');
331
+ var escapeChar = config.escapeChar !== undefined ? config.escapeChar : '\\';
332
+ var join = function (a) {
333
+ var s = '';
334
+
335
+ // If there is a field boundary, then we might need to escape it in
336
+ // the source data
337
+ for (var i = 0, ien = a.length; i < ien; i++) {
338
+ if (i > 0) {
339
+ s += separator;
340
+ }
341
+
342
+ s += boundary
343
+ ? boundary +
344
+ ('' + a[i]).replace(reBoundary, escapeChar + boundary) +
345
+ boundary
346
+ : a[i];
347
+ }
348
+
349
+ return s;
350
+ };
351
+
352
+ var header = '';
353
+ var footer = '';
354
+ var body = [];
355
+
356
+ if (config.header) {
357
+ header =
358
+ data.headerStructure
359
+ .map(function (row) {
360
+ return join(
361
+ row.map(function (cell) {
362
+ return cell ? cell.title : '';
363
+ })
364
+ );
365
+ })
366
+ .join(newLine) + newLine;
367
+ }
368
+
369
+ if (config.footer && data.footer) {
370
+ footer =
371
+ data.footerStructure
372
+ .map(function (row) {
373
+ return join(
374
+ row.map(function (cell) {
375
+ return cell ? cell.title : '';
376
+ })
377
+ );
378
+ })
379
+ .join(newLine) + newLine;
380
+ }
381
+
382
+ for (var i = 0, ien = data.body.length; i < ien; i++) {
383
+ body.push(join(data.body[i]));
384
+ }
385
+
386
+ return {
387
+ str: header + body.join(newLine) + newLine + footer,
388
+ rows: body.length
389
+ };
390
+ };
391
+
392
+ /**
393
+ * Older versions of Safari (prior to tech preview 18) don't support the
394
+ * download option required.
395
+ *
396
+ * @return {Boolean} `true` if old Safari
397
+ */
398
+ var _isDuffSafari = function () {
399
+ var safari =
400
+ navigator.userAgent.indexOf('Safari') !== -1 &&
401
+ navigator.userAgent.indexOf('Chrome') === -1 &&
402
+ navigator.userAgent.indexOf('Opera') === -1;
403
+
404
+ if (!safari) {
405
+ return false;
406
+ }
407
+
408
+ var version = navigator.userAgent.match(/AppleWebKit\/(\d+\.\d+)/);
409
+ if (version && version.length > 1 && version[1] * 1 < 603.1) {
410
+ return true;
411
+ }
412
+
413
+ return false;
414
+ };
415
+
416
+ /**
417
+ * Convert from numeric position to letter for column names in Excel
418
+ * @param {int} n Column number
419
+ * @return {string} Column letter(s) name
420
+ */
421
+ function createCellPos(n) {
422
+ var ordA = 'A'.charCodeAt(0);
423
+ var ordZ = 'Z'.charCodeAt(0);
424
+ var len = ordZ - ordA + 1;
425
+ var s = '';
426
+
427
+ while (n >= 0) {
428
+ s = String.fromCharCode((n % len) + ordA) + s;
429
+ n = Math.floor(n / len) - 1;
430
+ }
431
+
432
+ return s;
433
+ }
434
+
435
+ try {
436
+ var _serialiser = new XMLSerializer();
437
+ var _ieExcel;
438
+ } catch (t) {
439
+ // noop
440
+ }
441
+
442
+ /**
443
+ * Recursively add XML files from an object's structure to a ZIP file. This
444
+ * allows the XSLX file to be easily defined with an object's structure matching
445
+ * the files structure.
446
+ *
447
+ * @param {JSZip} zip ZIP package
448
+ * @param {object} obj Object to add (recursive)
449
+ */
450
+ function _addToZip(zip, obj) {
451
+ if (_ieExcel === undefined) {
452
+ // Detect if we are dealing with IE's _awful_ serialiser by seeing if it
453
+ // drop attributes
454
+ _ieExcel =
455
+ _serialiser
456
+ .serializeToString(
457
+ new window.DOMParser().parseFromString(
458
+ excelStrings['xl/worksheets/sheet1.xml'],
459
+ 'text/xml'
460
+ )
461
+ )
462
+ .indexOf('xmlns:r') === -1;
463
+ }
464
+
465
+ $.each(obj, function (name, val) {
466
+ if ($.isPlainObject(val)) {
467
+ var newDir = zip.folder(name);
468
+ _addToZip(newDir, val);
469
+ }
470
+ else {
471
+ if (_ieExcel) {
472
+ // IE's XML serialiser will drop some name space attributes from
473
+ // from the root node, so we need to save them. Do this by
474
+ // replacing the namespace nodes with a regular attribute that
475
+ // we convert back when serialised. Edge does not have this
476
+ // issue
477
+ var worksheet = val.childNodes[0];
478
+ var i, ien;
479
+ var attrs = [];
480
+
481
+ for (i = worksheet.attributes.length - 1; i >= 0; i--) {
482
+ var attrName = worksheet.attributes[i].nodeName;
483
+ var attrValue = worksheet.attributes[i].nodeValue;
484
+
485
+ if (attrName.indexOf(':') !== -1) {
486
+ attrs.push({ name: attrName, value: attrValue });
487
+
488
+ worksheet.removeAttribute(attrName);
489
+ }
490
+ }
491
+
492
+ for (i = 0, ien = attrs.length; i < ien; i++) {
493
+ var attr = val.createAttribute(
494
+ attrs[i].name.replace(':', '_dt_b_namespace_token_')
495
+ );
496
+ attr.value = attrs[i].value;
497
+ worksheet.setAttributeNode(attr);
498
+ }
499
+ }
500
+
501
+ var str = _serialiser.serializeToString(val);
502
+
503
+ // Fix IE's XML
504
+ if (_ieExcel) {
505
+ // IE doesn't include the XML declaration
506
+ if (str.indexOf('<?xml') === -1) {
507
+ str =
508
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
509
+ str;
510
+ }
511
+
512
+ // Return namespace attributes to being as such
513
+ str = str.replace(/_dt_b_namespace_token_/g, ':');
514
+
515
+ // Remove testing name space that IE puts into the space preserve attr
516
+ str = str.replace(/xmlns:NS[\d]+="" NS[\d]+:/g, '');
517
+ }
518
+
519
+ // Safari, IE and Edge will put empty name space attributes onto
520
+ // various elements making them useless. This strips them out
521
+ str = str.replace(/<([^<>]*?) xmlns=""([^<>]*?)>/g, '<$1 $2>');
522
+
523
+ zip.file(name, str);
524
+ }
525
+ });
526
+ }
527
+
528
+ /**
529
+ * Create an XML node and add any children, attributes, etc without needing to
530
+ * be verbose in the DOM.
531
+ *
532
+ * @param {object} doc XML document
533
+ * @param {string} nodeName Node name
534
+ * @param {object} opts Options - can be `attr` (attributes), `children`
535
+ * (child nodes) and `text` (text content)
536
+ * @return {node} Created node
537
+ */
538
+ function _createNode(doc, nodeName, opts) {
539
+ var tempNode = doc.createElement(nodeName);
540
+
541
+ if (opts) {
542
+ if (opts.attr) {
543
+ $(tempNode).attr(opts.attr);
544
+ }
545
+
546
+ if (opts.children) {
547
+ $.each(opts.children, function (key, value) {
548
+ tempNode.appendChild(value);
549
+ });
550
+ }
551
+
552
+ if (opts.text !== null && opts.text !== undefined) {
553
+ tempNode.appendChild(doc.createTextNode(opts.text));
554
+ }
555
+ }
556
+
557
+ return tempNode;
558
+ }
559
+
560
+ /**
561
+ * Get the width for an Excel column based on the contents of that column
562
+ * @param {object} data Data for export
563
+ * @param {int} col Column index
564
+ * @return {int} Column width
565
+ */
566
+ function _excelColWidth(data, col) {
567
+ var max = data.header[col].length;
568
+ var len, lineSplit, str;
569
+
570
+ if (data.footer && data.footer[col] && data.footer[col].length > max) {
571
+ max = data.footer[col].length;
572
+ }
573
+
574
+ for (var i = 0, ien = data.body.length; i < ien; i++) {
575
+ var point = data.body[i][col];
576
+ str = point !== null && point !== undefined ? point.toString() : '';
577
+
578
+ // If there is a newline character, workout the width of the column
579
+ // based on the longest line in the string
580
+ if (str.indexOf('\n') !== -1) {
581
+ lineSplit = str.split('\n');
582
+ lineSplit.sort(function (a, b) {
583
+ return b.length - a.length;
584
+ });
585
+
586
+ len = lineSplit[0].length;
587
+ }
588
+ else {
589
+ len = str.length;
590
+ }
591
+
592
+ if (len > max) {
593
+ max = len;
594
+ }
595
+
596
+ // Max width rather than having potentially massive column widths
597
+ if (max > 40) {
598
+ return 54; // 40 * 1.35
599
+ }
600
+ }
601
+
602
+ max *= 1.35;
603
+
604
+ // And a min width
605
+ return max > 6 ? max : 6;
606
+ }
607
+
608
+ // Excel - Pre-defined strings to build a basic XLSX file
609
+ var excelStrings = {
610
+ '_rels/.rels':
611
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
612
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
613
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' +
614
+ '</Relationships>',
615
+
616
+ 'xl/_rels/workbook.xml.rels':
617
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
618
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
619
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>' +
620
+ '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>' +
621
+ '</Relationships>',
622
+
623
+ '[Content_Types].xml':
624
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
625
+ '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
626
+ '<Default Extension="xml" ContentType="application/xml" />' +
627
+ '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />' +
628
+ '<Default Extension="jpeg" ContentType="image/jpeg" />' +
629
+ '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" />' +
630
+ '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" />' +
631
+ '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" />' +
632
+ '</Types>',
633
+
634
+ 'xl/workbook.xml':
635
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
636
+ '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' +
637
+ '<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>' +
638
+ '<workbookPr showInkAnnotation="0" autoCompressPictures="0"/>' +
639
+ '<bookViews>' +
640
+ '<workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>' +
641
+ '</bookViews>' +
642
+ '<sheets>' +
643
+ '<sheet name="Sheet1" sheetId="1" r:id="rId1"/>' +
644
+ '</sheets>' +
645
+ '<definedNames/>' +
646
+ '</workbook>',
647
+
648
+ 'xl/worksheets/sheet1.xml':
649
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
650
+ '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">' +
651
+ '<sheetData/>' +
652
+ '<mergeCells count="0"/>' +
653
+ '</worksheet>',
654
+
655
+ 'xl/styles.xml':
656
+ '<?xml version="1.0" encoding="UTF-8"?>' +
657
+ '<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">' +
658
+ '<numFmts count="6">' +
659
+ '<numFmt numFmtId="164" formatCode="[$$-409]#,##0.00;-[$$-409]#,##0.00"/>' +
660
+ '<numFmt numFmtId="165" formatCode="&quot;£&quot;#,##0.00"/>' +
661
+ '<numFmt numFmtId="166" formatCode="[$€-2] #,##0.00"/>' +
662
+ '<numFmt numFmtId="167" formatCode="0.0%"/>' +
663
+ '<numFmt numFmtId="168" formatCode="#,##0;(#,##0)"/>' +
664
+ '<numFmt numFmtId="169" formatCode="#,##0.00;(#,##0.00)"/>' +
665
+ '</numFmts>' +
666
+ '<fonts count="5" x14ac:knownFonts="1">' +
667
+ '<font>' +
668
+ '<sz val="11" />' +
669
+ '<name val="Calibri" />' +
670
+ '</font>' +
671
+ '<font>' +
672
+ '<sz val="11" />' +
673
+ '<name val="Calibri" />' +
674
+ '<color rgb="FFFFFFFF" />' +
675
+ '</font>' +
676
+ '<font>' +
677
+ '<sz val="11" />' +
678
+ '<name val="Calibri" />' +
679
+ '<b />' +
680
+ '</font>' +
681
+ '<font>' +
682
+ '<sz val="11" />' +
683
+ '<name val="Calibri" />' +
684
+ '<i />' +
685
+ '</font>' +
686
+ '<font>' +
687
+ '<sz val="11" />' +
688
+ '<name val="Calibri" />' +
689
+ '<u />' +
690
+ '</font>' +
691
+ '</fonts>' +
692
+ '<fills count="6">' +
693
+ '<fill>' +
694
+ '<patternFill patternType="none" />' +
695
+ '</fill>' +
696
+ '<fill>' + // Excel appears to use this as a dotted background regardless of values but
697
+ '<patternFill patternType="none" />' + // to be valid to the schema, use a patternFill
698
+ '</fill>' +
699
+ '<fill>' +
700
+ '<patternFill patternType="solid">' +
701
+ '<fgColor rgb="FFD9D9D9" />' +
702
+ '<bgColor indexed="64" />' +
703
+ '</patternFill>' +
704
+ '</fill>' +
705
+ '<fill>' +
706
+ '<patternFill patternType="solid">' +
707
+ '<fgColor rgb="FFD99795" />' +
708
+ '<bgColor indexed="64" />' +
709
+ '</patternFill>' +
710
+ '</fill>' +
711
+ '<fill>' +
712
+ '<patternFill patternType="solid">' +
713
+ '<fgColor rgb="ffc6efce" />' +
714
+ '<bgColor indexed="64" />' +
715
+ '</patternFill>' +
716
+ '</fill>' +
717
+ '<fill>' +
718
+ '<patternFill patternType="solid">' +
719
+ '<fgColor rgb="ffc6cfef" />' +
720
+ '<bgColor indexed="64" />' +
721
+ '</patternFill>' +
722
+ '</fill>' +
723
+ '</fills>' +
724
+ '<borders count="2">' +
725
+ '<border>' +
726
+ '<left />' +
727
+ '<right />' +
728
+ '<top />' +
729
+ '<bottom />' +
730
+ '<diagonal />' +
731
+ '</border>' +
732
+ '<border diagonalUp="false" diagonalDown="false">' +
733
+ '<left style="thin">' +
734
+ '<color auto="1" />' +
735
+ '</left>' +
736
+ '<right style="thin">' +
737
+ '<color auto="1" />' +
738
+ '</right>' +
739
+ '<top style="thin">' +
740
+ '<color auto="1" />' +
741
+ '</top>' +
742
+ '<bottom style="thin">' +
743
+ '<color auto="1" />' +
744
+ '</bottom>' +
745
+ '<diagonal />' +
746
+ '</border>' +
747
+ '</borders>' +
748
+ '<cellStyleXfs count="1">' +
749
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />' +
750
+ '</cellStyleXfs>' +
751
+ '<cellXfs count="68">' +
752
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
753
+ '<xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
754
+ '<xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
755
+ '<xf numFmtId="0" fontId="3" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
756
+ '<xf numFmtId="0" fontId="4" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
757
+ '<xf numFmtId="0" fontId="0" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
758
+ '<xf numFmtId="0" fontId="1" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
759
+ '<xf numFmtId="0" fontId="2" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
760
+ '<xf numFmtId="0" fontId="3" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
761
+ '<xf numFmtId="0" fontId="4" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
762
+ '<xf numFmtId="0" fontId="0" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
763
+ '<xf numFmtId="0" fontId="1" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
764
+ '<xf numFmtId="0" fontId="2" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
765
+ '<xf numFmtId="0" fontId="3" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
766
+ '<xf numFmtId="0" fontId="4" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
767
+ '<xf numFmtId="0" fontId="0" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
768
+ '<xf numFmtId="0" fontId="1" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
769
+ '<xf numFmtId="0" fontId="2" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
770
+ '<xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
771
+ '<xf numFmtId="0" fontId="4" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
772
+ '<xf numFmtId="0" fontId="0" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
773
+ '<xf numFmtId="0" fontId="1" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
774
+ '<xf numFmtId="0" fontId="2" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
775
+ '<xf numFmtId="0" fontId="3" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
776
+ '<xf numFmtId="0" fontId="4" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
777
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
778
+ '<xf numFmtId="0" fontId="1" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
779
+ '<xf numFmtId="0" fontId="2" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
780
+ '<xf numFmtId="0" fontId="3" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
781
+ '<xf numFmtId="0" fontId="4" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
782
+ '<xf numFmtId="0" fontId="0" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
783
+ '<xf numFmtId="0" fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
784
+ '<xf numFmtId="0" fontId="2" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
785
+ '<xf numFmtId="0" fontId="3" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
786
+ '<xf numFmtId="0" fontId="4" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
787
+ '<xf numFmtId="0" fontId="0" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
788
+ '<xf numFmtId="0" fontId="1" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
789
+ '<xf numFmtId="0" fontId="2" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
790
+ '<xf numFmtId="0" fontId="3" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
791
+ '<xf numFmtId="0" fontId="4" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
792
+ '<xf numFmtId="0" fontId="0" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
793
+ '<xf numFmtId="0" fontId="1" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
794
+ '<xf numFmtId="0" fontId="2" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
795
+ '<xf numFmtId="0" fontId="3" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
796
+ '<xf numFmtId="0" fontId="4" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
797
+ '<xf numFmtId="0" fontId="0" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
798
+ '<xf numFmtId="0" fontId="1" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
799
+ '<xf numFmtId="0" fontId="2" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
800
+ '<xf numFmtId="0" fontId="3" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
801
+ '<xf numFmtId="0" fontId="4" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
802
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
803
+ '<alignment horizontal="left"/>' +
804
+ '</xf>' +
805
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
806
+ '<alignment horizontal="center"/>' +
807
+ '</xf>' +
808
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
809
+ '<alignment horizontal="right"/>' +
810
+ '</xf>' +
811
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
812
+ '<alignment horizontal="fill"/>' +
813
+ '</xf>' +
814
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
815
+ '<alignment textRotation="90"/>' +
816
+ '</xf>' +
817
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
818
+ '<alignment wrapText="1"/>' +
819
+ '</xf>' +
820
+ '<xf numFmtId="9" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
821
+ '<xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
822
+ '<xf numFmtId="165" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
823
+ '<xf numFmtId="166" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
824
+ '<xf numFmtId="167" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
825
+ '<xf numFmtId="168" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
826
+ '<xf numFmtId="169" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
827
+ '<xf numFmtId="3" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
828
+ '<xf numFmtId="4" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
829
+ '<xf numFmtId="1" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
830
+ '<xf numFmtId="2" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
831
+ '<xf numFmtId="14" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
832
+ '</cellXfs>' +
833
+ '<cellStyles count="1">' +
834
+ '<cellStyle name="Normal" xfId="0" builtinId="0" />' +
835
+ '</cellStyles>' +
836
+ '<dxfs count="0" />' +
837
+ '<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4" />' +
838
+ '</styleSheet>'
839
+ };
840
+ // Note we could use 3 `for` loops for the styles, but when gzipped there is
841
+ // virtually no difference in size, since the above can be easily compressed
842
+
843
+ // Pattern matching for special number formats. Perhaps this should be exposed
844
+ // via an API in future?
845
+ // Ref: section 3.8.30 - built in formatters in open spreadsheet
846
+ // https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf
847
+ var _excelSpecials = [
848
+ {
849
+ match: /^\-?\d+\.\d%$/,
850
+ style: 60,
851
+ fmt: function (d) {
852
+ return d / 100;
853
+ }
854
+ }, // Percent with d.p.
855
+ {
856
+ match: /^\-?\d+\.?\d*%$/,
857
+ style: 56,
858
+ fmt: function (d) {
859
+ return d / 100;
860
+ }
861
+ }, // Percent
862
+ { match: /^\-?\$[\d,]+.?\d*$/, style: 57 }, // Dollars
863
+ { match: /^\-?£[\d,]+.?\d*$/, style: 58 }, // Pounds
864
+ { match: /^\-?€[\d,]+.?\d*$/, style: 59 }, // Euros
865
+ { match: /^\-?\d+$/, style: 65 }, // Numbers without thousand separators
866
+ { match: /^\-?\d+\.\d{2}$/, style: 66 }, // Numbers 2 d.p. without thousands separators
867
+ {
868
+ match: /^\([\d,]+\)$/,
869
+ style: 61,
870
+ fmt: function (d) {
871
+ return -1 * d.replace(/[\(\)]/g, '');
872
+ }
873
+ }, // Negative numbers indicated by brackets
874
+ {
875
+ match: /^\([\d,]+\.\d{2}\)$/,
876
+ style: 62,
877
+ fmt: function (d) {
878
+ return -1 * d.replace(/[\(\)]/g, '');
879
+ }
880
+ }, // Negative numbers indicated by brackets - 2d.p.
881
+ { match: /^\-?[\d,]+$/, style: 63 }, // Numbers with thousand separators
882
+ { match: /^\-?[\d,]+\.\d{2}$/, style: 64 },
883
+ {
884
+ match: /^(19\d\d|[2-9]\d\d\d)\-(0\d|1[012])\-[0123][\d]$/,
885
+ style: 67,
886
+ fmt: function (d) {
887
+ return Math.round(25569 + Date.parse(d) / (86400 * 1000));
888
+ }
889
+ } //Date yyyy-mm-dd
890
+ ];
891
+
892
+ var _excelMergeCells = function (rels, row, column, rowspan, colspan) {
893
+ var mergeCells = $('mergeCells', rels);
894
+
895
+ mergeCells[0].appendChild(
896
+ _createNode(rels, 'mergeCell', {
897
+ attr: {
898
+ ref:
899
+ createCellPos(column) +
900
+ row +
901
+ ':' +
902
+ createCellPos(column + colspan - 1) +
903
+ (row + rowspan - 1)
904
+ }
905
+ })
906
+ );
907
+
908
+ mergeCells.attr('count', parseFloat(mergeCells.attr('count')) + 1);
909
+ };
910
+
911
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
912
+ * Buttons
913
+ */
914
+
915
+ //
916
+ // Copy to clipboard
917
+ //
918
+ DataTable.ext.buttons.copyHtml5 = {
919
+ className: 'buttons-copy buttons-html5',
920
+
921
+ text: function (dt) {
922
+ return dt.i18n('buttons.copy', 'Copy');
923
+ },
924
+
925
+ action: function (e, dt, button, config, cb) {
926
+ var exportData = _exportData(dt, config);
927
+ var info = dt.buttons.exportInfo(config);
928
+ var newline = _newLine(config);
929
+ var output = exportData.str;
930
+ var hiddenDiv = $('<div/>').css({
931
+ height: 1,
932
+ width: 1,
933
+ overflow: 'hidden',
934
+ position: 'fixed',
935
+ top: 0,
936
+ left: 0
937
+ });
938
+
939
+ if (info.title) {
940
+ output = info.title + newline + newline + output;
941
+ }
942
+
943
+ if (info.messageTop) {
944
+ output = info.messageTop + newline + newline + output;
945
+ }
946
+
947
+ if (info.messageBottom) {
948
+ output = output + newline + newline + info.messageBottom;
949
+ }
950
+
951
+ if (config.customize) {
952
+ output = config.customize(output, config, dt);
953
+ }
954
+
955
+ var textarea = $('<textarea readonly/>')
956
+ .val(output)
957
+ .appendTo(hiddenDiv);
958
+
959
+ // For browsers that support the copy execCommand, try to use it
960
+ if (document.queryCommandSupported('copy')) {
961
+ hiddenDiv.appendTo(dt.table().container());
962
+ textarea[0].focus();
963
+ textarea[0].select();
964
+
965
+ try {
966
+ var successful = document.execCommand('copy');
967
+ hiddenDiv.remove();
968
+
969
+ if (successful) {
970
+ if (config.copySuccess) {
971
+ dt.buttons.info(
972
+ dt.i18n('buttons.copyTitle', 'Copy to clipboard'),
973
+ dt.i18n(
974
+ 'buttons.copySuccess',
975
+ {
976
+ 1: 'Copied one row to clipboard',
977
+ _: 'Copied %d rows to clipboard'
978
+ },
979
+ exportData.rows
980
+ ),
981
+ 2000
982
+ );
983
+ }
984
+
985
+ cb();
986
+ return;
987
+ }
988
+ } catch (t) {
989
+ // noop
990
+ }
991
+ }
992
+
993
+ // Otherwise we show the text box and instruct the user to use it
994
+ var message = $(
995
+ '<span>' +
996
+ dt.i18n(
997
+ 'buttons.copyKeys',
998
+ 'Press <i>ctrl</i> or <i>\u2318</i> + <i>C</i> to copy the table data<br>to your system clipboard.<br><br>' +
999
+ 'To cancel, click this message or press escape.'
1000
+ ) +
1001
+ '</span>'
1002
+ ).append(hiddenDiv);
1003
+
1004
+ dt.buttons.info(
1005
+ dt.i18n('buttons.copyTitle', 'Copy to clipboard'),
1006
+ message,
1007
+ 0
1008
+ );
1009
+
1010
+ // Select the text so when the user activates their system clipboard
1011
+ // it will copy that text
1012
+ textarea[0].focus();
1013
+ textarea[0].select();
1014
+
1015
+ // Event to hide the message when the user is done
1016
+ var container = $(message).closest('.dt-button-info');
1017
+ var close = function () {
1018
+ container.off('click.buttons-copy');
1019
+ $(document).off('.buttons-copy');
1020
+ dt.buttons.info(false);
1021
+ };
1022
+
1023
+ container.on('click.buttons-copy', function () {
1024
+ close();
1025
+ cb();
1026
+ });
1027
+ $(document)
1028
+ .on('keydown.buttons-copy', function (e) {
1029
+ if (e.keyCode === 27) {
1030
+ // esc
1031
+ close();
1032
+ cb();
1033
+ }
1034
+ })
1035
+ .on('copy.buttons-copy cut.buttons-copy', function () {
1036
+ close();
1037
+ cb();
1038
+ });
1039
+ },
1040
+
1041
+ async: 100,
1042
+
1043
+ copySuccess: true,
1044
+
1045
+ exportOptions: {},
1046
+
1047
+ fieldSeparator: '\t',
1048
+
1049
+ fieldBoundary: '',
1050
+
1051
+ header: true,
1052
+
1053
+ footer: true,
1054
+
1055
+ title: '*',
1056
+
1057
+ messageTop: '*',
1058
+
1059
+ messageBottom: '*'
1060
+ };
1061
+
1062
+ //
1063
+ // CSV export
1064
+ //
1065
+ DataTable.ext.buttons.csvHtml5 = {
1066
+ bom: false,
1067
+
1068
+ className: 'buttons-csv buttons-html5',
1069
+
1070
+ available: function () {
1071
+ return window.FileReader !== undefined && window.Blob;
1072
+ },
1073
+
1074
+ text: function (dt) {
1075
+ return dt.i18n('buttons.csv', 'CSV');
1076
+ },
1077
+
1078
+ action: function (e, dt, button, config, cb) {
1079
+ // Set the text
1080
+ var output = _exportData(dt, config).str;
1081
+ var info = dt.buttons.exportInfo(config);
1082
+ var charset = config.charset;
1083
+
1084
+ if (config.customize) {
1085
+ output = config.customize(output, config, dt);
1086
+ }
1087
+
1088
+ if (charset !== false) {
1089
+ if (!charset) {
1090
+ charset = document.characterSet || document.charset;
1091
+ }
1092
+
1093
+ if (charset) {
1094
+ charset = ';charset=' + charset;
1095
+ }
1096
+ }
1097
+ else {
1098
+ charset = '';
1099
+ }
1100
+
1101
+ if (config.bom) {
1102
+ output = String.fromCharCode(0xfeff) + output;
1103
+ }
1104
+
1105
+ _saveAs(
1106
+ new Blob([output], { type: 'text/csv' + charset }),
1107
+ info.filename,
1108
+ true
1109
+ );
1110
+
1111
+ cb();
1112
+ },
1113
+
1114
+ async: 100,
1115
+
1116
+ filename: '*',
1117
+
1118
+ extension: '.csv',
1119
+
1120
+ exportOptions: {
1121
+ escapeExcelFormula: true
1122
+ },
1123
+
1124
+ fieldSeparator: ',',
1125
+
1126
+ fieldBoundary: '"',
1127
+
1128
+ escapeChar: '"',
1129
+
1130
+ charset: null,
1131
+
1132
+ header: true,
1133
+
1134
+ footer: true
1135
+ };
1136
+
1137
+ //
1138
+ // Excel (xlsx) export
1139
+ //
1140
+ DataTable.ext.buttons.excelHtml5 = {
1141
+ className: 'buttons-excel buttons-html5',
1142
+
1143
+ available: function () {
1144
+ return (
1145
+ window.FileReader !== undefined &&
1146
+ _jsZip() !== undefined &&
1147
+ !_isDuffSafari() &&
1148
+ _serialiser
1149
+ );
1150
+ },
1151
+
1152
+ text: function (dt) {
1153
+ return dt.i18n('buttons.excel', 'Excel');
1154
+ },
1155
+
1156
+ action: function (e, dt, button, config, cb) {
1157
+ var rowPos = 0;
1158
+ var dataStartRow, dataEndRow;
1159
+ var getXml = function (type) {
1160
+ var str = excelStrings[type];
1161
+
1162
+ //str = str.replace( /xmlns:/g, 'xmlns_' ).replace( /mc:/g, 'mc_' );
1163
+
1164
+ return $.parseXML(str);
1165
+ };
1166
+ var rels = getXml('xl/worksheets/sheet1.xml');
1167
+ var relsGet = rels.getElementsByTagName('sheetData')[0];
1168
+
1169
+ var xlsx = {
1170
+ _rels: {
1171
+ '.rels': getXml('_rels/.rels')
1172
+ },
1173
+ xl: {
1174
+ _rels: {
1175
+ 'workbook.xml.rels': getXml('xl/_rels/workbook.xml.rels')
1176
+ },
1177
+ 'workbook.xml': getXml('xl/workbook.xml'),
1178
+ 'styles.xml': getXml('xl/styles.xml'),
1179
+ worksheets: {
1180
+ 'sheet1.xml': rels
1181
+ }
1182
+ },
1183
+ '[Content_Types].xml': getXml('[Content_Types].xml')
1184
+ };
1185
+
1186
+ var data = dt.buttons.exportData(config.exportOptions);
1187
+ var currentRow, rowNode;
1188
+ var addRow = function (row) {
1189
+ currentRow = rowPos + 1;
1190
+ rowNode = _createNode(rels, 'row', { attr: { r: currentRow } });
1191
+
1192
+ for (var i = 0, ien = row.length; i < ien; i++) {
1193
+ // Concat both the Cell Columns as a letter and the Row of the cell.
1194
+ var cellId = createCellPos(i) + '' + currentRow;
1195
+ var cell = null;
1196
+
1197
+ // For null, undefined of blank cell, continue so it doesn't create the _createNode
1198
+ if (row[i] === null || row[i] === undefined || row[i] === '') {
1199
+ if (config.createEmptyCells === true) {
1200
+ row[i] = '';
1201
+ }
1202
+ else {
1203
+ continue;
1204
+ }
1205
+ }
1206
+
1207
+ var originalContent = row[i];
1208
+ row[i] =
1209
+ typeof row[i].trim === 'function' ? row[i].trim() : row[i];
1210
+
1211
+ // Special number formatting options
1212
+ for (var j = 0, jen = _excelSpecials.length; j < jen; j++) {
1213
+ var special = _excelSpecials[j];
1214
+
1215
+ // TODO Need to provide the ability for the specials to say
1216
+ // if they are returning a string, since at the moment it is
1217
+ // assumed to be a number
1218
+ if (
1219
+ row[i].match &&
1220
+ !row[i].match(/^0\d+/) &&
1221
+ row[i].match(special.match)
1222
+ ) {
1223
+ var val = row[i].replace(/[^\d\.\-]/g, '');
1224
+
1225
+ if (special.fmt) {
1226
+ val = special.fmt(val);
1227
+ }
1228
+
1229
+ cell = _createNode(rels, 'c', {
1230
+ attr: {
1231
+ r: cellId,
1232
+ s: special.style
1233
+ },
1234
+ children: [_createNode(rels, 'v', { text: val })]
1235
+ });
1236
+
1237
+ break;
1238
+ }
1239
+ }
1240
+
1241
+ if (!cell) {
1242
+ if (
1243
+ typeof row[i] === 'number' ||
1244
+ (row[i].match &&
1245
+ row[i].match(/^-?\d+(\.\d+)?([eE]\-?\d+)?$/) && // Includes exponential format
1246
+ !row[i].match(/^0\d+/))
1247
+ ) {
1248
+ // Detect numbers - don't match numbers with leading zeros
1249
+ // or a negative anywhere but the start
1250
+ cell = _createNode(rels, 'c', {
1251
+ attr: {
1252
+ t: 'n',
1253
+ r: cellId
1254
+ },
1255
+ children: [_createNode(rels, 'v', { text: row[i] })]
1256
+ });
1257
+ }
1258
+ else {
1259
+ // String output - replace non standard characters for text output
1260
+ /*eslint no-control-regex: "off"*/
1261
+ var text = !originalContent.replace
1262
+ ? originalContent
1263
+ : originalContent.replace(
1264
+ /[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g,
1265
+ ''
1266
+ );
1267
+
1268
+ cell = _createNode(rels, 'c', {
1269
+ attr: {
1270
+ t: 'inlineStr',
1271
+ r: cellId
1272
+ },
1273
+ children: {
1274
+ row: _createNode(rels, 'is', {
1275
+ children: {
1276
+ row: _createNode(rels, 't', {
1277
+ text: text,
1278
+ attr: {
1279
+ 'xml:space': 'preserve'
1280
+ }
1281
+ })
1282
+ }
1283
+ })
1284
+ }
1285
+ });
1286
+ }
1287
+ }
1288
+
1289
+ rowNode.appendChild(cell);
1290
+ }
1291
+
1292
+ relsGet.appendChild(rowNode);
1293
+ rowPos++;
1294
+ };
1295
+
1296
+ var addHeader = function (structure) {
1297
+ structure.forEach(function (row) {
1298
+ addRow(
1299
+ row.map(function (cell) {
1300
+ return cell ? cell.title : '';
1301
+ }),
1302
+ rowPos
1303
+ );
1304
+ $('row:last c', rels).attr('s', '2'); // bold
1305
+
1306
+ // Add any merge cells
1307
+ row.forEach(function (cell, columnCounter) {
1308
+ if (cell && (cell.colSpan > 1 || cell.rowSpan > 1)) {
1309
+ _excelMergeCells(
1310
+ rels,
1311
+ rowPos,
1312
+ columnCounter,
1313
+ cell.rowSpan,
1314
+ cell.colSpan
1315
+ );
1316
+ }
1317
+ });
1318
+ });
1319
+ };
1320
+
1321
+ // Title and top messages
1322
+ var exportInfo = dt.buttons.exportInfo(config);
1323
+ if (exportInfo.title) {
1324
+ addRow([exportInfo.title], rowPos);
1325
+ _excelMergeCells(rels, rowPos, 0, 1, data.header.length);
1326
+ $('row:last c', rels).attr('s', '51'); // centre
1327
+ }
1328
+
1329
+ if (exportInfo.messageTop) {
1330
+ addRow([exportInfo.messageTop], rowPos);
1331
+ _excelMergeCells(rels, rowPos, 0, 1, data.header.length);
1332
+ }
1333
+
1334
+ // Table header
1335
+ if (config.header) {
1336
+ addHeader(data.headerStructure);
1337
+ }
1338
+
1339
+ dataStartRow = rowPos;
1340
+
1341
+ // Table body
1342
+ for (var n = 0, ie = data.body.length; n < ie; n++) {
1343
+ addRow(data.body[n], rowPos);
1344
+ }
1345
+
1346
+ dataEndRow = rowPos;
1347
+
1348
+ // Table footer
1349
+ if (config.footer && data.footer) {
1350
+ addHeader(data.footerStructure);
1351
+ }
1352
+
1353
+ // Below the table
1354
+ if (exportInfo.messageBottom) {
1355
+ addRow([exportInfo.messageBottom], rowPos);
1356
+ _excelMergeCells(rels, rowPos, 0, 1, data.header.length);
1357
+ }
1358
+
1359
+ // Set column widths
1360
+ var cols = _createNode(rels, 'cols');
1361
+ $('worksheet', rels).prepend(cols);
1362
+
1363
+ for (var i = 0, ien = data.header.length; i < ien; i++) {
1364
+ cols.appendChild(
1365
+ _createNode(rels, 'col', {
1366
+ attr: {
1367
+ min: i + 1,
1368
+ max: i + 1,
1369
+ width: _excelColWidth(data, i),
1370
+ customWidth: 1
1371
+ }
1372
+ })
1373
+ );
1374
+ }
1375
+
1376
+ // Workbook modifications
1377
+ var workbook = xlsx.xl['workbook.xml'];
1378
+
1379
+ $('sheets sheet', workbook).attr('name', _sheetname(config));
1380
+
1381
+ // Auto filter for columns
1382
+ if (config.autoFilter) {
1383
+ $('mergeCells', rels).before(
1384
+ _createNode(rels, 'autoFilter', {
1385
+ attr: {
1386
+ ref:
1387
+ 'A' +
1388
+ dataStartRow +
1389
+ ':' +
1390
+ createCellPos(data.header.length - 1) +
1391
+ dataEndRow
1392
+ }
1393
+ })
1394
+ );
1395
+
1396
+ $('definedNames', workbook).append(
1397
+ _createNode(workbook, 'definedName', {
1398
+ attr: {
1399
+ name: '_xlnm._FilterDatabase',
1400
+ localSheetId: '0',
1401
+ hidden: 1
1402
+ },
1403
+ text:
1404
+ '\'' +
1405
+ _sheetname(config).replace(/'/g, '\'\'') +
1406
+ '\'!$A$' +
1407
+ dataStartRow +
1408
+ ':' +
1409
+ createCellPos(data.header.length - 1) +
1410
+ dataEndRow
1411
+ })
1412
+ );
1413
+ }
1414
+
1415
+ // Let the developer customise the document if they want to
1416
+ if (config.customize) {
1417
+ config.customize(xlsx, config, dt);
1418
+ }
1419
+
1420
+ // Excel doesn't like an empty mergeCells tag
1421
+ if ($('mergeCells', rels).children().length === 0) {
1422
+ $('mergeCells', rels).remove();
1423
+ }
1424
+
1425
+ var jszip = _jsZip();
1426
+ var zip = new jszip();
1427
+ var zipConfig = {
1428
+ compression: 'DEFLATE',
1429
+ type: 'blob',
1430
+ mimeType:
1431
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
1432
+ };
1433
+
1434
+ _addToZip(zip, xlsx);
1435
+
1436
+ // Modern Excel has a 218 character limit on the file name + path of the file (why!?)
1437
+ // https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
1438
+ // So we truncate to allow for this.
1439
+ var filename = exportInfo.filename;
1440
+
1441
+ if (filename > 175) {
1442
+ filename = filename.substr(0, 175);
1443
+ }
1444
+
1445
+ // Let the developer customize the final zip file if they want to before it is generated and sent to the browser
1446
+ if (config.customizeZip) {
1447
+ config.customizeZip(zip, data, filename);
1448
+ }
1449
+
1450
+
1451
+ if (zip.generateAsync) {
1452
+ // JSZip 3+
1453
+ zip.generateAsync(zipConfig).then(function (blob) {
1454
+ _saveAs(blob, filename);
1455
+ cb();
1456
+ });
1457
+ }
1458
+ else {
1459
+ // JSZip 2.5
1460
+ _saveAs(zip.generate(zipConfig), filename);
1461
+ cb();
1462
+ }
1463
+ },
1464
+
1465
+ async: 100,
1466
+
1467
+ filename: '*',
1468
+
1469
+ extension: '.xlsx',
1470
+
1471
+ exportOptions: {},
1472
+
1473
+ header: true,
1474
+
1475
+ footer: true,
1476
+
1477
+ title: '*',
1478
+
1479
+ messageTop: '*',
1480
+
1481
+ messageBottom: '*',
1482
+
1483
+ createEmptyCells: false,
1484
+
1485
+ autoFilter: false,
1486
+
1487
+ sheetName: ''
1488
+ };
1489
+
1490
+ //
1491
+ // PDF export - using pdfMake - http://pdfmake.org
1492
+ //
1493
+ DataTable.ext.buttons.pdfHtml5 = {
1494
+ className: 'buttons-pdf buttons-html5',
1495
+
1496
+ available: function () {
1497
+ return window.FileReader !== undefined && _pdfMake();
1498
+ },
1499
+
1500
+ text: function (dt) {
1501
+ return dt.i18n('buttons.pdf', 'PDF');
1502
+ },
1503
+
1504
+ action: function (e, dt, button, config, cb) {
1505
+ var data = dt.buttons.exportData(config.exportOptions);
1506
+ var info = dt.buttons.exportInfo(config);
1507
+ var rows = [];
1508
+
1509
+ if (config.header) {
1510
+ data.headerStructure.forEach(function (row) {
1511
+ rows.push(
1512
+ row.map(function (cell) {
1513
+ return cell
1514
+ ? {
1515
+ text: cell.title,
1516
+ colSpan: cell.colspan,
1517
+ rowSpan: cell.rowspan,
1518
+ style: 'tableHeader'
1519
+ }
1520
+ : {};
1521
+ })
1522
+ );
1523
+ });
1524
+ }
1525
+
1526
+ for (var i = 0, ien = data.body.length; i < ien; i++) {
1527
+ rows.push(
1528
+ data.body[i].map(function (d) {
1529
+ return {
1530
+ text:
1531
+ d === null || d === undefined
1532
+ ? ''
1533
+ : typeof d === 'string'
1534
+ ? d
1535
+ : d.toString()
1536
+ };
1537
+ })
1538
+ );
1539
+ }
1540
+
1541
+ if (config.footer) {
1542
+ data.footerStructure.forEach(function (row) {
1543
+ rows.push(
1544
+ row.map(function (cell) {
1545
+ return cell
1546
+ ? {
1547
+ text: cell.title,
1548
+ colSpan: cell.colspan,
1549
+ rowSpan: cell.rowspan,
1550
+ style: 'tableFooter'
1551
+ }
1552
+ : {};
1553
+ })
1554
+ );
1555
+ });
1556
+ }
1557
+
1558
+ var doc = {
1559
+ pageSize: config.pageSize,
1560
+ pageOrientation: config.orientation,
1561
+ content: [
1562
+ {
1563
+ style: 'table',
1564
+ table: {
1565
+ headerRows: data.headerStructure.length,
1566
+ footerRows: data.footerStructure.length, // Used for styling, doesn't do anything in pdfmake
1567
+ body: rows
1568
+ },
1569
+ layout: {
1570
+ hLineWidth: function (i, node) {
1571
+ if (i === 0 || i === node.table.body.length) {
1572
+ return 0;
1573
+ }
1574
+ return 0.5;
1575
+ },
1576
+ vLineWidth: function () {
1577
+ return 0;
1578
+ },
1579
+ hLineColor: function (i, node) {
1580
+ return i === node.table.headerRows ||
1581
+ i ===
1582
+ node.table.body.length -
1583
+ node.table.footerRows
1584
+ ? '#333'
1585
+ : '#ddd';
1586
+ },
1587
+ fillColor: function (rowIndex) {
1588
+ if (rowIndex < data.headerStructure.length) {
1589
+ return '#fff';
1590
+ }
1591
+ return rowIndex % 2 === 0 ? '#f3f3f3' : null;
1592
+ },
1593
+ paddingTop: function () {
1594
+ return 5;
1595
+ },
1596
+ paddingBottom: function () {
1597
+ return 5;
1598
+ }
1599
+ }
1600
+ }
1601
+ ],
1602
+ styles: {
1603
+ tableHeader: {
1604
+ bold: true,
1605
+ fontSize: 11,
1606
+ alignment: 'center'
1607
+ },
1608
+ tableFooter: {
1609
+ bold: true,
1610
+ fontSize: 11,
1611
+ alignment: 'center'
1612
+ },
1613
+ table: {
1614
+ margin: [0, 5, 0, 5]
1615
+ },
1616
+ title: {
1617
+ alignment: 'center',
1618
+ fontSize: 13
1619
+ },
1620
+ message: {}
1621
+ },
1622
+ defaultStyle: {
1623
+ fontSize: 10
1624
+ }
1625
+ };
1626
+
1627
+ if (info.messageTop) {
1628
+ doc.content.unshift({
1629
+ text: info.messageTop,
1630
+ style: 'message',
1631
+ margin: [0, 0, 0, 12]
1632
+ });
1633
+ }
1634
+
1635
+ if (info.messageBottom) {
1636
+ doc.content.push({
1637
+ text: info.messageBottom,
1638
+ style: 'message',
1639
+ margin: [0, 0, 0, 12]
1640
+ });
1641
+ }
1642
+
1643
+ if (info.title) {
1644
+ doc.content.unshift({
1645
+ text: info.title,
1646
+ style: 'title',
1647
+ margin: [0, 0, 0, 12]
1648
+ });
1649
+ }
1650
+
1651
+ if (config.customize) {
1652
+ config.customize(doc, config, dt);
1653
+ }
1654
+
1655
+ var pdf = _pdfMake().createPdf(doc);
1656
+
1657
+ if (config.download === 'open' && !_isDuffSafari()) {
1658
+ pdf.open();
1659
+ }
1660
+ else {
1661
+ pdf.download(info.filename);
1662
+ }
1663
+
1664
+ cb();
1665
+ },
1666
+
1667
+ async: 100,
1668
+
1669
+ title: '*',
1670
+
1671
+ filename: '*',
1672
+
1673
+ extension: '.pdf',
1674
+
1675
+ exportOptions: {},
1676
+
1677
+ orientation: 'portrait',
1678
+
1679
+ // This isn't perfect, but it is close
1680
+ pageSize:
1681
+ navigator.language === 'en-US' || navigator.language === 'en-CA'
1682
+ ? 'LETTER'
1683
+ : 'A4',
1684
+
1685
+ header: true,
1686
+
1687
+ footer: true,
1688
+
1689
+ messageTop: '*',
1690
+
1691
+ messageBottom: '*',
1692
+
1693
+ customize: null,
1694
+
1695
+ download: 'download'
1696
+ };
1697
+
1698
+
1699
+ return DataTable;
1700
+ }));