buttons.flash.js (45617B)
1 /*! 2 * Flash export buttons for Buttons and DataTables. 3 * 2015 SpryMedia Ltd - datatables.net/license 4 * 5 * ZeroClipbaord - MIT license 6 * Copyright (c) 2012 Joseph Huckaby 7 */ 8 9 (function( factory ){ 10 if ( typeof define === 'function' && define.amd ) { 11 // AMD 12 define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) { 13 return factory( $, window, document ); 14 } ); 15 } 16 else if ( typeof exports === 'object' ) { 17 // CommonJS 18 module.exports = function (root, $) { 19 if ( ! root ) { 20 root = window; 21 } 22 23 if ( ! $ || ! $.fn.dataTable ) { 24 $ = require('datatables.net')(root, $).$; 25 } 26 27 if ( ! $.fn.dataTable.Buttons ) { 28 require('datatables.net-buttons')(root, $); 29 } 30 31 return factory( $, root, root.document ); 32 }; 33 } 34 else { 35 // Browser 36 factory( jQuery, window, document ); 37 } 38 }(function( $, window, document, undefined ) { 39 'use strict'; 40 var DataTable = $.fn.dataTable; 41 42 43 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 44 * ZeroClipboard dependency 45 */ 46 47 /* 48 * ZeroClipboard 1.0.4 with modifications 49 * Author: Joseph Huckaby 50 * License: MIT 51 * 52 * Copyright (c) 2012 Joseph Huckaby 53 */ 54 var ZeroClipboard_TableTools = { 55 version: "1.0.4-TableTools2", 56 clients: {}, // registered upload clients on page, indexed by id 57 moviePath: '', // URL to movie 58 nextId: 1, // ID of next movie 59 60 $: function(thingy) { 61 // simple DOM lookup utility function 62 if (typeof(thingy) == 'string') { 63 thingy = document.getElementById(thingy); 64 } 65 if (!thingy.addClass) { 66 // extend element with a few useful methods 67 thingy.hide = function() { this.style.display = 'none'; }; 68 thingy.show = function() { this.style.display = ''; }; 69 thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; }; 70 thingy.removeClass = function(name) { 71 this.className = this.className.replace( new RegExp("\\s*" + name + "\\s*"), " ").replace(/^\s+/, '').replace(/\s+$/, ''); 72 }; 73 thingy.hasClass = function(name) { 74 return !!this.className.match( new RegExp("\\s*" + name + "\\s*") ); 75 }; 76 } 77 return thingy; 78 }, 79 80 setMoviePath: function(path) { 81 // set path to ZeroClipboard.swf 82 this.moviePath = path; 83 }, 84 85 dispatch: function(id, eventName, args) { 86 // receive event from flash movie, send to client 87 var client = this.clients[id]; 88 if (client) { 89 client.receiveEvent(eventName, args); 90 } 91 }, 92 93 log: function ( str ) { 94 console.log( 'Flash: '+str ); 95 }, 96 97 register: function(id, client) { 98 // register new client to receive events 99 this.clients[id] = client; 100 }, 101 102 getDOMObjectPosition: function(obj) { 103 // get absolute coordinates for dom element 104 var info = { 105 left: 0, 106 top: 0, 107 width: obj.width ? obj.width : obj.offsetWidth, 108 height: obj.height ? obj.height : obj.offsetHeight 109 }; 110 111 if ( obj.style.width !== "" ) { 112 info.width = obj.style.width.replace("px",""); 113 } 114 115 if ( obj.style.height !== "" ) { 116 info.height = obj.style.height.replace("px",""); 117 } 118 119 while (obj) { 120 info.left += obj.offsetLeft; 121 info.top += obj.offsetTop; 122 obj = obj.offsetParent; 123 } 124 125 return info; 126 }, 127 128 Client: function(elem) { 129 // constructor for new simple upload client 130 this.handlers = {}; 131 132 // unique ID 133 this.id = ZeroClipboard_TableTools.nextId++; 134 this.movieId = 'ZeroClipboard_TableToolsMovie_' + this.id; 135 136 // register client with singleton to receive flash events 137 ZeroClipboard_TableTools.register(this.id, this); 138 139 // create movie 140 if (elem) { 141 this.glue(elem); 142 } 143 } 144 }; 145 146 ZeroClipboard_TableTools.Client.prototype = { 147 148 id: 0, // unique ID for us 149 ready: false, // whether movie is ready to receive events or not 150 movie: null, // reference to movie object 151 clipText: '', // text to copy to clipboard 152 fileName: '', // default file save name 153 action: 'copy', // action to perform 154 handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor 155 cssEffects: true, // enable CSS mouse effects on dom container 156 handlers: null, // user event handlers 157 sized: false, 158 sheetName: '', // default sheet name for excel export 159 160 glue: function(elem, title) { 161 // glue to DOM element 162 // elem can be ID or actual DOM element object 163 this.domElement = ZeroClipboard_TableTools.$(elem); 164 165 // float just above object, or zIndex 99 if dom element isn't set 166 var zIndex = 99; 167 if (this.domElement.style.zIndex) { 168 zIndex = parseInt(this.domElement.style.zIndex, 10) + 1; 169 } 170 171 // find X/Y position of domElement 172 var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement); 173 174 // create floating DIV above element 175 this.div = document.createElement('div'); 176 var style = this.div.style; 177 style.position = 'absolute'; 178 style.left = '0px'; 179 style.top = '0px'; 180 style.width = (box.width) + 'px'; 181 style.height = box.height + 'px'; 182 style.zIndex = zIndex; 183 184 if ( typeof title != "undefined" && title !== "" ) { 185 this.div.title = title; 186 } 187 if ( box.width !== 0 && box.height !== 0 ) { 188 this.sized = true; 189 } 190 191 // style.backgroundColor = '#f00'; // debug 192 if ( this.domElement ) { 193 this.domElement.appendChild(this.div); 194 this.div.innerHTML = this.getHTML( box.width, box.height ).replace(/&/g, '&'); 195 } 196 }, 197 198 positionElement: function() { 199 var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement); 200 var style = this.div.style; 201 202 style.position = 'absolute'; 203 //style.left = (this.domElement.offsetLeft)+'px'; 204 //style.top = this.domElement.offsetTop+'px'; 205 style.width = box.width + 'px'; 206 style.height = box.height + 'px'; 207 208 if ( box.width !== 0 && box.height !== 0 ) { 209 this.sized = true; 210 } else { 211 return; 212 } 213 214 var flash = this.div.childNodes[0]; 215 flash.width = box.width; 216 flash.height = box.height; 217 }, 218 219 getHTML: function(width, height) { 220 // return HTML for movie 221 var html = ''; 222 var flashvars = 'id=' + this.id + 223 '&width=' + width + 224 '&height=' + height; 225 226 if (navigator.userAgent.match(/MSIE/)) { 227 // IE gets an OBJECT tag 228 var protocol = location.href.match(/^https/i) ? 'https://' : 'http://'; 229 html += '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="'+protocol+'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0" width="'+width+'" height="'+height+'" id="'+this.movieId+'" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+ZeroClipboard_TableTools.moviePath+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/><param name="wmode" value="transparent"/></object>'; 230 } 231 else { 232 // all other browsers get an EMBED tag 233 html += '<embed id="'+this.movieId+'" src="'+ZeroClipboard_TableTools.moviePath+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="'+this.movieId+'" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" wmode="transparent" />'; 234 } 235 return html; 236 }, 237 238 hide: function() { 239 // temporarily hide floater offscreen 240 if (this.div) { 241 this.div.style.left = '-2000px'; 242 } 243 }, 244 245 show: function() { 246 // show ourselves after a call to hide() 247 this.reposition(); 248 }, 249 250 destroy: function() { 251 // destroy control and floater 252 var that = this; 253 254 if (this.domElement && this.div) { 255 $(this.div).remove(); 256 257 this.domElement = null; 258 this.div = null; 259 260 $.each( ZeroClipboard_TableTools.clients, function ( id, client ) { 261 if ( client === that ) { 262 delete ZeroClipboard_TableTools.clients[ id ]; 263 } 264 } ); 265 } 266 }, 267 268 reposition: function(elem) { 269 // reposition our floating div, optionally to new container 270 // warning: container CANNOT change size, only position 271 if (elem) { 272 this.domElement = ZeroClipboard_TableTools.$(elem); 273 if (!this.domElement) { 274 this.hide(); 275 } 276 } 277 278 if (this.domElement && this.div) { 279 var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement); 280 var style = this.div.style; 281 style.left = '' + box.left + 'px'; 282 style.top = '' + box.top + 'px'; 283 } 284 }, 285 286 clearText: function() { 287 // clear the text to be copy / saved 288 this.clipText = ''; 289 if (this.ready) { 290 this.movie.clearText(); 291 } 292 }, 293 294 appendText: function(newText) { 295 // append text to that which is to be copied / saved 296 this.clipText += newText; 297 if (this.ready) { this.movie.appendText(newText) ;} 298 }, 299 300 setText: function(newText) { 301 // set text to be copied to be copied / saved 302 this.clipText = newText; 303 if (this.ready) { this.movie.setText(newText) ;} 304 }, 305 306 setFileName: function(newText) { 307 // set the file name 308 this.fileName = newText; 309 if (this.ready) { 310 this.movie.setFileName(newText); 311 } 312 }, 313 314 setSheetData: function(data) { 315 // set the xlsx sheet data 316 if (this.ready) { 317 this.movie.setSheetData( JSON.stringify( data ) ); 318 } 319 }, 320 321 setAction: function(newText) { 322 // set action (save or copy) 323 this.action = newText; 324 if (this.ready) { 325 this.movie.setAction(newText); 326 } 327 }, 328 329 addEventListener: function(eventName, func) { 330 // add user event listener for event 331 // event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel 332 eventName = eventName.toString().toLowerCase().replace(/^on/, ''); 333 if (!this.handlers[eventName]) { 334 this.handlers[eventName] = []; 335 } 336 this.handlers[eventName].push(func); 337 }, 338 339 setHandCursor: function(enabled) { 340 // enable hand cursor (true), or default arrow cursor (false) 341 this.handCursorEnabled = enabled; 342 if (this.ready) { 343 this.movie.setHandCursor(enabled); 344 } 345 }, 346 347 setCSSEffects: function(enabled) { 348 // enable or disable CSS effects on DOM container 349 this.cssEffects = !!enabled; 350 }, 351 352 receiveEvent: function(eventName, args) { 353 var self; 354 355 // receive event from flash 356 eventName = eventName.toString().toLowerCase().replace(/^on/, ''); 357 358 // special behavior for certain events 359 switch (eventName) { 360 case 'load': 361 // movie claims it is ready, but in IE this isn't always the case... 362 // bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function 363 this.movie = document.getElementById(this.movieId); 364 if (!this.movie) { 365 self = this; 366 setTimeout( function() { self.receiveEvent('load', null); }, 1 ); 367 return; 368 } 369 370 // firefox on pc needs a "kick" in order to set these in certain cases 371 if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) { 372 self = this; 373 setTimeout( function() { self.receiveEvent('load', null); }, 100 ); 374 this.ready = true; 375 return; 376 } 377 378 this.ready = true; 379 this.movie.clearText(); 380 this.movie.appendText( this.clipText ); 381 this.movie.setFileName( this.fileName ); 382 this.movie.setAction( this.action ); 383 this.movie.setHandCursor( this.handCursorEnabled ); 384 break; 385 386 case 'mouseover': 387 if (this.domElement && this.cssEffects) { 388 //this.domElement.addClass('hover'); 389 if (this.recoverActive) { 390 this.domElement.addClass('active'); 391 } 392 } 393 break; 394 395 case 'mouseout': 396 if (this.domElement && this.cssEffects) { 397 this.recoverActive = false; 398 if (this.domElement.hasClass('active')) { 399 this.domElement.removeClass('active'); 400 this.recoverActive = true; 401 } 402 //this.domElement.removeClass('hover'); 403 } 404 break; 405 406 case 'mousedown': 407 if (this.domElement && this.cssEffects) { 408 this.domElement.addClass('active'); 409 } 410 break; 411 412 case 'mouseup': 413 if (this.domElement && this.cssEffects) { 414 this.domElement.removeClass('active'); 415 this.recoverActive = false; 416 } 417 break; 418 } // switch eventName 419 420 if (this.handlers[eventName]) { 421 for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) { 422 var func = this.handlers[eventName][idx]; 423 424 if (typeof(func) == 'function') { 425 // actual function reference 426 func(this, args); 427 } 428 else if ((typeof(func) == 'object') && (func.length == 2)) { 429 // PHP style object + method, i.e. [myObject, 'myMethod'] 430 func[0][ func[1] ](this, args); 431 } 432 else if (typeof(func) == 'string') { 433 // name of function 434 window[func](this, args); 435 } 436 } // foreach event handler defined 437 } // user defined handler for event 438 } 439 }; 440 441 ZeroClipboard_TableTools.hasFlash = function () 442 { 443 try { 444 var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); 445 if (fo) { 446 return true; 447 } 448 } 449 catch (e) { 450 if ( 451 navigator.mimeTypes && 452 navigator.mimeTypes['application/x-shockwave-flash'] !== undefined && 453 navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin 454 ) { 455 return true; 456 } 457 } 458 459 return false; 460 }; 461 462 // For the Flash binding to work, ZeroClipboard_TableTools must be on the global 463 // object list 464 window.ZeroClipboard_TableTools = ZeroClipboard_TableTools; 465 466 467 468 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 469 * Local (private) functions 470 */ 471 472 /** 473 * If a Buttons instance is initlaised before it is placed into the DOM, Flash 474 * won't be able to bind to it, so we need to wait until it is available, this 475 * method abstracts that out. 476 * 477 * @param {ZeroClipboard} flash ZeroClipboard instance 478 * @param {jQuery} node Button 479 */ 480 var _glue = function ( flash, node ) 481 { 482 var id = node.attr('id'); 483 484 if ( node.parents('html').length ) { 485 flash.glue( node[0], '' ); 486 } 487 else { 488 setTimeout( function () { 489 _glue( flash, node ); 490 }, 500 ); 491 } 492 }; 493 494 /** 495 * Get the file name for an exported file. 496 * 497 * @param {object} config Button configuration 498 * @param {boolean} incExtension Include the file name extension 499 */ 500 var _filename = function ( config, incExtension ) 501 { 502 // Backwards compatibility 503 var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined ? 504 config.title : 505 config.filename; 506 507 if ( typeof filename === 'function' ) { 508 filename = filename(); 509 } 510 511 if ( filename.indexOf( '*' ) !== -1 ) { 512 filename = $.trim( filename.replace( '*', $('title').text() ) ); 513 } 514 515 // Strip characters which the OS will object to 516 filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, ""); 517 518 return incExtension === undefined || incExtension === true ? 519 filename+config.extension : 520 filename; 521 }; 522 523 /** 524 * Get the sheet name for Excel exports. 525 * 526 * @param {object} config Button configuration 527 */ 528 var _sheetname = function ( config ) 529 { 530 var sheetName = 'Sheet1'; 531 532 if ( config.sheetName ) { 533 sheetName = config.sheetName.replace(/[\[\]\*\/\\\?\:]/g, ''); 534 } 535 536 return sheetName; 537 }; 538 539 /** 540 * Get the title for an exported file. 541 * 542 * @param {object} config Button configuration 543 */ 544 var _title = function ( config ) 545 { 546 var title = config.title; 547 548 if ( typeof title === 'function' ) { 549 title = title(); 550 } 551 552 return title.indexOf( '*' ) !== -1 ? 553 title.replace( '*', $('title').text() || 'Exported data' ) : 554 title; 555 }; 556 557 /** 558 * Set the flash text. This has to be broken up into chunks as the Javascript / 559 * Flash bridge has a size limit. There is no indication in the Flash 560 * documentation what this is, and it probably depends upon the browser. 561 * Experimentation shows that the point is around 50k when data starts to get 562 * lost, so an 8K limit used here is safe. 563 * 564 * @param {ZeroClipboard} flash ZeroClipboard instance 565 * @param {string} data Data to send to Flash 566 */ 567 var _setText = function ( flash, data ) 568 { 569 var parts = data.match(/[\s\S]{1,8192}/g) || []; 570 571 flash.clearText(); 572 for ( var i=0, len=parts.length ; i<len ; i++ ) 573 { 574 flash.appendText( parts[i] ); 575 } 576 }; 577 578 /** 579 * Get the newline character(s) 580 * 581 * @param {object} config Button configuration 582 * @return {string} Newline character 583 */ 584 var _newLine = function ( config ) 585 { 586 return config.newline ? 587 config.newline : 588 navigator.userAgent.match(/Windows/) ? 589 '\r\n' : 590 '\n'; 591 }; 592 593 /** 594 * Combine the data from the `buttons.exportData` method into a string that 595 * will be used in the export file. 596 * 597 * @param {DataTable.Api} dt DataTables API instance 598 * @param {object} config Button configuration 599 * @return {object} The data to export 600 */ 601 var _exportData = function ( dt, config ) 602 { 603 var newLine = _newLine( config ); 604 var data = dt.buttons.exportData( config.exportOptions ); 605 var boundary = config.fieldBoundary; 606 var separator = config.fieldSeparator; 607 var reBoundary = new RegExp( boundary, 'g' ); 608 var escapeChar = config.escapeChar !== undefined ? 609 config.escapeChar : 610 '\\'; 611 var join = function ( a ) { 612 var s = ''; 613 614 // If there is a field boundary, then we might need to escape it in 615 // the source data 616 for ( var i=0, ien=a.length ; i<ien ; i++ ) { 617 if ( i > 0 ) { 618 s += separator; 619 } 620 621 s += boundary ? 622 boundary + ('' + a[i]).replace( reBoundary, escapeChar+boundary ) + boundary : 623 a[i]; 624 } 625 626 return s; 627 }; 628 629 var header = config.header ? join( data.header )+newLine : ''; 630 var footer = config.footer && data.footer ? newLine+join( data.footer ) : ''; 631 var body = []; 632 633 for ( var i=0, ien=data.body.length ; i<ien ; i++ ) { 634 body.push( join( data.body[i] ) ); 635 } 636 637 return { 638 str: header + body.join( newLine ) + footer, 639 rows: body.length 640 }; 641 }; 642 643 644 // Basic initialisation for the buttons is common between them 645 var flashButton = { 646 available: function () { 647 return ZeroClipboard_TableTools.hasFlash(); 648 }, 649 650 init: function ( dt, button, config ) { 651 // Insert the Flash movie 652 ZeroClipboard_TableTools.moviePath = DataTable.Buttons.swfPath; 653 var flash = new ZeroClipboard_TableTools.Client(); 654 655 flash.setHandCursor( true ); 656 flash.addEventListener('mouseDown', function(client) { 657 config._fromFlash = true; 658 dt.button( button[0] ).trigger(); 659 config._fromFlash = false; 660 } ); 661 662 _glue( flash, button ); 663 664 config._flash = flash; 665 }, 666 667 destroy: function ( dt, button, config ) { 668 config._flash.destroy(); 669 }, 670 671 fieldSeparator: ',', 672 673 fieldBoundary: '"', 674 675 exportOptions: {}, 676 677 title: '*', 678 679 filename: '*', 680 681 extension: '.csv', 682 683 header: true, 684 685 footer: false 686 }; 687 688 689 /** 690 * Convert from numeric position to letter for column names in Excel 691 * @param {int} n Column number 692 * @return {string} Column letter(s) name 693 */ 694 function createCellPos( n ){ 695 var ordA = 'A'.charCodeAt(0); 696 var ordZ = 'Z'.charCodeAt(0); 697 var len = ordZ - ordA + 1; 698 var s = ""; 699 700 while( n >= 0 ) { 701 s = String.fromCharCode(n % len + ordA) + s; 702 n = Math.floor(n / len) - 1; 703 } 704 705 return s; 706 } 707 708 /** 709 * Create an XML node and add any children, attributes, etc without needing to 710 * be verbose in the DOM. 711 * 712 * @param {object} doc XML document 713 * @param {string} nodeName Node name 714 * @param {object} opts Options - can be `attr` (attributes), `children` 715 * (child nodes) and `text` (text content) 716 * @return {node} Created node 717 */ 718 function _createNode( doc, nodeName, opts ){ 719 var tempNode = doc.createElement( nodeName ); 720 721 if ( opts ) { 722 if ( opts.attr ) { 723 $(tempNode).attr( opts.attr ); 724 } 725 726 if( opts.children ) { 727 $.each( opts.children, function ( key, value ) { 728 tempNode.appendChild( value ); 729 }); 730 } 731 732 if( opts.text ) { 733 tempNode.appendChild( doc.createTextNode( opts.text ) ); 734 } 735 } 736 737 return tempNode; 738 } 739 740 /** 741 * Get the width for an Excel column based on the contents of that column 742 * @param {object} data Data for export 743 * @param {int} col Column index 744 * @return {int} Column width 745 */ 746 function _excelColWidth( data, col ) { 747 var max = data.header[col].length; 748 var len, lineSplit, str; 749 750 if ( data.footer && data.footer[col].length > max ) { 751 max = data.footer[col].length; 752 } 753 754 for ( var i=0, ien=data.body.length ; i<ien ; i++ ) { 755 var point = data.body[i][col]; 756 str = point !== null && point !== undefined ? 757 point.toString() : 758 ''; 759 760 // If there is a newline character, workout the width of the column 761 // based on the longest line in the string 762 if ( str.indexOf('\n') !== -1 ) { 763 lineSplit = str.split('\n'); 764 lineSplit.sort( function (a, b) { 765 return b.length - a.length; 766 } ); 767 768 len = lineSplit[0].length; 769 } 770 else { 771 len = str.length; 772 } 773 774 if ( len > max ) { 775 max = len; 776 } 777 778 // Max width rather than having potentially massive column widths 779 if ( max > 40 ) { 780 return 52; // 40 * 1.3 781 } 782 } 783 784 max *= 1.3; 785 786 // And a min width 787 return max > 6 ? max : 6; 788 } 789 790 var _serialiser = ""; 791 if (typeof window.XMLSerializer === 'undefined') { 792 _serialiser = new function () { 793 this.serializeToString = function (input) { 794 return input.xml 795 } 796 }; 797 } else { 798 _serialiser = new XMLSerializer(); 799 } 800 801 var _ieExcel; 802 803 804 /** 805 * Convert XML documents in an object to strings 806 * @param {object} obj XLSX document object 807 */ 808 function _xlsxToStrings( obj ) { 809 if ( _ieExcel === undefined ) { 810 // Detect if we are dealing with IE's _awful_ serialiser by seeing if it 811 // drop attributes 812 _ieExcel = _serialiser 813 .serializeToString( 814 $.parseXML( excelStrings['xl/worksheets/sheet1.xml'] ) 815 ) 816 .indexOf( 'xmlns:r' ) === -1; 817 } 818 819 $.each( obj, function ( name, val ) { 820 if ( $.isPlainObject( val ) ) { 821 _xlsxToStrings( val ); 822 } 823 else { 824 if ( _ieExcel ) { 825 // IE's XML serialiser will drop some name space attributes from 826 // from the root node, so we need to save them. Do this by 827 // replacing the namespace nodes with a regular attribute that 828 // we convert back when serialised. Edge does not have this 829 // issue 830 var worksheet = val.childNodes[0]; 831 var i, ien; 832 var attrs = []; 833 834 for ( i=worksheet.attributes.length-1 ; i>=0 ; i-- ) { 835 var attrName = worksheet.attributes[i].nodeName; 836 var attrValue = worksheet.attributes[i].nodeValue; 837 838 if ( attrName.indexOf( ':' ) !== -1 ) { 839 attrs.push( { name: attrName, value: attrValue } ); 840 841 worksheet.removeAttribute( attrName ); 842 } 843 } 844 845 for ( i=0, ien=attrs.length ; i<ien ; i++ ) { 846 var attr = val.createAttribute( attrs[i].name.replace( ':', '_dt_b_namespace_token_' ) ); 847 attr.value = attrs[i].value; 848 worksheet.setAttributeNode( attr ); 849 } 850 } 851 852 var str = _serialiser.serializeToString(val); 853 854 // Fix IE's XML 855 if ( _ieExcel ) { 856 // IE doesn't include the XML declaration 857 if ( str.indexOf( '<?xml' ) === -1 ) { 858 str = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+str; 859 } 860 861 // Return namespace attributes to being as such 862 str = str.replace( /_dt_b_namespace_token_/g, ':' ); 863 } 864 865 // Safari, IE and Edge will put empty name space attributes onto 866 // various elements making them useless. This strips them out 867 str = str.replace( /<([^<>]*?) xmlns=""([^<>]*?)>/g, '<$1 $2>' ); 868 869 obj[ name ] = str; 870 } 871 } ); 872 } 873 874 // Excel - Pre-defined strings to build a basic XLSX file 875 var excelStrings = { 876 "_rels/.rels": 877 '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+ 878 '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'+ 879 '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>'+ 880 '</Relationships>', 881 882 "xl/_rels/workbook.xml.rels": 883 '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+ 884 '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'+ 885 '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>'+ 886 '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>'+ 887 '</Relationships>', 888 889 "[Content_Types].xml": 890 '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+ 891 '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'+ 892 '<Default Extension="xml" ContentType="application/xml" />'+ 893 '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />'+ 894 '<Default Extension="jpeg" ContentType="image/jpeg" />'+ 895 '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" />'+ 896 '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" />'+ 897 '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" />'+ 898 '</Types>', 899 900 "xl/workbook.xml": 901 '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+ 902 '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'+ 903 '<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>'+ 904 '<workbookPr showInkAnnotation="0" autoCompressPictures="0"/>'+ 905 '<bookViews>'+ 906 '<workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>'+ 907 '</bookViews>'+ 908 '<sheets>'+ 909 '<sheet name="" sheetId="1" r:id="rId1"/>'+ 910 '</sheets>'+ 911 '</workbook>', 912 913 "xl/worksheets/sheet1.xml": 914 '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+ 915 '<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">'+ 916 '<sheetData/>'+ 917 '</worksheet>', 918 919 "xl/styles.xml": 920 '<?xml version="1.0" encoding="UTF-8"?>'+ 921 '<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">'+ 922 '<numFmts count="6">'+ 923 '<numFmt numFmtId="164" formatCode="#,##0.00_-\ [$$-45C]"/>'+ 924 '<numFmt numFmtId="165" formatCode=""£"#,##0.00"/>'+ 925 '<numFmt numFmtId="166" formatCode="[$€-2]\ #,##0.00"/>'+ 926 '<numFmt numFmtId="167" formatCode="0.0%"/>'+ 927 '<numFmt numFmtId="168" formatCode="#,##0;(#,##0)"/>'+ 928 '<numFmt numFmtId="169" formatCode="#,##0.00;(#,##0.00)"/>'+ 929 '</numFmts>'+ 930 '<fonts count="5" x14ac:knownFonts="1">'+ 931 '<font>'+ 932 '<sz val="11" />'+ 933 '<name val="Calibri" />'+ 934 '</font>'+ 935 '<font>'+ 936 '<sz val="11" />'+ 937 '<name val="Calibri" />'+ 938 '<color rgb="FFFFFFFF" />'+ 939 '</font>'+ 940 '<font>'+ 941 '<sz val="11" />'+ 942 '<name val="Calibri" />'+ 943 '<b />'+ 944 '</font>'+ 945 '<font>'+ 946 '<sz val="11" />'+ 947 '<name val="Calibri" />'+ 948 '<i />'+ 949 '</font>'+ 950 '<font>'+ 951 '<sz val="11" />'+ 952 '<name val="Calibri" />'+ 953 '<u />'+ 954 '</font>'+ 955 '</fonts>'+ 956 '<fills count="6">'+ 957 '<fill>'+ 958 '<patternFill patternType="none" />'+ 959 '</fill>'+ 960 '<fill/>'+ // Excel appears to use this as a dotted background regardless of values 961 '<fill>'+ 962 '<patternFill patternType="solid">'+ 963 '<fgColor rgb="FFD9D9D9" />'+ 964 '<bgColor indexed="64" />'+ 965 '</patternFill>'+ 966 '</fill>'+ 967 '<fill>'+ 968 '<patternFill patternType="solid">'+ 969 '<fgColor rgb="FFD99795" />'+ 970 '<bgColor indexed="64" />'+ 971 '</patternFill>'+ 972 '</fill>'+ 973 '<fill>'+ 974 '<patternFill patternType="solid">'+ 975 '<fgColor rgb="ffc6efce" />'+ 976 '<bgColor indexed="64" />'+ 977 '</patternFill>'+ 978 '</fill>'+ 979 '<fill>'+ 980 '<patternFill patternType="solid">'+ 981 '<fgColor rgb="ffc6cfef" />'+ 982 '<bgColor indexed="64" />'+ 983 '</patternFill>'+ 984 '</fill>'+ 985 '</fills>'+ 986 '<borders count="2">'+ 987 '<border>'+ 988 '<left />'+ 989 '<right />'+ 990 '<top />'+ 991 '<bottom />'+ 992 '<diagonal />'+ 993 '</border>'+ 994 '<border diagonalUp="false" diagonalDown="false">'+ 995 '<left style="thin">'+ 996 '<color auto="1" />'+ 997 '</left>'+ 998 '<right style="thin">'+ 999 '<color auto="1" />'+ 1000 '</right>'+ 1001 '<top style="thin">'+ 1002 '<color auto="1" />'+ 1003 '</top>'+ 1004 '<bottom style="thin">'+ 1005 '<color auto="1" />'+ 1006 '</bottom>'+ 1007 '<diagonal />'+ 1008 '</border>'+ 1009 '</borders>'+ 1010 '<cellStyleXfs count="1">'+ 1011 '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />'+ 1012 '</cellStyleXfs>'+ 1013 '<cellXfs count="61">'+ 1014 '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1015 '<xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1016 '<xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1017 '<xf numFmtId="0" fontId="3" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1018 '<xf numFmtId="0" fontId="4" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1019 '<xf numFmtId="0" fontId="0" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1020 '<xf numFmtId="0" fontId="1" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1021 '<xf numFmtId="0" fontId="2" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1022 '<xf numFmtId="0" fontId="3" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1023 '<xf numFmtId="0" fontId="4" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1024 '<xf numFmtId="0" fontId="0" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1025 '<xf numFmtId="0" fontId="1" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1026 '<xf numFmtId="0" fontId="2" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1027 '<xf numFmtId="0" fontId="3" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1028 '<xf numFmtId="0" fontId="4" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1029 '<xf numFmtId="0" fontId="0" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1030 '<xf numFmtId="0" fontId="1" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1031 '<xf numFmtId="0" fontId="2" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1032 '<xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1033 '<xf numFmtId="0" fontId="4" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1034 '<xf numFmtId="0" fontId="0" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1035 '<xf numFmtId="0" fontId="1" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1036 '<xf numFmtId="0" fontId="2" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1037 '<xf numFmtId="0" fontId="3" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1038 '<xf numFmtId="0" fontId="4" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1039 '<xf numFmtId="0" fontId="0" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1040 '<xf numFmtId="0" fontId="1" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1041 '<xf numFmtId="0" fontId="2" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1042 '<xf numFmtId="0" fontId="3" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1043 '<xf numFmtId="0" fontId="4" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1044 '<xf numFmtId="0" fontId="0" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1045 '<xf numFmtId="0" fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1046 '<xf numFmtId="0" fontId="2" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1047 '<xf numFmtId="0" fontId="3" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1048 '<xf numFmtId="0" fontId="4" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1049 '<xf numFmtId="0" fontId="0" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1050 '<xf numFmtId="0" fontId="1" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1051 '<xf numFmtId="0" fontId="2" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1052 '<xf numFmtId="0" fontId="3" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1053 '<xf numFmtId="0" fontId="4" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1054 '<xf numFmtId="0" fontId="0" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1055 '<xf numFmtId="0" fontId="1" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1056 '<xf numFmtId="0" fontId="2" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1057 '<xf numFmtId="0" fontId="3" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1058 '<xf numFmtId="0" fontId="4" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1059 '<xf numFmtId="0" fontId="0" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1060 '<xf numFmtId="0" fontId="1" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1061 '<xf numFmtId="0" fontId="2" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1062 '<xf numFmtId="0" fontId="3" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1063 '<xf numFmtId="0" fontId="4" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+ 1064 '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+ 1065 '<alignment horizontal="left"/>'+ 1066 '</xf>'+ 1067 '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+ 1068 '<alignment horizontal="center"/>'+ 1069 '</xf>'+ 1070 '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+ 1071 '<alignment horizontal="right"/>'+ 1072 '</xf>'+ 1073 '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+ 1074 '<alignment horizontal="fill"/>'+ 1075 '</xf>'+ 1076 '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+ 1077 '<alignment textRotation="90"/>'+ 1078 '</xf>'+ 1079 '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+ 1080 '<alignment wrapText="1"/>'+ 1081 '</xf>'+ 1082 '<xf numFmtId="9" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+ 1083 '<xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+ 1084 '<xf numFmtId="165" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+ 1085 '<xf numFmtId="166" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+ 1086 '<xf numFmtId="167" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+ 1087 '<xf numFmtId="168" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+ 1088 '<xf numFmtId="169" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+ 1089 '<xf numFmtId="3" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+ 1090 '<xf numFmtId="4" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+ 1091 '</cellXfs>'+ 1092 '<cellStyles count="1">'+ 1093 '<cellStyle name="Normal" xfId="0" builtinId="0" />'+ 1094 '</cellStyles>'+ 1095 '<dxfs count="0" />'+ 1096 '<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4" />'+ 1097 '</styleSheet>' 1098 }; 1099 // Note we could use 3 `for` loops for the styles, but when gzipped there is 1100 // virtually no difference in size, since the above can be easily compressed 1101 1102 // Pattern matching for special number formats. Perhaps this should be exposed 1103 // via an API in future? 1104 var _excelSpecials = [ 1105 { match: /^\-?\d+\.\d%$/, style: 60, fmt: function (d) { return d/100; } }, // Precent with d.p. 1106 { match: /^\-?\d+\.?\d*%$/, style: 56, fmt: function (d) { return d/100; } }, // Percent 1107 { match: /^\-?\$[\d,]+.?\d*$/, style: 57 }, // Dollars 1108 { match: /^\-?£[\d,]+.?\d*$/, style: 58 }, // Pounds 1109 { match: /^\-?€[\d,]+.?\d*$/, style: 59 }, // Euros 1110 { match: /^\([\d,]+\)$/, style: 61, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } }, // Negative numbers indicated by brackets 1111 { match: /^\([\d,]+\.\d{2}\)$/, style: 62, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } }, // Negative numbers indicated by brackets - 2d.p. 1112 { match: /^[\d,]+$/, style: 63 }, // Numbers with thousand separators 1113 { match: /^[\d,]+\.\d{2}$/, style: 64 } // Numbers with 2d.p. and thousands separators 1114 ]; 1115 1116 1117 1118 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1119 * DataTables options and methods 1120 */ 1121 1122 // Set the default SWF path 1123 DataTable.Buttons.swfPath = '//cdn.datatables.net/buttons/1.2.4/swf/flashExport.swf'; 1124 1125 // Method to allow Flash buttons to be resized when made visible - as they are 1126 // of zero height and width if initialised hidden 1127 DataTable.Api.register( 'buttons.resize()', function () { 1128 $.each( ZeroClipboard_TableTools.clients, function ( i, client ) { 1129 if ( client.domElement !== undefined && client.domElement.parentNode ) { 1130 client.positionElement(); 1131 } 1132 } ); 1133 } ); 1134 1135 1136 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1137 * Button definitions 1138 */ 1139 1140 // Copy to clipboard 1141 DataTable.ext.buttons.copyFlash = $.extend( {}, flashButton, { 1142 className: 'buttons-copy buttons-flash', 1143 1144 text: function ( dt ) { 1145 return dt.i18n( 'buttons.copy', 'Copy' ); 1146 }, 1147 1148 action: function ( e, dt, button, config ) { 1149 // Check that the trigger did actually occur due to a Flash activation 1150 if ( ! config._fromFlash ) { 1151 return; 1152 } 1153 1154 this.processing( true ); 1155 1156 var flash = config._flash; 1157 var data = _exportData( dt, config ); 1158 var output = config.customize ? 1159 config.customize( data.str, config ) : 1160 data.str; 1161 1162 flash.setAction( 'copy' ); 1163 _setText( flash, output ); 1164 1165 this.processing( false ); 1166 1167 dt.buttons.info( 1168 dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ), 1169 dt.i18n( 'buttons.copySuccess', { 1170 _: 'Copied %d rows to clipboard', 1171 1: 'Copied 1 row to clipboard' 1172 }, data.rows ), 1173 3000 1174 ); 1175 }, 1176 1177 fieldSeparator: '\t', 1178 1179 fieldBoundary: '' 1180 } ); 1181 1182 // CSV save file 1183 DataTable.ext.buttons.csvFlash = $.extend( {}, flashButton, { 1184 className: 'buttons-csv buttons-flash', 1185 1186 text: function ( dt ) { 1187 return dt.i18n( 'buttons.csv', 'CSV' ); 1188 }, 1189 1190 action: function ( e, dt, button, config ) { 1191 // Set the text 1192 var flash = config._flash; 1193 var data = _exportData( dt, config ); 1194 var output = config.customize ? 1195 config.customize( data.str, config ) : 1196 data.str; 1197 1198 flash.setAction( 'csv' ); 1199 flash.setFileName( _filename( config ) ); 1200 _setText( flash, output ); 1201 }, 1202 1203 escapeChar: '"' 1204 } ); 1205 1206 // Excel save file - this is really a CSV file using UTF-8 that Excel can read 1207 DataTable.ext.buttons.excelFlash = $.extend( {}, flashButton, { 1208 className: 'buttons-excel buttons-flash', 1209 1210 text: function ( dt ) { 1211 return dt.i18n( 'buttons.excel', 'Excel' ); 1212 }, 1213 1214 action: function ( e, dt, button, config ) { 1215 this.processing( true ); 1216 1217 var flash = config._flash; 1218 var rowPos = 0; 1219 var rels = $.parseXML( excelStrings['xl/worksheets/sheet1.xml'] ) ; //Parses xml 1220 var relsGet = rels.getElementsByTagName( "sheetData" )[0]; 1221 1222 var xlsx = { 1223 _rels: { 1224 ".rels": $.parseXML( excelStrings['_rels/.rels'] ) 1225 }, 1226 xl: { 1227 _rels: { 1228 "workbook.xml.rels": $.parseXML( excelStrings['xl/_rels/workbook.xml.rels'] ) 1229 }, 1230 "workbook.xml": $.parseXML( excelStrings['xl/workbook.xml'] ), 1231 "styles.xml": $.parseXML( excelStrings['xl/styles.xml'] ), 1232 "worksheets": { 1233 "sheet1.xml": rels 1234 } 1235 1236 }, 1237 "[Content_Types].xml": $.parseXML( excelStrings['[Content_Types].xml']) 1238 }; 1239 1240 var data = dt.buttons.exportData( config.exportOptions ); 1241 var currentRow, rowNode; 1242 var addRow = function ( row ) { 1243 currentRow = rowPos+1; 1244 rowNode = _createNode( rels, "row", { attr: {r:currentRow} } ); 1245 1246 for ( var i=0, ien=row.length ; i<ien ; i++ ) { 1247 // Concat both the Cell Columns as a letter and the Row of the cell. 1248 var cellId = createCellPos(i) + '' + currentRow; 1249 var cell = null; 1250 1251 // For null, undefined of blank cell, continue so it doesn't create the _createNode 1252 if ( row[i] === null || row[i] === undefined || row[i] === '' ) { 1253 continue; 1254 } 1255 1256 row[i] = $.trim( row[i] ); 1257 1258 // Special number formatting options 1259 for ( var j=0, jen=_excelSpecials.length ; j<jen ; j++ ) { 1260 var special = _excelSpecials[j]; 1261 1262 // TODO Need to provide the ability for the specials to say 1263 // if they are returning a string, since at the moment it is 1264 // assumed to be a number 1265 if ( row[i].match && ! row[i].match(/^0\d+/) && row[i].match( special.match ) ) { 1266 var val = row[i].replace(/[^\d\.\-]/g, ''); 1267 1268 if ( special.fmt ) { 1269 val = special.fmt( val ); 1270 } 1271 1272 cell = _createNode( rels, 'c', { 1273 attr: { 1274 r: cellId, 1275 s: special.style 1276 }, 1277 children: [ 1278 _createNode( rels, 'v', { text: val } ) 1279 ] 1280 } ); 1281 1282 break; 1283 } 1284 } 1285 1286 if ( ! cell ) { 1287 if ( typeof row[i] === 'number' || ( 1288 row[i].match && 1289 row[i].match(/^-?\d+(\.\d+)?$/) && 1290 ! row[i].match(/^0\d+/) ) 1291 ) { 1292 // Detect numbers - don't match numbers with leading zeros 1293 // or a negative anywhere but the start 1294 cell = _createNode( rels, 'c', { 1295 attr: { 1296 t: 'n', 1297 r: cellId 1298 }, 1299 children: [ 1300 _createNode( rels, 'v', { text: row[i] } ) 1301 ] 1302 } ); 1303 } 1304 else { 1305 // String output - replace non standard characters for text output 1306 var text = ! row[i].replace ? 1307 row[i] : 1308 row[i].replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, ''); 1309 1310 cell = _createNode( rels, 'c', { 1311 attr: { 1312 t: 'inlineStr', 1313 r: cellId 1314 }, 1315 children:{ 1316 row: _createNode( rels, 'is', { 1317 children: { 1318 row: _createNode( rels, 't', { 1319 text: text 1320 } ) 1321 } 1322 } ) 1323 } 1324 } ); 1325 } 1326 } 1327 1328 rowNode.appendChild( cell ); 1329 } 1330 1331 relsGet.appendChild(rowNode); 1332 rowPos++; 1333 }; 1334 1335 $( 'sheets sheet', xlsx.xl['workbook.xml'] ).attr( 'name', _sheetname( config ) ); 1336 1337 if ( config.customizeData ) { 1338 config.customizeData( data ); 1339 } 1340 1341 if ( config.header ) { 1342 addRow( data.header, rowPos ); 1343 $('row c', rels).attr( 's', '2' ); // bold 1344 } 1345 1346 for ( var n=0, ie=data.body.length ; n<ie ; n++ ) { 1347 addRow( data.body[n], rowPos ); 1348 } 1349 1350 if ( config.footer && data.footer ) { 1351 addRow( data.footer, rowPos); 1352 $('row:last c', rels).attr( 's', '2' ); // bold 1353 } 1354 1355 // Set column widths 1356 var cols = _createNode( rels, 'cols' ); 1357 $('worksheet', rels).prepend( cols ); 1358 1359 for ( var i=0, ien=data.header.length ; i<ien ; i++ ) { 1360 cols.appendChild( _createNode( rels, 'col', { 1361 attr: { 1362 min: i+1, 1363 max: i+1, 1364 width: _excelColWidth( data, i ), 1365 customWidth: 1 1366 } 1367 } ) ); 1368 } 1369 1370 // Let the developer customise the document if they want to 1371 if ( config.customize ) { 1372 config.customize( xlsx ); 1373 } 1374 1375 _xlsxToStrings( xlsx ); 1376 1377 flash.setAction( 'excel' ); 1378 flash.setFileName( _filename( config ) ); 1379 flash.setSheetData( xlsx ); 1380 _setText( flash, '' ); 1381 1382 this.processing( false ); 1383 }, 1384 1385 extension: '.xlsx' 1386 } ); 1387 1388 1389 1390 // PDF export 1391 DataTable.ext.buttons.pdfFlash = $.extend( {}, flashButton, { 1392 className: 'buttons-pdf buttons-flash', 1393 1394 text: function ( dt ) { 1395 return dt.i18n( 'buttons.pdf', 'PDF' ); 1396 }, 1397 1398 action: function ( e, dt, button, config ) { 1399 this.processing( true ); 1400 1401 // Set the text 1402 var flash = config._flash; 1403 var data = dt.buttons.exportData( config.exportOptions ); 1404 var totalWidth = dt.table().node().offsetWidth; 1405 1406 // Calculate the column width ratios for layout of the table in the PDF 1407 var ratios = dt.columns( config.columns ).indexes().map( function ( idx ) { 1408 return dt.column( idx ).header().offsetWidth / totalWidth; 1409 } ); 1410 1411 flash.setAction( 'pdf' ); 1412 flash.setFileName( _filename( config ) ); 1413 1414 _setText( flash, JSON.stringify( { 1415 title: _filename(config, false), 1416 message: typeof config.message == 'function' ? config.message(dt, button, config) : config.message, 1417 colWidth: ratios.toArray(), 1418 orientation: config.orientation, 1419 size: config.pageSize, 1420 header: config.header ? data.header : null, 1421 footer: config.footer ? data.footer : null, 1422 body: data.body 1423 } ) ); 1424 1425 this.processing( false ); 1426 }, 1427 1428 extension: '.pdf', 1429 1430 orientation: 'portrait', 1431 1432 pageSize: 'A4', 1433 1434 message: '', 1435 1436 newline: '\n' 1437 } ); 1438 1439 1440 return DataTable.Buttons; 1441 }));