1 var Download =
function(table){
4 this.columnsByIndex = [];
5 this.columnsByField = {};
11 Download.prototype.download =
function(type, filename, options, active, interceptCallback){
17 function buildLink(data, mime){
18 if(interceptCallback){
19 if(interceptCallback ===
true){
20 self.triggerDownload(data, mime, type, filename,
true);
22 interceptCallback(data);
26 self.triggerDownload(data, mime, type, filename);
30 if(typeof type ==
"function"){
33 if(
self.downloaders[type]){
34 downloadFunc =
self.downloaders[type];
36 console.warn(
"Download Error - No such download type found: ", type);
40 this.processColumns();
43 downloadFunc.call(
this,
self.processDefinitions(),
self.processData(active ||
"active") , options || {}, buildLink, this.config);
47 Download.prototype.processConfig =
function(){
54 if(this.table.options.downloadConfig){
55 for(var key in this.table.options.downloadConfig){
56 config[key] = this.table.options.downloadConfig[key];
60 if (config.rowGroups &&
this.table.options.groupBy &&
this.table.modExists(
"groupRows")){
61 this.config.rowGroups =
true;
64 if (config.columnGroups &&
this.table.columnManager.columns.length !=
this.table.columnManager.columnsByIndex.length){
65 this.config.columnGroups =
true;
68 if (config.columnCalcs &&
this.table.modExists(
"columnCalcs")){
69 this.config.columnCalcs =
true;
73 Download.prototype.processColumns =
function () {
76 self.columnsByIndex = [];
77 self.columnsByField = {};
79 self.table.columnManager.columnsByIndex.forEach(
function (column) {
81 if (column.field && column.definition.download !==
false && (column.visible || (!column.visible && column.definition.download))) {
82 self.columnsByIndex.push(column);
83 self.columnsByField[column.field] = column;
88 Download.prototype.processDefinitions =
function(){
90 processedDefinitions = [];
92 if(this.config.columnGroups){
93 self.table.columnManager.columns.forEach(
function(column){
94 var colData =
self.processColumnGroup(column);
97 processedDefinitions.push(colData);
101 self.columnsByIndex.forEach(
function(column){
102 if(column.download !==
false){
104 processedDefinitions.push(
self.processDefinition(column));
109 return processedDefinitions;
112 Download.prototype.processColumnGroup =
function(column){
113 var subGroups = column.columns,
115 var processedColumn = this.processDefinition(column);
118 title:processedColumn.title,
123 if(subGroups.length){
124 groupData.subGroups = [];
127 subGroups.forEach((subGroup) => {
128 var subGroupData = this.processColumnGroup(subGroup);
130 if(subGroupData.depth > maxDepth){
131 maxDepth = subGroupData.depth;
135 groupData.width += subGroupData.width;
136 groupData.subGroups.push(subGroupData);
140 groupData.depth += maxDepth;
142 if(!groupData.width){
146 if(column.field && column.definition.download !==
false && (column.visible || (!column.visible && column.definition.download))){
148 groupData.definition = processedColumn;
157 Download.prototype.processDefinition =
function(column){
160 for(var key in column.definition){
161 def[key] = column.definition[key];
164 if(typeof column.definition.downloadTitle !=
"undefined"){
165 def.title = column.definition.downloadTitle;
171 Download.prototype.processData =
function(active){
178 if(this.config.rowGroups){
180 if(active ==
"visible"){
182 rows =
self.table.rowManager.getRows(active);
184 rows.forEach((row) => {
185 if(row.type ==
"row"){
186 var group = row.getGroup();
188 if(groups.indexOf(group) === -1){
194 groups = this.table.modules.groupRows.getGroups();
197 groups.forEach((group) => {
198 data.push(this.processGroupData(group, rows));
202 data =
self.table.rowManager.getData(active,
"download");
206 if(this.config.columnCalcs){
207 calcs = this.table.getCalcResults();
216 if(typeof
self.table.options.downloadDataFormatter ==
"function"){
217 data =
self.table.options.downloadDataFormatter(data);
224 Download.prototype.processGroupData =
function(group, visRows){
225 var subGroups = group.getSubGroups();
232 if(subGroups.length){
233 groupData.subGroups = [];
235 subGroups.forEach((subGroup) => {
236 groupData.subGroups.push(this.processGroupData(subGroup, visRows));
242 group.rows.forEach(
function(row){
243 if(visRows.indexOf(row) > -1){
244 groupData.rows.push(row.getData(
"download"));
248 groupData.rows = group.getData(
true,
"download");
256 Download.prototype.triggerDownload =
function(data, mime, type, filename, newTab){
257 var element = document.createElement(
'a'),
258 blob =
new Blob([data],{type:mime}),
259 filename = filename ||
"Tabulator." + (typeof type ===
"function" ?
"txt" : type);
261 blob = this.table.options.downloadReady.call(this.table, data, blob);
266 window.open(window.URL.createObjectURL(blob));
268 if(navigator.msSaveOrOpenBlob){
269 navigator.msSaveOrOpenBlob(blob, filename);
271 element.setAttribute(
'href', window.URL.createObjectURL(blob));
274 element.setAttribute(
'download', filename);
277 element.style.display =
'none';
278 document.body.appendChild(element);
282 document.body.removeChild(element);
287 if(this.table.options.downloadComplete){
288 this.table.options.downloadComplete();
295 Download.prototype.getFieldValue =
function(field, data){
296 var column = this.columnsByField[field];
299 return column.getFieldValue(data);
306 Download.prototype.commsReceived =
function(table, action, data){
309 this.download(data.type,
"", data.options, data.active, data.intercept);
316 Download.prototype.downloaders = {
317 csv:
function(columns, data, options, setFileContents, config){
321 delimiter = options && options.delimiter ? options.delimiter :
",",
322 fileContents, output;
325 function parseSimpleTitles(){
326 columns.forEach(
function(column){
327 titles.push(
'"' + String(column.title).split(
'"').join(
'""') +
'"');
328 fields.push(column.field);
332 function parseColumnGroup(column, level){
333 if(column.subGroups){
334 column.subGroups.forEach(
function(subGroup){
335 parseColumnGroup(subGroup, level+1);
338 titles.push(
'"' + String(column.title).split(
'"').join(
'""') +
'"');
339 fields.push(column.definition.field);
343 if(config.columnGroups){
344 console.warn(
"Download Warning - CSV downloader cannot process column groups");
346 columns.forEach(
function(column){
347 parseColumnGroup(column,0);
355 fileContents = [titles.join(delimiter)];
357 function parseRows(data){
359 data.forEach(
function(row){
362 fields.forEach(
function(field){
363 var value =
self.getFieldValue(field, row);
365 switch(typeof value){
367 value = JSON.stringify(value);
380 rowData.push(
'"' + String(value).split(
'"').join(
'""') +
'"');
383 fileContents.push(rowData.join(delimiter));
387 function parseGroup(group){
389 group.subGroups.forEach(
function(subGroup){
390 parseGroup(subGroup);
393 parseRows(group.rows);
397 if(config.columnCalcs){
398 console.warn(
"Download Warning - CSV downloader cannot process column calculations");
402 if(config.rowGroups){
403 console.warn(
"Download Warning - CSV downloader cannot process row groups");
405 data.forEach(
function(group){
412 output = fileContents.join(
"\n");
415 output =
"\ufeff" + output;
418 setFileContents(output,
"text/csv");
421 json:
function(columns, data, options, setFileContents, config){
424 if(config.columnCalcs){
425 console.warn(
"Download Warning - CSV downloader cannot process column calculations");
429 fileContents = JSON.stringify(data, null,
'\t');
431 setFileContents(fileContents,
"application/json");
434 pdf:
function(columns, data, options, setFileContents, config){
442 autoTableParams = {},
443 rowGroupStyles = options.rowGroupStyles || {
449 rowCalcStyles = options.rowCalcStyles || {
455 jsPDFParams = options.jsPDF || {},
456 title = options && options.title ? options.title :
"";
458 if(config.columnCalcs){
463 if(!jsPDFParams.orientation){
464 jsPDFParams.orientation = options.orientation ||
"landscape";
467 if(!jsPDFParams.unit){
468 jsPDFParams.unit =
"pt";
472 function parseSimpleTitles(){
473 columns.forEach(
function(column){
475 header.push(column.title ||
"");
476 fields.push(column.field);
483 function parseColumnGroup(column, level){
484 var colSpan = column.width,
487 content:column.title ||
"",
490 if(column.subGroups){
491 column.subGroups.forEach(
function(subGroup){
492 parseColumnGroup(subGroup, level+1);
496 fields.push(column.definition.field);
497 rowSpan = headerDepth - level;
500 col.rowSpan = rowSpan;
503 header[level].push(col);
508 for(var i = level + 1; i < headerDepth; i++){
513 for(var i = 0; i < colSpan; i++){
514 header[level].push(
"");
518 if(config.columnGroups){
519 columns.forEach(
function(column){
520 if(column.depth > headerDepth){
521 headerDepth = column.depth;
525 for(var i=0; i < headerDepth; i++){
529 columns.forEach(
function(column){
530 parseColumnGroup(column,0);
537 function parseValue(value){
538 switch(typeof value){
540 value = JSON.stringify(value);
555 function parseRows(data){
557 data.forEach(
function(row){
558 body.push(parseRow(row));
562 function parseRow(row, styles){
565 fields.forEach(
function(field){
566 var value =
self.getFieldValue(field, row);
567 value = parseValue(value);
582 function parseGroup(group, calcObj){
585 groupData.push({content:parseValue(group.key), colSpan:fields.length, styles:rowGroupStyles});
587 body.push(groupData);
590 group.subGroups.forEach(
function(subGroup){
591 parseGroup(subGroup, calcObj[group.key] ? calcObj[group.key].groups || {} : {});
595 if(config.columnCalcs){
596 addCalcRow(calcObj, group.key,
"top");
599 parseRows(group.rows);
601 if(config.columnCalcs){
602 addCalcRow(calcObj, group.key,
"bottom");
607 function addCalcRow(calcs, selector, pos){
608 var calcData = calcs[selector];
612 calcData = calcData[pos];
615 if(Object.keys(calcData).length){
616 body.push(parseRow(calcData, rowCalcStyles));
621 if(config.rowGroups){
622 data.forEach(
function(group){
623 parseGroup(group, calcs);
626 if(config.columnCalcs){
627 addCalcRow(calcs,
"top");
632 if(config.columnCalcs){
633 addCalcRow(calcs,
"bottom");
637 var doc =
new jsPDF(jsPDFParams);
639 if(options && options.autoTable){
640 if(typeof options.autoTable ===
"function"){
641 autoTableParams = options.autoTable(doc) || {};
643 autoTableParams = options.autoTable;
648 autoTableParams.addPageContent =
function(data) {
649 doc.text(title, 40, 30);
653 autoTableParams.head = header;
654 autoTableParams.body = body;
656 doc.autoTable(autoTableParams);
658 if(options && options.documentProcessing){
659 options.documentProcessing(doc);
662 setFileContents(doc.output(
"arraybuffer"),
"application/pdf");
665 xlsx:
function(columns, data, options, setFileContents, config){
667 sheetName = options.sheetName ||
"Sheet1",
668 workbook = XLSX.utils.book_new(),
671 groupColumnIndexs = [],
675 workbook.SheetNames = [];
676 workbook.Sheets = {};
678 if(config.columnCalcs){
683 function generateSheet(){
690 function rowsToSheet(){
692 var range = {s: {c:0, r:0}, e: {c:fields.length, r:rows.length }};
694 XLSX.utils.sheet_add_aoa(sheet, rows);
696 sheet[
'!ref'] = XLSX.utils.encode_range(range);
698 var merges = generateMerges();
701 sheet[
"!merges"] = merges;
707 function parseSimpleTitles(){
709 columns.forEach(
function(column){
710 titles.push(column.title);
711 fields.push(column.field);
717 function parseColumnGroup(column, level){
719 if(typeof titles[level] ===
"undefined"){
723 if(typeof groupColumnIndexs[level] ===
"undefined"){
724 groupColumnIndexs[level] = [];
727 if(column.width > 1){
729 groupColumnIndexs[level].push({
731 start:titles[level].length,
732 end:titles[level].length + column.width - 1,
736 titles[level].push(column.title);
738 if(column.subGroups){
739 column.subGroups.forEach(
function(subGroup){
740 parseColumnGroup(subGroup, level+1);
743 fields.push(column.definition.field);
744 padColumnTitles(fields.length - 1, level);
746 groupColumnIndexs[level].push({
748 start:fields.length - 1,
755 function padColumnTitles(){
758 titles.forEach(
function(title){
759 var len = title.length;
765 titles.forEach(
function(title){
766 var len = title.length;
768 for(var i = len; i < max; i++){
775 if(config.columnGroups){
776 columns.forEach(
function(column){
777 parseColumnGroup(column,0);
780 titles.forEach(
function(title){
787 function generateMerges(){
790 groupRowIndexs.forEach(
function(index){
791 output.push({s:{r:index,c:0},e:{r:index,c:fields.length - 1}});
794 groupColumnIndexs.forEach(
function(merges, level){
795 merges.forEach(
function(merge){
796 if(merge.type ===
"hoz"){
797 output.push({s:{r:level,c:merge.start},e:{r:level,c:merge.end}});
799 if(level != titles.length - 1){
800 output.push({s:{r:level,c:merge.start},e:{r:titles.length - 1,c:merge.start}});
810 function parseRows(data){
811 data.forEach(
function(row){
812 rows.push(parseRow(row));
816 function parseRow(row){
819 fields.forEach(
function(field){
820 var value =
self.getFieldValue(field, row);
821 rowData.push(!(value instanceof Date) && typeof value ===
"object" ? JSON.stringify(value) : value);
828 function addCalcRow(calcs, selector, pos){
829 var calcData = calcs[selector];
833 calcData = calcData[pos];
836 if(Object.keys(calcData).length){
837 calcRowIndexs.push(rows.length);
838 rows.push(parseRow(calcData));
843 function parseGroup(group, calcObj){
846 groupData.push(group.key);
848 groupRowIndexs.push(rows.length);
850 rows.push(groupData);
853 group.subGroups.forEach(
function(subGroup){
854 parseGroup(subGroup, calcObj[group.key] ? calcObj[group.key].groups || {} : {});
858 if(config.columnCalcs){
859 addCalcRow(calcObj, group.key,
"top");
862 parseRows(group.rows);
864 if(config.columnCalcs){
865 addCalcRow(calcObj, group.key,
"bottom");
872 if(config.rowGroups){
873 data.forEach(
function(group){
874 parseGroup(group, calcs);
877 if(config.columnCalcs){
878 addCalcRow(calcs,
"top");
883 if(config.columnCalcs){
884 addCalcRow(calcs,
"bottom");
888 worksheet = rowsToSheet();
893 if(options.sheetOnly){
894 setFileContents(generateSheet());
899 for(var sheet in options.sheets){
901 if(options.sheets[sheet] ===
true){
902 workbook.SheetNames.push(sheet);
903 workbook.Sheets[sheet] = generateSheet();
906 workbook.SheetNames.push(sheet);
908 this.table.modules.comms.send(options.sheets[sheet],
"download",
"intercept",{
910 options:{sheetOnly:
true},
912 intercept:
function(data){
913 workbook.Sheets[sheet] = data;
919 workbook.SheetNames.push(sheetName);
920 workbook.Sheets[sheetName] = generateSheet();
923 if(options.documentProcessing){
924 workbook = options.documentProcessing(workbook);
929 var buf =
new ArrayBuffer(s.length);
930 var view =
new Uint8Array(buf);
931 for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
935 output = XLSX.write(workbook, {bookType:
'xlsx', bookSST:
true, type:
'binary'});
937 setFileContents(s2ab(output),
"application/octet-stream");
940 html:
function(columns, data, options, setFileContents, config){
941 if(this.table.modExists(
"htmlTableExport",
true)){
942 setFileContents(this.table.modules.htmlTableExport.getHtml(
true, options.style, config),
"text/html");
949 Tabulator.prototype.registerModule(
"download", Download);