Administrator
2022-09-14 58d006e05dcf2a20d0ec5367dd03d66a61db6849
提交 | 用户 | 时间
58d006 1 /*!
A 2  * HTML5 export buttons for Buttons and DataTables.
3  * 2015 SpryMedia Ltd - datatables.net/license
4  *
5  * FileSaver.js (2015-05-07.2) - MIT license
6  * Copyright © 2015 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         module.exports = function (root, $) {
19             if ( ! root ) {
20                 root = window;
21             }
22
23             if ( ! $ || ! $.fn.dataTable ) {
24                 $ = require('datatables.net')(root, $).$;
25             }
26
27             if ( ! $.fn.dataTable.Buttons ) {
28                 require('datatables.net-buttons')(root, $);
29             }
30
31             return factory( $, root, root.document );
32         };
33     }
34     else {
35         // Browser
36         factory( jQuery, window, document );
37     }
38 }(function( $, window, document, undefined ) {
39 'use strict';
40 var DataTable = $.fn.dataTable;
41
42
43 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
44  * FileSaver.js dependency
45  */
46
47 /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
48
49 var _saveAs = (function(view) {
50     // IE <10 is explicitly unsupported
51     if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
52         return;
53     }
54     var
55           doc = view.document
56           // only get URL when necessary in case Blob.js hasn't overridden it yet
57         , get_URL = function() {
58             return view.URL || view.webkitURL || view;
59         }
60         , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
61         , can_use_save_link = "download" in save_link
62         , click = function(node) {
63             var event = doc.createEvent("MouseEvents");
64             event.initMouseEvent(
65                 "click", true, false, view, 0, 0, 0, 0, 0
66                 , false, false, false, false, 0, null
67             );
68             node.dispatchEvent(event);
69         }
70         , webkit_req_fs = view.webkitRequestFileSystem
71         , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
72         , throw_outside = function(ex) {
73             (view.setImmediate || view.setTimeout)(function() {
74                 throw ex;
75             }, 0);
76         }
77         , force_saveable_type = "application/octet-stream"
78         , fs_min_size = 0
79         // See https://code.google.com/p/chromium/issues/detail?id=375297#c7 and
80         // https://github.com/eligrey/FileSaver.js/commit/485930a#commitcomment-8768047
81         // for the reasoning behind the timeout and revocation flow
82         , arbitrary_revoke_timeout = 500 // in ms
83         , revoke = function(file) {
84             var revoker = function() {
85                 if (typeof file === "string") { // file is an object URL
86                     get_URL().revokeObjectURL(file);
87                 } else { // file is a File
88                     file.remove();
89                 }
90             };
91             if (view.chrome) {
92                 revoker();
93             } else {
94                 setTimeout(revoker, arbitrary_revoke_timeout);
95             }
96         }
97         , dispatch = function(filesaver, event_types, event) {
98             event_types = [].concat(event_types);
99             var i = event_types.length;
100             while (i--) {
101                 var listener = filesaver["on" + event_types[i]];
102                 if (typeof listener === "function") {
103                     try {
104                         listener.call(filesaver, event || filesaver);
105                     } catch (ex) {
106                         throw_outside(ex);
107                     }
108                 }
109             }
110         }
111         , auto_bom = function(blob) {
112             // prepend BOM for UTF-8 XML and text/* types (including HTML)
113             if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
114                 return new Blob(["\ufeff", blob], {type: blob.type});
115             }
116             return blob;
117         }
118         , FileSaver = function(blob, name) {
119             blob = auto_bom(blob);
120             // First try a.download, then web filesystem, then object URLs
121             var
122                   filesaver = this
123                 , type = blob.type
124                 , blob_changed = false
125                 , object_url
126                 , target_view
127                 , dispatch_all = function() {
128                     dispatch(filesaver, "writestart progress write writeend".split(" "));
129                 }
130                 // on any filesys errors revert to saving with object URLs
131                 , fs_error = function() {
132                     // don't create more object URLs than needed
133                     if (blob_changed || !object_url) {
134                         object_url = get_URL().createObjectURL(blob);
135                     }
136                     if (target_view) {
137                         target_view.location.href = object_url;
138                     } else {
139                         var new_tab = view.open(object_url, "_blank");
140                         if (new_tab === undefined && typeof safari !== "undefined") {
141                             //Apple do not allow window.open, see http://bit.ly/1kZffRI
142                             view.location.href = object_url;
143                         }
144                     }
145                     filesaver.readyState = filesaver.DONE;
146                     dispatch_all();
147                     revoke(object_url);
148                 }
149                 , abortable = function(func) {
150                     return function() {
151                         if (filesaver.readyState !== filesaver.DONE) {
152                             return func.apply(this, arguments);
153                         }
154                     };
155                 }
156                 , create_if_not_found = {create: true, exclusive: false}
157                 , slice
158             ;
159             filesaver.readyState = filesaver.INIT;
160             if (!name) {
161                 name = "download";
162             }
163             if (can_use_save_link) {
164                 object_url = get_URL().createObjectURL(blob);
165                 save_link.href = object_url;
166                 save_link.download = name;
167                 click(save_link);
168                 filesaver.readyState = filesaver.DONE;
169                 dispatch_all();
170                 revoke(object_url);
171                 return;
172             }
173             // Object and web filesystem URLs have a problem saving in Google Chrome when
174             // viewed in a tab, so I force save with application/octet-stream
175             // http://code.google.com/p/chromium/issues/detail?id=91158
176             // Update: Google errantly closed 91158, I submitted it again:
177             // https://code.google.com/p/chromium/issues/detail?id=389642
178             if (view.chrome && type && type !== force_saveable_type) {
179                 slice = blob.slice || blob.webkitSlice;
180                 blob = slice.call(blob, 0, blob.size, force_saveable_type);
181                 blob_changed = true;
182             }
183             // Since I can't be sure that the guessed media type will trigger a download
184             // in WebKit, I append .download to the filename.
185             // https://bugs.webkit.org/show_bug.cgi?id=65440
186             if (webkit_req_fs && name !== "download") {
187                 name += ".download";
188             }
189             if (type === force_saveable_type || webkit_req_fs) {
190                 target_view = view;
191             }
192             if (!req_fs) {
193                 fs_error();
194                 return;
195             }
196             fs_min_size += blob.size;
197             req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
198                 fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
199                     var save = function() {
200                         dir.getFile(name, create_if_not_found, abortable(function(file) {
201                             file.createWriter(abortable(function(writer) {
202                                 writer.onwriteend = function(event) {
203                                     target_view.location.href = file.toURL();
204                                     filesaver.readyState = filesaver.DONE;
205                                     dispatch(filesaver, "writeend", event);
206                                     revoke(file);
207                                 };
208                                 writer.onerror = function() {
209                                     var error = writer.error;
210                                     if (error.code !== error.ABORT_ERR) {
211                                         fs_error();
212                                     }
213                                 };
214                                 "writestart progress write abort".split(" ").forEach(function(event) {
215                                     writer["on" + event] = filesaver["on" + event];
216                                 });
217                                 writer.write(blob);
218                                 filesaver.abort = function() {
219                                     writer.abort();
220                                     filesaver.readyState = filesaver.DONE;
221                                 };
222                                 filesaver.readyState = filesaver.WRITING;
223                             }), fs_error);
224                         }), fs_error);
225                     };
226                     dir.getFile(name, {create: false}, abortable(function(file) {
227                         // delete file if it already exists
228                         file.remove();
229                         save();
230                     }), abortable(function(ex) {
231                         if (ex.code === ex.NOT_FOUND_ERR) {
232                             save();
233                         } else {
234                             fs_error();
235                         }
236                     }));
237                 }), fs_error);
238             }), fs_error);
239         }
240         , FS_proto = FileSaver.prototype
241         , saveAs = function(blob, name) {
242             return new FileSaver(blob, name);
243         }
244     ;
245     // IE 10+ (native saveAs)
246     if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
247         return function(blob, name) {
248             return navigator.msSaveOrOpenBlob(auto_bom(blob), name);
249         };
250     }
251
252     FS_proto.abort = function() {
253         var filesaver = this;
254         filesaver.readyState = filesaver.DONE;
255         dispatch(filesaver, "abort");
256     };
257     FS_proto.readyState = FS_proto.INIT = 0;
258     FS_proto.WRITING = 1;
259     FS_proto.DONE = 2;
260
261     FS_proto.error =
262     FS_proto.onwritestart =
263     FS_proto.onprogress =
264     FS_proto.onwrite =
265     FS_proto.onabort =
266     FS_proto.onerror =
267     FS_proto.onwriteend =
268         null;
269
270     return saveAs;
271 }(window));
272
273
274
275 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
276  * Local (private) functions
277  */
278
279 /**
280  * Get the file name for an exported file.
281  *
282  * @param {object}  config       Button configuration
283  * @param {boolean} incExtension Include the file name extension
284  */
285 var _filename = function ( config, incExtension )
286 {
287     // Backwards compatibility
288     var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined ?
289         config.title :
290         config.filename;
291
292     if ( filename.indexOf( '*' ) !== -1 ) {
293         filename = filename.replace( '*', $('title').text() );
294     }
295
296     // Strip characters which the OS will object to
297     filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
298
299     return incExtension === undefined || incExtension === true ?
300         filename+config.extension :
301         filename;
302 };
303
304 /**
305  * Get the title for an exported file.
306  *
307  * @param {object}  config  Button configuration
308  */
309 var _title = function ( config )
310 {
311     var title = config.title;
312
313     return title.indexOf( '*' ) !== -1 ?
314         title.replace( '*', $('title').text() ) :
315         title;
316 };
317
318 /**
319  * Get the newline character(s)
320  *
321  * @param {object}  config Button configuration
322  * @return {string}        Newline character
323  */
324 var _newLine = function ( config )
325 {
326     return config.newline ?
327         config.newline :
328         navigator.userAgent.match(/Windows/) ?
329             '\r\n' :
330             '\n';
331 };
332
333 /**
334  * Combine the data from the `buttons.exportData` method into a string that
335  * will be used in the export file.
336  *
337  * @param  {DataTable.Api} dt     DataTables API instance
338  * @param  {object}        config Button configuration
339  * @return {object}               The data to export
340  */
341 var _exportData = function ( dt, config )
342 {
343     var newLine = _newLine( config );
344     var data = dt.buttons.exportData( config.exportOptions );
345     var boundary = config.fieldBoundary;
346     var separator = config.fieldSeparator;
347     var reBoundary = new RegExp( boundary, 'g' );
348     var escapeChar = config.escapeChar !== undefined ?
349         config.escapeChar :
350         '\\';
351     var join = function ( a ) {
352         var s = '';
353
354         // If there is a field boundary, then we might need to escape it in
355         // the source data
356         for ( var i=0, ien=a.length ; i<ien ; i++ ) {
357             if ( i > 0 ) {
358                 s += separator;
359             }
360
361             s += boundary ?
362                 boundary + ('' + a[i]).replace( reBoundary, escapeChar+boundary ) + boundary :
363                 a[i];
364         }
365
366         return s;
367     };
368
369     var header = config.header ? join( data.header )+newLine : '';
370     var footer = config.footer ? newLine+join( data.footer ) : '';
371     var body = [];
372
373     for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
374         body.push( join( data.body[i] ) );
375     }
376
377     return {
378         str: header + body.join( newLine ) + footer,
379         rows: body.length
380     };
381 };
382
383 /**
384  * Safari's data: support for creating and downloading files is really poor, so
385  * various options need to be disabled in it. See
386  * https://bugs.webkit.org/show_bug.cgi?id=102914
387  * 
388  * @return {Boolean} `true` if Safari
389  */
390 var _isSafari = function ()
391 {
392     return navigator.userAgent.indexOf('Safari') !== -1 &&
393         navigator.userAgent.indexOf('Chrome') === -1 &&
394         navigator.userAgent.indexOf('Opera') === -1;
395 };
396
397
398 // Excel - Pre-defined strings to build a minimal XLSX file
399 var excelStrings = {
400     "_rels/.rels": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\
401 <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\
402     <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>\
403 </Relationships>',
404
405     "xl/_rels/workbook.xml.rels": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\
406 <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\
407     <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>\
408 </Relationships>',
409
410     "[Content_Types].xml": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\
411 <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">\
412     <Default Extension="xml" ContentType="application/xml"/>\
413     <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>\
414     <Default Extension="jpeg" ContentType="image/jpeg"/>\
415     <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>\
416     <Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>\
417 </Types>',
418
419     "xl/workbook.xml": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\
420 <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">\
421     <fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>\
422     <workbookPr showInkAnnotation="0" autoCompressPictures="0"/>\
423     <bookViews>\
424         <workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>\
425     </bookViews>\
426     <sheets>\
427         <sheet name="Sheet1" sheetId="1" r:id="rId1"/>\
428     </sheets>\
429 </workbook>',
430
431     "xl/worksheets/sheet1.xml": '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\
432 <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">\
433     <sheetData>\
434         __DATA__\
435     </sheetData>\
436 </worksheet>'
437 };
438
439
440
441 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
442  * Buttons
443  */
444
445 //
446 // Copy to clipboard
447 //
448 DataTable.ext.buttons.copyHtml5 = {
449     className: 'buttons-copy buttons-html5',
450
451     text: function ( dt ) {
452         return dt.i18n( 'buttons.copy', 'Copy' );
453     },
454
455     action: function ( e, dt, button, config ) {
456         var exportData = _exportData( dt, config );
457         var output = exportData.str;
458         var hiddenDiv = $('<div/>')
459             .css( {
460                 height: 1,
461                 width: 1,
462                 overflow: 'hidden',
463                 position: 'fixed',
464                 top: 0,
465                 left: 0
466             } );
467         var textarea = $('<textarea readonly/>')
468             .val( output )
469             .appendTo( hiddenDiv );
470
471         // For browsers that support the copy execCommand, try to use it
472         if ( document.queryCommandSupported('copy') ) {
473             hiddenDiv.appendTo( 'body' );
474             textarea[0].focus();
475             textarea[0].select();
476
477             try {
478                 document.execCommand( 'copy' );
479                 hiddenDiv.remove();
480
481                 dt.buttons.info(
482                     dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ),
483                     dt.i18n( 'buttons.copySuccess', {
484                             1: "Copied one row to clipboard",
485                             _: "Copied %d rows to clipboard"
486                         }, exportData.rows ),
487                     2000
488                 );
489
490                 return;
491             }
492             catch (t) {}
493         }
494
495         // Otherwise we show the text box and instruct the user to use it
496         var message = $('<span>'+dt.i18n( 'buttons.copyKeys',
497                 'Press <i>ctrl</i> or <i>\u2318</i> + <i>C</i> to copy the table data<br>to your system clipboard.<br><br>'+
498                 'To cancel, click this message or press escape.' )+'</span>'
499             )
500             .append( hiddenDiv );
501
502         dt.buttons.info( dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ), message, 0 );
503
504         // Select the text so when the user activates their system clipboard
505         // it will copy that text
506         textarea[0].focus();
507         textarea[0].select();
508
509         // Event to hide the message when the user is done
510         var container = $(message).closest('.dt-button-info');
511         var close = function () {
512             container.off( 'click.buttons-copy' );
513             $(document).off( '.buttons-copy' );
514             dt.buttons.info( false );
515         };
516
517         container.on( 'click.buttons-copy', close );
518         $(document)
519             .on( 'keydown.buttons-copy', function (e) {
520                 if ( e.keyCode === 27 ) { // esc
521                     close();
522                 }
523             } )
524             .on( 'copy.buttons-copy cut.buttons-copy', function () {
525                 close();
526             } );
527     },
528
529     exportOptions: {},
530
531     fieldSeparator: '\t',
532
533     fieldBoundary: '',
534
535     header: true,
536
537     footer: false
538 };
539
540 //
541 // CSV export
542 //
543 DataTable.ext.buttons.csvHtml5 = {
544     className: 'buttons-csv buttons-html5',
545
546     available: function () {
547         return window.FileReader !== undefined && window.Blob;
548     },
549
550     text: function ( dt ) {
551         return dt.i18n( 'buttons.csv', 'CSV' );
552     },
553
554     action: function ( e, dt, button, config ) {
555         // Set the text
556         var newLine = _newLine( config );
557         var output = _exportData( dt, config ).str;
558         var charset = config.charset;
559
560         if ( charset !== false ) {
561             if ( ! charset ) {
562                 charset = document.characterSet || document.charset;
563             }
564
565             if ( charset ) {
566                 charset = ';charset='+charset;
567             }
568         }
569         else {
570             charset = '';
571         }
572
573         _saveAs(
574             new Blob( [output], {type: 'text/csv'+charset} ),
575             _filename( config )
576         );
577     },
578
579     filename: '*',
580
581     extension: '.csv',
582
583     exportOptions: {},
584
585     fieldSeparator: ',',
586
587     fieldBoundary: '"',
588
589     escapeChar: '"',
590
591     charset: null,
592
593     header: true,
594
595     footer: false
596 };
597
598 //
599 // Excel (xlsx) export
600 //
601 DataTable.ext.buttons.excelHtml5 = {
602     className: 'buttons-excel buttons-html5',
603
604     available: function () {
605         return window.FileReader !== undefined && window.JSZip !== undefined && ! _isSafari();
606     },
607
608     text: function ( dt ) {
609         return dt.i18n( 'buttons.excel', 'Excel' );
610     },
611
612     action: function ( e, dt, button, config ) {
613         // Set the text
614         var xml = '';
615         var data = dt.buttons.exportData( config.exportOptions );
616         var addRow = function ( row ) {
617             var cells = [];
618
619             for ( var i=0, ien=row.length ; i<ien ; i++ ) {
620                 if ( row[i] === null || row[i] === undefined ) {
621                     row[i] = '';
622                 }
623
624                 // Don't match numbers with leading zeros or a negative anywhere
625                 // but the start
626                 cells.push( typeof row[i] === 'number' || (row[i].match && row[i].match(/^-?[0-9\.]+$/) && row[i].charAt(0) !== '0') ?
627                     '<c t="n"><v>'+row[i]+'</v></c>' :
628                     '<c t="inlineStr"><is><t>'+(
629                         ! row[i].replace ?
630                             row[i] :
631                             row[i]
632                                 .replace(/&(?!amp;)/g, '&amp;')
633                                 .replace(/[\x00-\x1F\x7F-\x9F]/g, ''))+ // remove control characters
634                     '</t></is></c>'                                    // they are not valid in XML
635                 );
636             }
637
638             return '<row>'+cells.join('')+'</row>';
639         };
640
641         if ( config.header ) {
642             xml += addRow( data.header );
643         }
644
645         for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
646             xml += addRow( data.body[i] );
647         }
648
649         if ( config.footer ) {
650             xml += addRow( data.footer );
651         }
652
653         var zip           = new window.JSZip();
654         var _rels         = zip.folder("_rels");
655         var xl            = zip.folder("xl");
656         var xl_rels       = zip.folder("xl/_rels");
657         var xl_worksheets = zip.folder("xl/worksheets");
658
659         zip.file(           '[Content_Types].xml', excelStrings['[Content_Types].xml'] );
660         _rels.file(         '.rels',               excelStrings['_rels/.rels'] );
661         xl.file(            'workbook.xml',        excelStrings['xl/workbook.xml'] );
662         xl_rels.file(       'workbook.xml.rels',   excelStrings['xl/_rels/workbook.xml.rels'] );
663         xl_worksheets.file( 'sheet1.xml',          excelStrings['xl/worksheets/sheet1.xml'].replace( '__DATA__', xml ) );
664
665         _saveAs(
666             zip.generate( {type:"blob"} ),
667             _filename( config )
668         );
669     },
670
671     filename: '*',
672
673     extension: '.xlsx',
674
675     exportOptions: {},
676
677     header: true,
678
679     footer: false
680 };
681
682 //
683 // PDF export - using pdfMake - http://pdfmake.org
684 //
685 DataTable.ext.buttons.pdfHtml5 = {
686     className: 'buttons-pdf buttons-html5',
687
688     available: function () {
689         return window.FileReader !== undefined && window.pdfMake;
690     },
691
692     text: function ( dt ) {
693         return dt.i18n( 'buttons.pdf', 'PDF' );
694     },
695
696     action: function ( e, dt, button, config ) {
697         var newLine = _newLine( config );
698         var data = dt.buttons.exportData( config.exportOptions );
699         var rows = [];
700
701         if ( config.header ) {
702             rows.push( $.map( data.header, function ( d ) {
703                 return {
704                     text: typeof d === 'string' ? d : d+'',
705                     style: 'tableHeader'
706                 };
707             } ) );
708         }
709
710         for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
711             rows.push( $.map( data.body[i], function ( d ) {
712                 return {
713                     text: typeof d === 'string' ? d : d+'',
714                     style: i % 2 ? 'tableBodyEven' : 'tableBodyOdd'
715                 };
716             } ) );
717         }
718
719         if ( config.footer ) {
720             rows.push( $.map( data.footer, function ( d ) {
721                 return {
722                     text: typeof d === 'string' ? d : d+'',
723                     style: 'tableFooter'
724                 };
725             } ) );
726         }
727
728         var doc = {
729             pageSize: config.pageSize,
730             pageOrientation: config.orientation,
731             content: [
732                 {
733                     table: {
734                         headerRows: 1,
735                         body: rows
736                     },
737                     layout: 'noBorders'
738                 }
739             ],
740             styles: {
741                 tableHeader: {
742                     bold: true,
743                     fontSize: 11,
744                     color: 'white',
745                     fillColor: '#2d4154',
746                     alignment: 'center'
747                 },
748                 tableBodyEven: {},
749                 tableBodyOdd: {
750                     fillColor: '#f3f3f3'
751                 },
752                 tableFooter: {
753                     bold: true,
754                     fontSize: 11,
755                     color: 'white',
756                     fillColor: '#2d4154'
757                 },
758                 title: {
759                     alignment: 'center',
760                     fontSize: 15
761                 },
762                 message: {}
763             },
764             defaultStyle: {
765                 fontSize: 10
766             }
767         };
768
769         if ( config.message ) {
770             doc.content.unshift( {
771                 text: config.message,
772                 style: 'message',
773                 margin: [ 0, 0, 0, 12 ]
774             } );
775         }
776
777         if ( config.title ) {
778             doc.content.unshift( {
779                 text: _title( config, false ),
780                 style: 'title',
781                 margin: [ 0, 0, 0, 12 ]
782             } );
783         }
784
785         if ( config.customize ) {
786             config.customize( doc );
787         }
788
789         var pdf = window.pdfMake.createPdf( doc );
790
791         if ( config.download === 'open' && ! _isSafari() ) {
792             pdf.open();
793         }
794         else {
795             pdf.getBuffer( function (buffer) {
796                 var blob = new Blob( [buffer], {type:'application/pdf'} );
797
798                 _saveAs( blob, _filename( config ) );
799             } );
800         }
801     },
802
803     title: '*',
804
805     filename: '*',
806
807     extension: '.pdf',
808
809     exportOptions: {},
810
811     orientation: 'portrait',
812
813     pageSize: 'A4',
814
815     header: true,
816
817     footer: false,
818
819     message: null,
820
821     customize: null,
822
823     download: 'download'
824 };
825
826
827 return DataTable.Buttons;
828 }));