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