excellentexport 3.7.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +1 -1
- package/.github/dependabot.yml +7 -0
- package/.github/workflows/webpack.yml +28 -0
- package/LICENSE.txt +20 -20
- package/README.md +286 -266
- package/bower.json +32 -32
- package/dist/excellentexport.d.ts +35 -33
- package/dist/excellentexport.js +2 -1
- package/dist/excellentexport.js.LICENSE.txt +3 -0
- package/dist/utils.d.ts +29 -0
- package/index.bigtable.html +80 -80
- package/index.filters.html +63 -63
- package/index.html +127 -127
- package/index.noanchor.html +31 -31
- package/index.rtl.html +69 -0
- package/jest.config.ts +203 -203
- package/package.json +60 -56
- package/src/excellentexport.ts +203 -335
- package/src/utils.ts +141 -0
- package/test/convert-filters.test.ts +70 -70
- package/test/convert-table.test.ts +71 -71
- package/test/fixdata.test.ts +74 -74
- package/test/utils.test.ts +29 -0
- package/test/utils_fixdata.test.ts +24 -0
- package/test/utils_removeColumns.test.ts +98 -0
- package/tsconfig.json +17 -17
- package/webpack.config.js +4 -1
- package/.travis.yml +0 -9
package/src/excellentexport.ts
CHANGED
|
@@ -1,335 +1,203 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ExcellentExport 3.7.
|
|
3
|
-
* A client side Javascript export to Excel.
|
|
4
|
-
*
|
|
5
|
-
* @author: Jordi Burgos (jordiburgos@gmail.com)
|
|
6
|
-
* @url: https://github.com/jmaister/excellentexport
|
|
7
|
-
*
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import * as XLSX from 'xlsx';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
]
|
|
205
|
-
*/
|
|
206
|
-
const convert = function(options:ConvertOptions, sheets:SheetOptions[]) {
|
|
207
|
-
const workbook = {
|
|
208
|
-
SheetNames: [],
|
|
209
|
-
Sheets: {}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
if (!options.format) {
|
|
213
|
-
throw new Error("'format' option must be defined");
|
|
214
|
-
}
|
|
215
|
-
if (options.format === 'csv' && sheets.length > 1) {
|
|
216
|
-
throw new Error("'csv' format only supports one sheet");
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
sheets.forEach(function(sheetConf:SheetOptions, index:number) {
|
|
220
|
-
const name = sheetConf.name;
|
|
221
|
-
if (!name) {
|
|
222
|
-
throw new Error('Sheet ' + index + ' must have the property "name".');
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Select data source
|
|
226
|
-
let dataArray;
|
|
227
|
-
if (sheetConf.from && sheetConf.from.table) {
|
|
228
|
-
dataArray = tableToArray(getTable(sheetConf.from.table));
|
|
229
|
-
} else if(sheetConf.from && sheetConf.from.array) {
|
|
230
|
-
dataArray = sheetConf.from.array
|
|
231
|
-
} else {
|
|
232
|
-
throw new Error('No data for sheet: [' + name + ']');
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Filter rows
|
|
236
|
-
if (sheetConf.filterRowFn) {
|
|
237
|
-
if (sheetConf.filterRowFn instanceof Function) {
|
|
238
|
-
dataArray = dataArray.filter(sheetConf.filterRowFn);
|
|
239
|
-
} else {
|
|
240
|
-
throw new Error('Parameter "filterRowFn" must be a function.');
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
// Filter columns
|
|
244
|
-
if (sheetConf.removeColumns) {
|
|
245
|
-
const toRemove = sheetConf.removeColumns.sort().reverse();
|
|
246
|
-
toRemove.forEach(function(columnIndex) {
|
|
247
|
-
removeColumn(dataArray, columnIndex);
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Convert data, by value
|
|
252
|
-
if (sheetConf.fixValue && typeof sheetConf.fixValue === 'function') {
|
|
253
|
-
const fn = sheetConf.fixValue;
|
|
254
|
-
dataArray.map((r, rownum) => {
|
|
255
|
-
r.map((value, colnum) => {
|
|
256
|
-
dataArray[rownum][colnum] = fn(value, rownum, colnum);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Convert data, whole array
|
|
262
|
-
if (sheetConf.fixArray && typeof sheetConf.fixArray === 'function') {
|
|
263
|
-
const fn = sheetConf.fixArray;
|
|
264
|
-
dataArray = fn(dataArray);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Create sheet
|
|
268
|
-
workbook.SheetNames.push(name);
|
|
269
|
-
const worksheet = XLSX.utils.aoa_to_sheet(dataArray, {sheet: name} as XLSX.AOA2SheetOpts);
|
|
270
|
-
workbook.Sheets[name] = worksheet;
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
const wbOut:string = XLSX.write(workbook, {bookType: options.format, bookSST:true, type: 'binary'});
|
|
274
|
-
try {
|
|
275
|
-
const blob = new Blob([s2ab(wbOut)], { type: "application/octet-stream" });
|
|
276
|
-
const filename = (options.filename || 'download') + '.' + options.format;
|
|
277
|
-
// Support for IE.
|
|
278
|
-
if (window.navigator.msSaveBlob) {
|
|
279
|
-
window.navigator.msSaveBlob(blob, filename);
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
if (options.anchor) {
|
|
283
|
-
const anchor = getAnchor(options.anchor);
|
|
284
|
-
anchor.href = window.URL.createObjectURL(blob);
|
|
285
|
-
anchor.download = filename;
|
|
286
|
-
} else if (options.openAsDownload) {
|
|
287
|
-
const a = document.createElement("a");
|
|
288
|
-
a.href = URL.createObjectURL(blob);
|
|
289
|
-
a.download = filename;
|
|
290
|
-
document.body.appendChild(a);
|
|
291
|
-
a.click();
|
|
292
|
-
document.body.removeChild(a);
|
|
293
|
-
} else {
|
|
294
|
-
throw new Error('Options should specify an anchor or openAsDownload=true.')
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
} catch(e) {
|
|
298
|
-
throw new Error('Error converting to '+ options.format + '. ' + e);
|
|
299
|
-
}
|
|
300
|
-
return wbOut;
|
|
301
|
-
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
return {
|
|
305
|
-
version: function(): string {
|
|
306
|
-
return version;
|
|
307
|
-
},
|
|
308
|
-
excel: function(anchor:(HTMLAnchorElement|string), table:HTMLTableElement, name:string) {
|
|
309
|
-
table = getTable(table);
|
|
310
|
-
anchor = getAnchor(anchor);
|
|
311
|
-
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');
|
|
314
|
-
},
|
|
315
|
-
csv: function(anchor:(HTMLAnchorElement|string), table:HTMLTableElement, delimiter?:string, newLine?:string) {
|
|
316
|
-
if (delimiter !== undefined && delimiter) {
|
|
317
|
-
csvDelimiter = delimiter;
|
|
318
|
-
}
|
|
319
|
-
if (newLine !== undefined && newLine) {
|
|
320
|
-
csvNewLine = newLine;
|
|
321
|
-
}
|
|
322
|
-
|
|
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');
|
|
328
|
-
},
|
|
329
|
-
convert: function(options:ConvertOptions, sheets:SheetOptions[]) {
|
|
330
|
-
return convert(options, sheets);
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
}();
|
|
334
|
-
|
|
335
|
-
export default ExcellentExport;
|
|
1
|
+
/**
|
|
2
|
+
* ExcellentExport 3.7.2
|
|
3
|
+
* A client side Javascript export to Excel.
|
|
4
|
+
*
|
|
5
|
+
* @author: Jordi Burgos (jordiburgos@gmail.com)
|
|
6
|
+
* @url: https://github.com/jmaister/excellentexport
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as XLSX from 'xlsx';
|
|
11
|
+
|
|
12
|
+
import * as utils from './utils';
|
|
13
|
+
|
|
14
|
+
export interface ConvertOptions {
|
|
15
|
+
anchor?: (string|HTMLAnchorElement),
|
|
16
|
+
openAsDownload?: boolean,
|
|
17
|
+
format: ('csv' | 'xls' | 'xlsx'),
|
|
18
|
+
filename?: string,
|
|
19
|
+
rtl?: boolean,
|
|
20
|
+
}
|
|
21
|
+
export interface FromOptions {
|
|
22
|
+
table?: (string|HTMLTableElement),
|
|
23
|
+
array?: any[][],
|
|
24
|
+
}
|
|
25
|
+
export interface SheetOptions {
|
|
26
|
+
name: string,
|
|
27
|
+
from: FromOptions,
|
|
28
|
+
removeColumns?: number[],
|
|
29
|
+
filterRowFn?(row:any[]): boolean ,
|
|
30
|
+
fixValue?(value:any, row:number, column:number): any,
|
|
31
|
+
fixArray?(array:any[][]): any[][],
|
|
32
|
+
rtl?: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
const ExcellentExport = function() {
|
|
37
|
+
|
|
38
|
+
const version = "3.8.0";
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
ExcellentExport.convert(options, sheets);
|
|
42
|
+
|
|
43
|
+
Options:
|
|
44
|
+
{
|
|
45
|
+
anchor: String or HTML Element,
|
|
46
|
+
openAsDownload: boolean, // Use this options if not using an anchor tag
|
|
47
|
+
format: 'xlsx' or 'xls' or 'csv',
|
|
48
|
+
filename: String,
|
|
49
|
+
rtl: boolean (optional), specify if all the workbook has text in RTL mode
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
Sheets must be an array of sheet configuration objects. Sheet description:
|
|
53
|
+
[
|
|
54
|
+
{
|
|
55
|
+
name: 'Sheet 1', // Sheet name
|
|
56
|
+
from: {
|
|
57
|
+
table: String/Element, // Table ID or table element
|
|
58
|
+
array: [...] // Array with the data. Array where each element is a row. Every row is an array of the cells.
|
|
59
|
+
},
|
|
60
|
+
removeColumns: [...], // Array of column indexes (from 0)
|
|
61
|
+
filterRowFn: function(row) {return true}, // Function to decide which rows are returned
|
|
62
|
+
fixValue: function(value, row, column) {return fixedValue} // Function to fix values, receiving value, row num, column num
|
|
63
|
+
fixArray: function(array) {return array} // Function to manipulate the whole data array
|
|
64
|
+
rtl: boolean // optional: specify if the sheet has text in RTL mode
|
|
65
|
+
...
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
...
|
|
69
|
+
}, ...
|
|
70
|
+
]
|
|
71
|
+
*/
|
|
72
|
+
const convert = function(options:ConvertOptions, sheets:SheetOptions[]) {
|
|
73
|
+
const workbook = {
|
|
74
|
+
SheetNames: [],
|
|
75
|
+
Sheets: {},
|
|
76
|
+
Views: []
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
if (!options.format) {
|
|
80
|
+
throw new Error("'format' option must be defined");
|
|
81
|
+
}
|
|
82
|
+
if (options.format === 'csv' && sheets.length > 1) {
|
|
83
|
+
throw new Error("'csv' format only supports one sheet");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
sheets.forEach(function(sheetConf:SheetOptions, index:number) {
|
|
87
|
+
const name = sheetConf.name;
|
|
88
|
+
if (!name) {
|
|
89
|
+
throw new Error('Sheet ' + index + ' must have the property "name".');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Select data source
|
|
93
|
+
let dataArray: any[][];
|
|
94
|
+
if (sheetConf.from && sheetConf.from.table) {
|
|
95
|
+
dataArray = utils.tableToArray(utils.getTable(sheetConf.from.table));
|
|
96
|
+
} else if(sheetConf.from && sheetConf.from.array) {
|
|
97
|
+
dataArray = sheetConf.from.array
|
|
98
|
+
} else {
|
|
99
|
+
throw new Error('No data for sheet: [' + name + ']');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Filter rows
|
|
103
|
+
if (sheetConf.filterRowFn) {
|
|
104
|
+
if (sheetConf.filterRowFn instanceof Function) {
|
|
105
|
+
dataArray = dataArray.filter(sheetConf.filterRowFn);
|
|
106
|
+
} else {
|
|
107
|
+
throw new Error('Parameter "filterRowFn" must be a function.');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Filter columns
|
|
111
|
+
if (sheetConf.removeColumns) {
|
|
112
|
+
utils.removeColumns(dataArray, sheetConf.removeColumns);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Convert data. Function applied to each value independently, receiving (value, rownum, colnum)
|
|
116
|
+
if (sheetConf.fixValue && typeof sheetConf.fixValue === 'function') {
|
|
117
|
+
const fn = sheetConf.fixValue;
|
|
118
|
+
dataArray.map((r, rownum) => {
|
|
119
|
+
r.map((value, colnum) => {
|
|
120
|
+
dataArray[rownum][colnum] = fn(value, rownum, colnum);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Convert data, whole array
|
|
126
|
+
if (sheetConf.fixArray && typeof sheetConf.fixArray === 'function') {
|
|
127
|
+
const fn = sheetConf.fixArray;
|
|
128
|
+
dataArray = fn(dataArray);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Create sheet
|
|
132
|
+
workbook.SheetNames.push(name);
|
|
133
|
+
const worksheet = XLSX.utils.aoa_to_sheet(dataArray, {sheet: name} as XLSX.AOA2SheetOpts);
|
|
134
|
+
workbook.Sheets[name] = worksheet;
|
|
135
|
+
workbook.Views.push({RTL: options.rtl || sheetConf.rtl || false});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const wbOut:string = XLSX.write(workbook, {bookType: options.format, bookSST:true, type: 'binary'});
|
|
139
|
+
try {
|
|
140
|
+
const blob = new Blob([utils.string2ArrayBuffer(wbOut)], { type: "application/octet-stream" });
|
|
141
|
+
const filename = (options.filename || 'download') + '.' + options.format;
|
|
142
|
+
// Support for IE.
|
|
143
|
+
if (window.navigator.msSaveBlob) {
|
|
144
|
+
window.navigator.msSaveBlob(blob, filename);
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
if (options.anchor) {
|
|
148
|
+
const anchor = utils.getAnchor(options.anchor);
|
|
149
|
+
anchor.href = window.URL.createObjectURL(blob);
|
|
150
|
+
anchor.download = filename;
|
|
151
|
+
} else if (options.openAsDownload) {
|
|
152
|
+
const a = document.createElement("a");
|
|
153
|
+
a.href = URL.createObjectURL(blob);
|
|
154
|
+
a.download = filename;
|
|
155
|
+
document.body.appendChild(a);
|
|
156
|
+
a.click();
|
|
157
|
+
document.body.removeChild(a);
|
|
158
|
+
} else {
|
|
159
|
+
throw new Error('Options should specify an anchor or openAsDownload=true.')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
} catch(e) {
|
|
163
|
+
throw new Error('Error converting to '+ options.format + '. ' + e);
|
|
164
|
+
}
|
|
165
|
+
return wbOut;
|
|
166
|
+
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
version: function(): string {
|
|
171
|
+
return version;
|
|
172
|
+
},
|
|
173
|
+
excel: function(anchor:(HTMLAnchorElement|string), table:HTMLTableElement, name:string) {
|
|
174
|
+
table = utils.getTable(table);
|
|
175
|
+
anchor = utils.getAnchor(anchor);
|
|
176
|
+
const ctx = {worksheet: name || 'Worksheet', table: table.innerHTML};
|
|
177
|
+
const b64 = utils.base64(utils.format(utils.templates.excel, ctx));
|
|
178
|
+
return utils.createDownloadLink(anchor, b64, 'application/vnd.ms-excel','export.xls');
|
|
179
|
+
},
|
|
180
|
+
csv: function(anchor:(HTMLAnchorElement|string), table:HTMLTableElement, delimiter?:string, newLine?:string) {
|
|
181
|
+
let csvDelimiter = ",";
|
|
182
|
+
let csvNewLine = "\r\n";
|
|
183
|
+
|
|
184
|
+
if (delimiter !== undefined && delimiter) {
|
|
185
|
+
csvDelimiter = delimiter;
|
|
186
|
+
}
|
|
187
|
+
if (newLine !== undefined && newLine) {
|
|
188
|
+
csvNewLine = newLine;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
table = utils.getTable(table);
|
|
192
|
+
anchor = utils.getAnchor(anchor);
|
|
193
|
+
const csvData = "\uFEFF" + utils.tableToCSV(table, csvDelimiter, csvNewLine);
|
|
194
|
+
const b64 = utils.base64(csvData);
|
|
195
|
+
return utils.createDownloadLink(anchor, b64, 'application/csv', 'export.csv');
|
|
196
|
+
},
|
|
197
|
+
convert: function(options:ConvertOptions, sheets:SheetOptions[]) {
|
|
198
|
+
return convert(options, sheets);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}();
|
|
202
|
+
|
|
203
|
+
export default ExcellentExport;
|