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