-
Notifications
You must be signed in to change notification settings - Fork 83
/
Copy pathxlsx.js
109 lines (101 loc) · 3.16 KB
/
xlsx.js
1
2
3
4
5
6
7
8
9
10
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
export class Workbook {
constructor(workbook) {
Object.defineProperties(this, {
_: {value: workbook},
sheetNames: {
value: workbook.worksheets.map((s) => s.name),
enumerable: true
}
});
}
sheet(name, options) {
const sname =
typeof name === "number"
? this.sheetNames[name]
: this.sheetNames.includes((name += ""))
? name
: null;
if (sname == null) throw new Error(`Sheet not found: ${name}`);
const sheet = this._.getWorksheet(sname);
return extract(sheet, options);
}
}
function extract(sheet, {range, headers} = {}) {
let [[c0, r0], [c1, r1]] = parseRange(range, sheet);
const headerRow = headers ? sheet._rows[r0++] : null;
let names = new Set(["#"]);
for (let n = c0; n <= c1; n++) {
const value = headerRow ? valueOf(headerRow.findCell(n + 1)) : null;
let name = (value && value + "") || toColumn(n);
while (names.has(name)) name += "_";
names.add(name);
}
names = new Array(c0).concat(Array.from(names));
const output = new Array(r1 - r0 + 1);
for (let r = r0; r <= r1; r++) {
const row = (output[r - r0] = Object.create(null, {"#": {value: r + 1}}));
const _row = sheet.getRow(r + 1);
if (_row.hasValues)
for (let c = c0; c <= c1; c++) {
const value = valueOf(_row.findCell(c + 1));
if (value != null) row[names[c + 1]] = value;
}
}
output.columns = names.filter(() => true); // Filter sparse columns
return output;
}
function valueOf(cell) {
if (!cell) return;
const {value} = cell;
if (value && typeof value === "object" && !(value instanceof Date)) {
if (value.formula || value.sharedFormula) {
return value.result && value.result.error ? NaN : value.result;
}
if (value.richText) {
return richText(value);
}
if (value.text) {
let {text} = value;
if (text.richText) text = richText(text);
return value.hyperlink && value.hyperlink !== text
? `${value.hyperlink} ${text}`
: text;
}
return value;
}
return value;
}
function richText(value) {
return value.richText.map((d) => d.text).join("");
}
function parseRange(specifier = ":", {columnCount, rowCount}) {
specifier += "";
if (!specifier.match(/^[A-Z]*\d*:[A-Z]*\d*$/))
throw new Error("Malformed range specifier");
const [[c0 = 0, r0 = 0], [c1 = columnCount - 1, r1 = rowCount - 1]] =
specifier.split(":").map(fromCellReference);
return [
[c0, r0],
[c1, r1]
];
}
// Returns the default column name for a zero-based column index.
// For example: 0 -> "A", 1 -> "B", 25 -> "Z", 26 -> "AA", 27 -> "AB".
function toColumn(c) {
let sc = "";
c++;
do {
sc = String.fromCharCode(64 + (c % 26 || 26)) + sc;
} while ((c = Math.floor((c - 1) / 26)));
return sc;
}
// Returns the zero-based indexes from a cell reference.
// For example: "A1" -> [0, 0], "B2" -> [1, 1], "AA10" -> [26, 9].
function fromCellReference(s) {
const [, sc, sr] = s.match(/^([A-Z]*)(\d*)$/);
let c = 0;
if (sc)
for (let i = 0; i < sc.length; i++)
c += Math.pow(26, sc.length - i - 1) * (sc.charCodeAt(i) - 64);
return [c ? c - 1 : undefined, sr ? +sr - 1 : undefined];
}