Skip to content

Commit 1d17cd3

Browse files
authored
Merge pull request #648 from jmaister/fix-remove-columns
Fix remove columns
2 parents c6cc3de + 0c9e364 commit 1d17cd3

File tree

4 files changed

+262
-156
lines changed

4 files changed

+262
-156
lines changed

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
"build": "webpack --config webpack.config.js --progress --mode development --watch",
2323
"prod": "webpack --config webpack.config.js --progress --mode production",
2424
"test": "jest --coverage && coveralls < coverage/lcov.info",
25-
"jest": "jest --coverage"
25+
"jest": "jest --coverage",
26+
"j": "jest --watch --testMatch remove",
27+
"watch": "jest --watch"
2628
},
2729
"main": "dist/excellentexport.js",
2830
"devDependencies": {

src/excellentexport.ts

+18-155
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import * as XLSX from 'xlsx';
1111

12+
import * as utils from './utils';
13+
1214
export interface ConvertOptions {
1315
anchor?: (string|HTMLAnchorElement),
1416
openAsDownload?: boolean,
@@ -31,147 +33,8 @@ export interface SheetOptions {
3133

3234
const ExcellentExport = function() {
3335

34-
const b64toBlob = function (b64Data:string, contentType:string, sliceSize?:number): Blob {
35-
// function taken from http://stackoverflow.com/a/16245768/2591950
36-
// author Jeremy Banks http://stackoverflow.com/users/1114/jeremy-banks
37-
contentType = contentType || '';
38-
sliceSize = sliceSize || 512;
39-
40-
const byteCharacters = atob(b64Data);
41-
const byteArrays = [];
42-
43-
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
44-
const slice = byteCharacters.slice(offset, offset + sliceSize);
45-
46-
const byteNumbers = new Array(slice.length);
47-
for (let i = 0; i < slice.length; i = i + 1) {
48-
byteNumbers[i] = slice.charCodeAt(i);
49-
}
50-
51-
const byteArray = new Uint8Array(byteNumbers);
52-
53-
byteArrays.push(byteArray);
54-
}
55-
56-
return new Blob(byteArrays, {
57-
type: contentType
58-
});
59-
};
60-
6136
const version = "3.7.2";
62-
const template = {excel: '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta name=ProgId content=Excel.Sheet> <meta name=Generator content="Microsoft Excel 11"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table></body></html>'};
63-
let csvDelimiter = ",";
64-
let csvNewLine = "\r\n";
65-
66-
/**
67-
* Convert a string to Base64.
68-
*/
69-
const base64 = function(s:string) : string {
70-
return btoa(unescape(encodeURIComponent(s)));
71-
};
72-
73-
const format = function(s:string, context:any) : string {
74-
return s.replace(new RegExp("{(\\w+)}", "g"), function(m, p) {
75-
return context[p];
76-
});
77-
};
78-
79-
/**
80-
* Get element by ID.
81-
* @param {*} element
82-
*/
83-
const getTable = function(element :(HTMLTableElement|string)) : HTMLTableElement {
84-
if (typeof element === 'string') {
85-
return document.getElementById(element) as HTMLTableElement;
86-
}
87-
return element;
88-
};
89-
90-
/**
91-
* Get element by ID.
92-
* @param {*} element
93-
*/
94-
const getAnchor = function(element :(HTMLAnchorElement|string)) : HTMLAnchorElement {
95-
if (typeof element === 'string') {
96-
return document.getElementById(element) as HTMLAnchorElement;
97-
}
98-
return element;
99-
};
100-
101-
/**
102-
* Encode a value for CSV.
103-
* @param {*} value
104-
*/
105-
const fixCSVField = function(value) {
106-
let fixedValue = value;
107-
const addQuotes = (value.indexOf(csvDelimiter) !== -1) || (value.indexOf('\r') !== -1) || (value.indexOf('\n') !== -1);
108-
const replaceDoubleQuotes = (value.indexOf('"') !== -1);
10937

110-
if (replaceDoubleQuotes) {
111-
fixedValue = fixedValue.replace(/"/g, '""');
112-
}
113-
if (addQuotes || replaceDoubleQuotes) {
114-
fixedValue = '"' + fixedValue + '"';
115-
}
116-
117-
return fixedValue;
118-
};
119-
120-
const tableToArray = function(table:HTMLTableElement) : any[][] {
121-
let tableInfo = Array.prototype.map.call(table.querySelectorAll('tr'), function(tr) {
122-
return Array.prototype.map.call(tr.querySelectorAll('th,td'), function(td) {
123-
return td.innerHTML;
124-
});
125-
});
126-
return tableInfo;
127-
};
128-
129-
const tableToCSV = function(table:HTMLTableElement) : string {
130-
let data = "";
131-
for (let i = 0; i < table.rows.length; i=i+1) {
132-
const row = table.rows[i];
133-
for (let j = 0; j < row.cells.length; j=j+1) {
134-
const col = row.cells[j];
135-
data = data + (j ? csvDelimiter : '') + fixCSVField(col.textContent.trim());
136-
}
137-
data = data + csvNewLine;
138-
}
139-
return data;
140-
};
141-
142-
const createDownloadLink = function(anchor:HTMLAnchorElement, base64data:string, exporttype:string, filename:string) : boolean {
143-
if (window.navigator.msSaveBlob) {
144-
const blob = b64toBlob(base64data, exporttype);
145-
window.navigator.msSaveBlob(blob, filename);
146-
return false;
147-
} else if (window.URL.createObjectURL) {
148-
const blob = b64toBlob(base64data, exporttype);
149-
anchor.href = window.URL.createObjectURL(blob);
150-
} else {
151-
anchor.download = filename;
152-
anchor.href = "data:" + exporttype + ";base64," + base64data;
153-
}
154-
155-
// Return true to allow the link to work
156-
return true;
157-
};
158-
159-
// String to ArrayBuffer
160-
const s2ab = function (s:string): ArrayBuffer {
161-
let buf = new ArrayBuffer(s.length);
162-
let view = new Uint8Array(buf);
163-
for (let i=0; i !== s.length; ++i) {
164-
view[i] = s.charCodeAt(i) & 0xFF;
165-
}
166-
return buf;
167-
};
168-
169-
const removeColumn = function(arr2d:any[][], colIndex:number) {
170-
for (let i = 0; i < arr2d.length; i++) {
171-
const row = arr2d[i];
172-
row.splice(colIndex, 1);
173-
}
174-
};
17538

17639
/*
17740
ExcellentExport.convert(options, sheets);
@@ -225,7 +88,7 @@ const ExcellentExport = function() {
22588
// Select data source
22689
let dataArray;
22790
if (sheetConf.from && sheetConf.from.table) {
228-
dataArray = tableToArray(getTable(sheetConf.from.table));
91+
dataArray = utils.tableToArray(utils.getTable(sheetConf.from.table));
22992
} else if(sheetConf.from && sheetConf.from.array) {
23093
dataArray = sheetConf.from.array
23194
} else {
@@ -242,10 +105,7 @@ const ExcellentExport = function() {
242105
}
243106
// Filter columns
244107
if (sheetConf.removeColumns) {
245-
const toRemove = sheetConf.removeColumns.sort().reverse();
246-
toRemove.forEach(function(columnIndex) {
247-
removeColumn(dataArray, columnIndex);
248-
});
108+
utils.removeColumns(dataArray, sheetConf.removeColumns);
249109
}
250110

251111
// Convert data, by value
@@ -272,15 +132,15 @@ const ExcellentExport = function() {
272132

273133
const wbOut:string = XLSX.write(workbook, {bookType: options.format, bookSST:true, type: 'binary'});
274134
try {
275-
const blob = new Blob([s2ab(wbOut)], { type: "application/octet-stream" });
135+
const blob = new Blob([utils.string2ArrayBuffer(wbOut)], { type: "application/octet-stream" });
276136
const filename = (options.filename || 'download') + '.' + options.format;
277137
// Support for IE.
278138
if (window.navigator.msSaveBlob) {
279139
window.navigator.msSaveBlob(blob, filename);
280140
return false;
281141
}
282142
if (options.anchor) {
283-
const anchor = getAnchor(options.anchor);
143+
const anchor = utils.getAnchor(options.anchor);
284144
anchor.href = window.URL.createObjectURL(blob);
285145
anchor.download = filename;
286146
} else if (options.openAsDownload) {
@@ -306,25 +166,28 @@ const ExcellentExport = function() {
306166
return version;
307167
},
308168
excel: function(anchor:(HTMLAnchorElement|string), table:HTMLTableElement, name:string) {
309-
table = getTable(table);
310-
anchor = getAnchor(anchor);
169+
table = utils.getTable(table);
170+
anchor = utils.getAnchor(anchor);
311171
const ctx = {worksheet: name || 'Worksheet', table: table.innerHTML};
312-
const b64 = base64(format(template.excel, ctx));
313-
return createDownloadLink(anchor, b64, 'application/vnd.ms-excel','export.xls');
172+
const b64 = utils.base64(utils.format(utils.templates.excel, ctx));
173+
return utils.createDownloadLink(anchor, b64, 'application/vnd.ms-excel','export.xls');
314174
},
315175
csv: function(anchor:(HTMLAnchorElement|string), table:HTMLTableElement, delimiter?:string, newLine?:string) {
176+
let csvDelimiter = ",";
177+
let csvNewLine = "\r\n";
178+
316179
if (delimiter !== undefined && delimiter) {
317180
csvDelimiter = delimiter;
318181
}
319182
if (newLine !== undefined && newLine) {
320183
csvNewLine = newLine;
321184
}
322185

323-
table = getTable(table);
324-
anchor = getAnchor(anchor);
325-
const csvData = "\uFEFF" + tableToCSV(table);
326-
const b64 = base64(csvData);
327-
return createDownloadLink(anchor, b64, 'application/csv', 'export.csv');
186+
table = utils.getTable(table);
187+
anchor = utils.getAnchor(anchor);
188+
const csvData = "\uFEFF" + utils.tableToCSV(table, csvDelimiter, csvNewLine);
189+
const b64 = utils.base64(csvData);
190+
return utils.createDownloadLink(anchor, b64, 'application/csv', 'export.csv');
328191
},
329192
convert: function(options:ConvertOptions, sheets:SheetOptions[]) {
330193
return convert(options, sheets);

src/utils.ts

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
2+
export const b64toBlob = function (b64Data:string, contentType:string, sliceSize?:number): Blob {
3+
// function taken from http://stackoverflow.com/a/16245768/2591950
4+
// author Jeremy Banks http://stackoverflow.com/users/1114/jeremy-banks
5+
contentType = contentType || '';
6+
sliceSize = sliceSize || 512;
7+
8+
const byteCharacters = atob(b64Data);
9+
const byteArrays = [];
10+
11+
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
12+
const slice = byteCharacters.slice(offset, offset + sliceSize);
13+
14+
const byteNumbers = new Array(slice.length);
15+
for (let i = 0; i < slice.length; i = i + 1) {
16+
byteNumbers[i] = slice.charCodeAt(i);
17+
}
18+
19+
const byteArray = new Uint8Array(byteNumbers);
20+
21+
byteArrays.push(byteArray);
22+
}
23+
24+
return new Blob(byteArrays, {
25+
type: contentType
26+
});
27+
};
28+
29+
export const templates = {excel: '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta name=ProgId content=Excel.Sheet> <meta name=Generator content="Microsoft Excel 11"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table></body></html>'};
30+
31+
/**
32+
* Convert a string to Base64.
33+
*/
34+
export const base64 = function(s:string) : string {
35+
return btoa(unescape(encodeURIComponent(s)));
36+
};
37+
38+
export const format = function(s:string, context:any) : string {
39+
return s.replace(new RegExp("{(\\w+)}", "g"), function(m, p) {
40+
return context[p];
41+
});
42+
};
43+
44+
/**
45+
* Get element by ID.
46+
* @param {*} element
47+
*/
48+
export const getTable = function(element :(HTMLTableElement|string)) : HTMLTableElement {
49+
if (typeof element === 'string') {
50+
return document.getElementById(element) as HTMLTableElement;
51+
}
52+
return element;
53+
};
54+
55+
/**
56+
* Get element by ID.
57+
* @param {*} element
58+
*/
59+
export const getAnchor = function(element :(HTMLAnchorElement|string)) : HTMLAnchorElement {
60+
if (typeof element === 'string') {
61+
return document.getElementById(element) as HTMLAnchorElement;
62+
}
63+
return element;
64+
};
65+
66+
/**
67+
* Encode a value for CSV.
68+
* @param {*} value
69+
*/
70+
const fixCSVField = function(value:string, csvDelimiter:string) {
71+
let fixedValue = value;
72+
const addQuotes = (value.indexOf(csvDelimiter) !== -1) || (value.indexOf('\r') !== -1) || (value.indexOf('\n') !== -1);
73+
const replaceDoubleQuotes = (value.indexOf('"') !== -1);
74+
75+
if (replaceDoubleQuotes) {
76+
fixedValue = fixedValue.replace(/"/g, '""');
77+
}
78+
if (addQuotes || replaceDoubleQuotes) {
79+
fixedValue = '"' + fixedValue + '"';
80+
}
81+
82+
return fixedValue;
83+
};
84+
85+
export const tableToArray = function(table:HTMLTableElement) : any[][] {
86+
let tableInfo = Array.prototype.map.call(table.querySelectorAll('tr'), function(tr) {
87+
return Array.prototype.map.call(tr.querySelectorAll('th,td'), function(td) {
88+
return td.innerHTML;
89+
});
90+
});
91+
return tableInfo;
92+
};
93+
94+
export const tableToCSV = function(table:HTMLTableElement, csvDelimiter:string = ',', csvNewLine:string = '\n') : string {
95+
let data = "";
96+
for (let i = 0; i < table.rows.length; i=i+1) {
97+
const row = table.rows[i];
98+
for (let j = 0; j < row.cells.length; j=j+1) {
99+
const col = row.cells[j];
100+
data = data + (j ? csvDelimiter : '') + fixCSVField(col.textContent.trim(), csvDelimiter);
101+
}
102+
data = data + csvNewLine;
103+
}
104+
return data;
105+
};
106+
107+
export const createDownloadLink = function(anchor:HTMLAnchorElement, base64data:string, exporttype:string, filename:string) : boolean {
108+
if (window.navigator.msSaveBlob) {
109+
const blob = b64toBlob(base64data, exporttype);
110+
window.navigator.msSaveBlob(blob, filename);
111+
return false;
112+
} else if (window.URL.createObjectURL) {
113+
const blob = b64toBlob(base64data, exporttype);
114+
anchor.href = window.URL.createObjectURL(blob);
115+
} else {
116+
anchor.download = filename;
117+
anchor.href = "data:" + exporttype + ";base64," + base64data;
118+
}
119+
120+
// Return true to allow the link to work
121+
return true;
122+
};
123+
124+
// String to ArrayBuffer
125+
export const string2ArrayBuffer = function (s:string): ArrayBuffer {
126+
let buf = new ArrayBuffer(s.length);
127+
let view = new Uint8Array(buf);
128+
for (let i=0; i !== s.length; ++i) {
129+
view[i] = s.charCodeAt(i) & 0xFF;
130+
}
131+
return buf;
132+
};
133+
134+
export const removeColumns = function(dataArray:any[][], columnIndexes:number[]) {
135+
const uniqueIndexes = [...new Set(columnIndexes)].sort().reverse();
136+
uniqueIndexes.forEach(function(columnIndex) {
137+
dataArray.forEach(function(row) {
138+
row.splice(columnIndex, 1);
139+
});
140+
});
141+
};

0 commit comments

Comments
 (0)