buttons.html5.js 22 KB

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