README cleanup in anticipation of node fetch

This commit is contained in:
SheetJS 2022-02-05 08:59:25 -05:00
parent 3cbe83c855
commit 6c436ae277
23 changed files with 5815 additions and 2031 deletions

@ -148,6 +148,7 @@ storages
- demos/extendscript/README.md
Photoshop
InDesign
minifier
- demos/function/README.md

970
README.md

File diff suppressed because it is too large Load Diff

@ -1,21 +1,21 @@
var CT_VBA = "application/vnd.ms-office.vbaProject";
function make_vba_xls(cfb/*:CFBContainer*/) {
var newcfb = CFB.utils.cfb_new({root:"R"});
cfb.FullPaths.forEach(function(p, i) {
if(p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/)) return;
var newpath = p.replace(/^[^\/]*/,"R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
});
return CFB.write(newcfb);
function make_vba_xls(cfb) {
var newcfb = CFB.utils.cfb_new({ root: "R" });
cfb.FullPaths.forEach(function(p, i) {
if (p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/))
return;
var newpath = p.replace(/^[^\/]*/, "R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
});
return CFB.write(newcfb);
}
function fill_vba_xls(cfb/*:CFBContainer*/, vba/*:CFBContainer*/)/*:void*/ {
vba.FullPaths.forEach(function(p, i) {
if(i == 0) return;
var newpath = p.replace(/[^\/]*[\/]/, "/_VBA_PROJECT_CUR/");
if(newpath.slice(-1) !== "/") CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
});
function fill_vba_xls(cfb, vba) {
vba.FullPaths.forEach(function(p, i) {
if (i == 0)
return;
var newpath = p.replace(/[^\/]*[\/]/, "/_VBA_PROJECT_CUR/");
if (newpath.slice(-1) !== "/")
CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
});
}
var VBAFMTS = [ "xlsb", "xlsm", "xlam", "biff8", "xla" ];
var VBAFMTS = ["xlsb", "xlsm", "xlam", "biff8", "xla"];

@ -869,7 +869,8 @@ function stringify_formula(formula/*Array<any>*/, range, cell/*:any*/, supbooks,
nameidx = (f[1][2]/*:any*/);
var lbl = (supbooks.names||[])[nameidx-1] || (supbooks[0]||[])[nameidx];
var name = lbl ? lbl.Name : "SH33TJSNAME" + String(nameidx);
if(name in XLSXFutureFunctions) name = XLSXFutureFunctions[name];
/* [MS-XLSB] 2.5.97.10 Ftab -- last verified 20220204 */
if(name && name.slice(0,6) == "_xlfn.") name = name.slice(6);
stack.push(name);
break;

File diff suppressed because it is too large Load Diff

0
bits/83_numbers.js Executable file → Normal file

@ -7,7 +7,7 @@ document.addEventListener('mousedown', function(mouse) {
});
chrome.runtime.onMessage.addListener(function(msg, sender, cb) {
if(!msg && !msg['Sheet']) return;
if(!msg || !msg['Sheet']) return;
if(msg.Sheet == "JS") {
var elt = document.elementFromPoint(coords[0], coords[1]);
while(elt != null) {

@ -1,10 +1,33 @@
# Internet Explorer
Despite the efforts to deprecate the pertinent operating systems, IE is still
very popular, required for various government and corporate websites throughout
the world. The modern upload and download strategies are not available in older
versions of IE, but there are alternative approaches.
The modern upload and download strategies are not available in older versions of
IE, but there are approaches using older technologies like ActiveX and Flash.
<details>
<summary><b>Live Demos</b> (click to show)</summary>
<http://oss.sheetjs.com/sheetjs/ajax.html> uses XMLHttpRequest to download test
files and convert to CSV.
<https://oss.sheetjs.com/sheetjs/> demonstrates reading files with `FileReader`.
Older versions of IE do not support HTML5 File API but do support Base64.
On OSX you can get the Base64 encoding with:
```bash
$ <target_file base64 | pbcopy
```
On Windows XP and up you can get the Base64 encoding using `certutil`:
```cmd
> certutil -encode target_file target_file.b64
```
(note: You have to open the file and remove the header and footer lines)
</details>
## Upload Strategies
@ -14,14 +37,14 @@ IE10 and IE11 support the standard HTML5 FileReader API:
```js
function handle_fr(e) {
var files = e.target.files, f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var data = new Uint8Array(e.target.result);
var wb = XLSX.read(data, {type: 'array'});
process_wb(wb);
};
reader.readAsArrayBuffer(f);
var files = e.target.files, f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var data = new Uint8Array(e.target.result);
var wb = XLSX.read(data, {type: 'array'});
process_wb(wb);
};
reader.readAsArrayBuffer(f);
}
input_dom_element.addEventListener('change', handle_fr, false);
```
@ -36,12 +59,12 @@ should be called from a file input `onchange` event:
```js
var input_dom_element = document.getElementById("file");
function handle_ie() {
/* get data from selected file */
var path = input_dom_element.value;
var bstr = IE_LoadFile(path);
/* read workbook */
var wb = XLSX.read(bstr, {type: 'binary'});
/* DO SOMETHING WITH workbook HERE */
/* get data from selected file */
var path = input_dom_element.value;
var bstr = IE_LoadFile(path);
/* read workbook */
var wb = XLSX.read(bstr, {type: 'binary'});
/* DO SOMETHING WITH workbook HERE */
}
input_dom_element.attachEvent('onchange', handle_ie);
```
@ -105,10 +128,10 @@ to a Base64 string and passing to the library:
```js
Downloadify.create(element_id, {
/* the demo includes the other options required by Downloadify */
filename: "test.xlsx",
data: function() { return XLSX.write(wb, {bookType:"xlsx", type:'base64'}); },
dataType: 'base64'
/* the demo includes the other options required by Downloadify */
filename: "test.xlsx",
data: function() { return XLSX.write(wb, {bookType:"xlsx", type:'base64'}); },
dataType: 'base64'
});
```

@ -13,7 +13,7 @@ The library can also be imported directly from JSX code with:
import XLSX from 'xlsx';
```
This demo shows a simple React component transpiled in the browser using the babel
This demo shows a simple React component transpiled in the browser using Babel
standalone library. Since there is no standard React table model, this demo
settles on the array of arrays approach.
@ -77,45 +77,29 @@ function make_cols(refstr/*:string*/) {
Reproducing the full project is straightforward:
```bash
# see native.sh
react-native init SheetJS
cd SheetJS
npm i -S xlsx react react-native react-native-table-component react-native-fs
cp ../react-native.js index.js
react-native link
$ make native # build the project
$ make ios # build and run the iOS demo
$ make android # build and run the android demo
```
`react-native-table-component` draws the data table. `react-native-fs` reads
and write files on devices. The app will prompt before reading and after
writing data. The printed location will be:
The app will prompt before reading and after writing data. The printed location
depends on the environment:
- android: path in the device filesystem
- iOS simulator: local path to file
- iOS device: a path accessible from iTunes App Documents view
`react-native-fs` supports `"ascii"` encoding for `readFile` and `writeFile`.
In practice, that encoding uses binary strings compatible with `"binary"` type:
Components used in the demo:
- [`react-native-table-component`](https://npm.im/react-native-table-component)
- [`react-native-file-access`](https://npm.im/react-native-file-access)
```js
import { writeFile, readFile } from 'react-native-fs';
/* read a workbook */
readFile(file, 'ascii').then((res) => {
const workbook = XLSX.read(res, {type:'binary'});
/* DO SOMETHING WITH workbook HERE */
});
/* write a workbook */
const wbout = XLSX.write(wb, {type:'binary', bookType:"xlsx"});
writeFile(file, wbout, 'ascii').then((r)=>{/* :) */}).catch((e)=>{/* :( */});
```
React Native does not provide a native component for reading and writing files.
The sample script <`react-native.js`> uses `react-native-file-access` and has
notes for integrations with `react-native-fetch-blob` and `react-native-fs`.
Note: for real app deployments, the `UIFileSharingEnabled` flag must be manually
set in the iOS project `Info.plist` file.
To run the React Native demo, run either `make ios` or `make android` while
connected to a device or emulator.
#### Server-Rendered React Components with Next.js
The demo uses the same component code as the in-browser version, but the build

@ -1,11 +1,17 @@
#!/bin/bash
# xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
if [ ! -e SheetJS ]; then
react-native init --version="0.62.2" SheetJS
cd SheetJS
npm i -S xlsx react-native-table-component react-native-fs
cd -
fi
# Create starter project
if [ ! -e SheetJS ]; then react-native init SheetJS --version="0.67.2"; fi
# Install dependencies
cd SheetJS; npm i -S xlsx react-native-table-component; cd -
cd SheetJS; npm i -S react-native-file-access@2.x; cd -
# cd SheetJS; npm i -S react-native-fs; cd -
# cd SheetJS; npm i -S react-native-fetch-blob; cd -
# Copy demo assets
if [ ! -e SheetJS/logo.png ]; then
curl -O http://oss.sheetjs.com/assets/img/logo.png
mv logo.png SheetJS/logo.png
@ -16,6 +22,6 @@ if [ -e SheetJS/index.ios.js ]; then
else
cp react-native.js SheetJS/index.js
fi
cd SheetJS;
RNFB_ANDROID_PERMISSIONS=true react-native link
cd -;
# Link
cd SheetJS; RNFB_ANDROID_PERMISSIONS=true react-native link; cd -

@ -14,12 +14,69 @@ import {
} from 'react-native';
import { Table, Row, Rows, TableWrapper } from 'react-native-table-component';
// react-native-file-access
var Base64 = function() {
var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
return {
encode: function(input) {
var o = "";
var c1 = 0, c2 = 0, c3 = 0, e1 = 0, e2 = 0, e3 = 0, e4 = 0;
for (var i = 0; i < input.length; ) {
c1 = input.charCodeAt(i++);
e1 = c1 >> 2;
c2 = input.charCodeAt(i++);
e2 = (c1 & 3) << 4 | c2 >> 4;
c3 = input.charCodeAt(i++);
e3 = (c2 & 15) << 2 | c3 >> 6;
e4 = c3 & 63;
if (isNaN(c2)) {
e3 = e4 = 64;
} else if (isNaN(c3)) {
e4 = 64;
}
o += map.charAt(e1) + map.charAt(e2) + map.charAt(e3) + map.charAt(e4);
}
return o;
},
decode: function(input) {
var o = "";
var c1 = 0, c2 = 0, c3 = 0, e1 = 0, e2 = 0, e3 = 0, e4 = 0;
input = input.replace(/[^\w\+\/\=]/g, "");
for (var i = 0; i < input.length; ) {
e1 = map.indexOf(input.charAt(i++));
e2 = map.indexOf(input.charAt(i++));
c1 = e1 << 2 | e2 >> 4;
o += String.fromCharCode(c1);
e3 = map.indexOf(input.charAt(i++));
c2 = (e2 & 15) << 4 | e3 >> 2;
if (e3 !== 64) {
o += String.fromCharCode(c2);
}
e4 = map.indexOf(input.charAt(i++));
c3 = (e3 & 3) << 6 | e4;
if (e4 !== 64) {
o += String.fromCharCode(c3);
}
}
return o;
}
};
}();
import { Dirs, FileSystem } from 'react-native-file-access';
const DDP = Dirs.DocumentDir + "/";
const readFile = (path, enc) => FileSystem.readFile(path, "base64");
const writeFile = (path, data, enc) => FileSystem.writeFile(path, data, "base64");
const input = res => Base64.decode(res);
const output = str => Base64.encode(str);
// react-native-fs
/*
import { writeFile, readFile, DocumentDirectoryPath } from 'react-native-fs';
const DDP = DocumentDirectoryPath + "/";
const input = res => res;
const output = str => str;
*/
// react-native-fetch-blob
/*
import RNFetchBlob from 'react-native-fetch-blob';
@ -36,7 +93,7 @@ export default class SheetJS extends Component {
constructor(props) {
super(props);
this.state = {
data: [[1,2,3],[4,5,6]],
data: [[2,3,4],[3,4,5]],
widthArr: [60, 60, 60],
cols: make_cols("A1:C2")
};

@ -2,7 +2,8 @@
### Installation
In the browser, just add a script tag:
The complete browser standalone build is saved to `dist/xlsx.full.min.js` and
can be directly added to a page with a `script` tag:
```html
<script lang="javascript" src="dist/xlsx.full.min.js"></script>
@ -18,7 +19,7 @@ In the browser, just add a script tag:
| `CDNjs` | <https://cdnjs.com/libraries/xlsx> |
| `packd` | <https://bundle.run/xlsx@latest?name=XLSX> |
`unpkg` makes the latest version available at:
For example, `unpkg` makes the latest version available at:
```html
<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
@ -26,33 +27,8 @@ In the browser, just add a script tag:
</details>
With [npm](https://www.npmjs.org/package/xlsx):
```bash
$ npm install xlsx
```
With [bower](https://bower.io/search/?q=js-xlsx):
```bash
$ bower install js-xlsx
```
<details>
<summary><b>Optional features</b> (click to show)</summary>
The node version automatically requires modules for additional features. Some
of these modules are rather large in size and are only needed in special
circumstances, so they do not ship with the core. For browser use, they must
be included directly:
```html
<!-- international support from js-codepage -->
<script src="dist/cpexcel.js"></script>
```
An appropriate version for each dependency is included in the dist/ directory.
<summary><b>Browser builds</b> (click to show)</summary>
The complete single-file version is generated at `dist/xlsx.full.min.js`
@ -74,8 +50,30 @@ be configured to remove support with `resolve.alias`:
</details>
With [npm](https://www.npmjs.org/package/xlsx):
```bash
$ npm install xlsx
```
With [bower](https://bower.io/search/?q=js-xlsx):
```bash
$ bower install js-xlsx
```
`dist/xlsx.extendscript.js` is an ExtendScript build for Photoshop and InDesign
that is included in the `npm` package. It can be directly referenced with a
`#include` directive:
```extendscript
#include "xlsx.extendscript.js"
```
<details>
<summary><b>ECMAScript 3 Compatibility</b> (click to show)</summary>
<summary><b>Internet Explorer and ECMAScript 3 Compatibility</b> (click to show)</summary>
For broad compatibility with JavaScript engines, the library is written using
ECMAScript 3 language dialect as well as some ES5 features like `Array#forEach`.

@ -31,8 +31,8 @@ var table_elt = document.getElementById("my-table-id");
var workbook = XLSX.utils.table_to_book(table_elt);
// Process Data (add a new row)
var worksheet = workbook.Sheets["Sheet1"];
XLSX.utils.sheet_add_aoa([["Created "+new Date().toISOString()}]], {origin:-1});
var ws = workbook.Sheets["Sheet1"];
XLSX.utils.sheet_add_aoa(ws, [["Created "+new Date().toISOString()]], {origin:-1});
// Package and Release Data (`writeFile` tries to write and save an XLSB file)
XLSX.writeFile(workbook, "Report.xlsb");
@ -40,7 +40,8 @@ XLSX.writeFile(workbook, "Report.xlsb");
This library tries to simplify steps 2 and 4 with functions to extract useful
data from spreadsheet files (`read` / `readFile`) and generate new spreadsheet
files from data (`write` / `writeFile`).
files from data (`write` / `writeFile`). Additional utility functions like
`table_to_book` work with other common data sources like HTML tables.
This documentation and various demo projects cover a number of common scenarios
and approaches for steps 1 and 5.
@ -48,8 +49,210 @@ and approaches for steps 1 and 5.
Utility functions help with step 3.
#### The Zen of SheetJS
### The Zen of SheetJS
_Data processing should fit in any workflow_
The library does not impose a separate lifecycle. It fits nicely in websites
and apps built using any framework. The plain JS data objects play nice with
Web Workers and future APIs.
["Acquiring and Extracting Data"](#acquiring-and-extracting-data) describes
solutions for common data import scenarios.
["Writing Workbooks"](#writing-workbooks) describes solutions for common data
export scenarios involving actual spreadsheet files.
["Utility Functions"](#utility-functions) details utility functions for
translating JSON Arrays and other common JS structures into worksheet objects.
_JavaScript is a powerful language for data processing_
The ["Common Spreadsheet Format"](#common-spreadsheet-format) is a simple object
representation of the core concepts of a workbook. The various functions in the
library provide low-level tools for working with the object.
For friendly JS processing, there are utility functions for converting parts of
a worksheet to/from an Array of Arrays. The following example combines powerful
JS Array methods with a network request library to download data, select the
information we want and create a workbook file:
<details>
<summary><b>Get Data from a JSON Endpoint and Generate a Workbook</b> (click to show)</summary>
The goal is to generate a XLSB workbook of US President names and birthdays.
**Acquire Data**
_Raw Data_
<https://theunitedstates.io/congress-legislators/executive.json> has the desired
data. For example, John Adams:
```js
{
"id": { /* (data omitted) */ },
"name": {
"first": "John", // <-- first name
"last": "Adams" // <-- last name
},
"bio": {
"birthday": "1735-10-19", // <-- birthday
"gender": "M"
},
"terms": [
{ "type": "viceprez", /* (other fields omitted) */ },
{ "type": "viceprez", /* (other fields omitted) */ },
{ "type": "prez", /* (other fields omitted) */ } // <-- look for "prez"
]
}
```
_Filtering for Presidents_
The dataset includes Aaron Burr, a Vice President who was never President!
`Array#filter` creates a new array with the desired rows. A President served
at least one term with `type` set to `"prez"`. To test if a particular row has
at least one `"prez"` term, `Array#some` is another native JS function. The
complete filter would be:
```js
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
```
_Lining up the data_
For this example, the name will be the first name combined with the last name
(`row.name.first + " " + row.name.last`) and the birthday will be the subfield
`row.bio.birthday`. Using `Array#map`, the dataset can be massaged in one call:
```js
const rows = prez.map(row => ({
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}));
```
The result is an array of "simple" objects with no nesting:
```js
[
{ name: "George Washington", birthday: "1732-02-22" },
{ name: "John Adams", birthday: "1735-10-19" },
// ... one row per President
]
```
**Extract Data**
With the cleaned dataset, `XLSX.utils.json_to_sheet` generates a worksheet:
```js
const worksheet = XLSX.utils.json_to_sheet(rows);
```
`XLSX.utils.book_new` creates a new workbook and `XLSX.utils.book_append_sheet`
appends a worksheet to the workbook. The new worksheet will be called "Dates":
```js
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
```
**Process Data**
_Fixing headers_
By default, `json_to_sheet` creates a worksheet with a header row. In this case,
the headers come from the JS object keys: "name" and "birthday".
The headers are in cells A1 and B1. `XLSX.utils.sheet_add_aoa` can write text
values to the existing worksheet starting at cell A1:
```js
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
```
_Fixing Column Widths_
Some of the names are longer than the default column width. Column widths are
set by [setting the `"!cols"` worksheet property](#row-and-column-properties).
The following line sets the width of column A to approximately 10 characters:
```js
worksheet["!cols"] = [ { wch: 10 } ]; // set column A width to 10 characters
```
One `Array#reduce` call over `rows` can calculate the maximum width:
```js
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];
```
Note: If the starting point was a file or HTML table, `XLSX.utils.sheet_to_json`
will generate an array of JS objects.
**Package and Release Data**
`XLSX.writeFile` creates a spreadsheet file and tries to write it to the system.
In the browser, it will try to prompt the user to download the file. In NodeJS,
it will write to the local directory.
```js
XLSX.writeFile(workbook, "Presidents.xlsx");
```
**Complete Example**
```js
// Uncomment the next line for use in NodeJS:
// const XLSX = require("xlsx"), axios = require("axios");
(async() => {
/* fetch JSON data and parse */
const url = "https://theunitedstates.io/congress-legislators/executive.json";
const raw_data = (await axios(url, {responseType: "json"})).data;
/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));
/* flatten objects */
const rows = prez.map(row => ({
name: row.name.first + " " + row.name.last,
birthday: row.bio.birthday
}));
/* generate worksheet and workbook */
const worksheet = XLSX.utils.json_to_sheet(rows);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");
/* fix headers */
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });
/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];
/* create an XLSX file and try to save to Presidents.xlsx */
XLSX.writeFile(workbook, "Presidents.xlsx");
})();
```
For use in the web browser, assuming the snippet is saved to `snippet.js`,
script tags should be used to include the `axios` and `xlsx` standalone builds:
```html
<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="snippet.js"></script>
```
</details>
_File formats are implementation details_
@ -59,36 +262,7 @@ The parser covers a wide gamut of common spreadsheet file formats to ensure that
The writer supports a number of common output formats for broad compatibility
with the data ecosystem.
_Data processing should fit in any workflow_
The library does not impose a separate lifecycle. It fits nicely in websites
and apps built using any framework. The plain JS data objects play nice with
Web Workers and future APIs.
["Parsing Workbooks"](#parsing-workbooks) describes solutions for common data
import scenarios involving actual spreadsheet files.
["Writing Workbooks"](#writing-workbooks) describes solutions for common data
export scenarios involving actual spreadsheet files.
["Utility Functions"](#utility-functions) details utility functions for
translating JSON Arrays and other common JS structures into worksheet objects.
_JavaScript is a powerful language for data processing_
The ["Common Spreadsheet Format"](#common-spreadsheet-format) is a simple object
representation of the core concepts of a workbook. The various functions in the
library provide low-level tools for working with the object.
For friendly JS processing, there are utility functions for converting parts of
a worksheet to/from an Array of Arrays. For example, summing columns from an
array of arrays can be implemented in a single Array reduce operation:
```js
var aoa = XLSX.utils.sheet_to_json(worksheet, {header: 1});
var sum_of_column_B = aoa.reduce((acc, row) => acc + (+row[1]||0), 0);
```
To the greatest extent possible, data processing code should not have to worry
about the specific file formats involved.

@ -1,82 +1,172 @@
## Parsing Workbooks
## Acquiring and Extracting Data
For parsing, the first step is to read the file. This involves acquiring the
data and feeding it into the library. Here are a few common scenarios:
### Parsing Workbooks
<details>
<summary><b>nodejs read a file</b> (click to show)</summary>
#### API
`readFile` is only available in server environments. Browsers have no API for
reading arbitrary files given a path, so another strategy must be used.
_Extract data from spreadsheet bytes_
```js
if(typeof require !== 'undefined') XLSX = require('xlsx');
var workbook = XLSX.readFile('test.xlsx');
/* DO SOMETHING WITH workbook HERE */
var workbook = XLSX.read(data, opts);
```
The `read` method can extract data from spreadsheet bytes stored in a JS string,
"binary string", NodeJS buffer or typed array (`Uint8Array` or `ArrayBuffer`).
_Read spreadsheet bytes from a local file and extract data_
```js
var workbook = XLSX.readFile(filename, opts);
```
The `readFile` method attempts to read a spreadsheet file at the supplied path.
Browsers generally do not allow reading files in this way (it is deemed a
security risk), and attempts to read files in this way will throw an error.
The second `opts` argument is optional. ["Parsing Options"](#parsing-options)
covers the supported properties and behaviors.
#### Examples
Here are a few common scenarios (click on each subtitle to see the code):
<details>
<summary><b>Local file in a NodeJS server</b> (click to show)</summary>
`readFile` uses `fs.readFileSync` under the hood:
```js
var XLSX = require("xlsx");
var workbook = XLSX.readFile("test.xlsx");
```
For Node ESM, the `readFile` helper is not enabled. Instead, `fs.readFileSync`
should be used to read the file data as a `Buffer` for use with `XLSX.read`:
```js
import { readFileSync } from "fs";
import { read } from "xlsx/xlsx.mjs";
const buf = readFileSync("test.xlsx");
/* buf is a Buffer */
const workbook = read(buf);
```
</details>
<details>
<summary><b>Photoshop ExtendScript read a file</b> (click to show)</summary>
<summary><b>User-submitted file in a web page ("Drag-and-Drop")</b> (click to show)</summary>
`readFile` wraps the `File` logic in Photoshop and other ExtendScript targets.
The specified path should be an absolute path:
For modern websites targeting Chrome 76+, `File#arrayBuffer` is recommended:
```js
#include "xlsx.extendscript.js"
/* Read test.xlsx from the Documents folder */
var workbook = XLSX.readFile(Folder.myDocuments + '/' + 'test.xlsx');
/* DO SOMETHING WITH workbook HERE */
// XLSX is a global from the standalone script
async function handleDropAsync(e) {
e.stopPropagation(); e.preventDefault();
const f = e.dataTransfer.files[0];
/* f is a File */
const data = await f.arrayBuffer();
/* data is an ArrayBuffer */
const workbook = XLSX.read(data);
/* DO SOMETHING WITH workbook HERE */
}
drop_dom_element.addEventListener("drop", handleDropAsync, false);
```
The [`extendscript` demo](demos/extendscript/) includes a more complex example.
For maximal compatibility, the `FileReader` API should be used:
```js
function handleDrop(e) {
e.stopPropagation(); e.preventDefault();
var f = e.dataTransfer.files[0];
/* f is a File */
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
/* reader.readAsArrayBuffer(file) -> data will be an ArrayBuffer */
var workbook = XLSX.read(data);
/* DO SOMETHING WITH workbook HERE */
};
reader.readAsArrayBuffer(f);
}
drop_dom_element.addEventListener("drop", handleDrop, false);
```
<https://oss.sheetjs.com/sheetjs/> demonstrates the FileReader technique.
</details>
<details>
<summary><b>Browser read TABLE element from page</b> (click to show)</summary>
<summary><b>User-submitted file with an HTML INPUT element</b> (click to show)</summary>
The `table_to_book` and `table_to_sheet` utility functions take a DOM TABLE
element and iterate through the child nodes.
Starting with an HTML INPUT element with `type="file"`:
```js
var workbook = XLSX.utils.table_to_book(document.getElementById('tableau'));
/* DO SOMETHING WITH workbook HERE */
```html
<input type="file" id="input_dom_element">
```
Multiple tables on a web page can be converted to individual worksheets:
For modern websites targeting Chrome 76+, `Blob#arrayBuffer` is recommended:
```js
/* create new workbook */
var workbook = XLSX.utils.book_new();
// XLSX is a global from the standalone script
/* convert table 'table1' to worksheet named "Sheet1" */
var ws1 = XLSX.utils.table_to_sheet(document.getElementById('table1'));
XLSX.utils.book_append_sheet(workbook, ws1, "Sheet1");
async function handleFileAsync(e) {
const file = e.target.files[0];
const data = await file.arrayBuffer();
/* data is an ArrayBuffer */
const workbook = XLSX.read(data);
/* convert table 'table2' to worksheet named "Sheet2" */
var ws2 = XLSX.utils.table_to_sheet(document.getElementById('table2'));
XLSX.utils.book_append_sheet(workbook, ws2, "Sheet2");
/* workbook now has 2 worksheets */
/* DO SOMETHING WITH workbook HERE */
}
input_dom_element.addEventListener("change", handleFileAsync, false);
```
Alternatively, the HTML code can be extracted and parsed:
For broader support (including IE10+), the `FileReader` approach is recommended:
```js
var htmlstr = document.getElementById('tableau').outerHTML;
var workbook = XLSX.read(htmlstr, {type:'string'});
function handleFile(e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
/* reader.readAsArrayBuffer(file) -> data will be an ArrayBuffer */
var workbook = XLSX.read(e.target.result);
/* DO SOMETHING WITH workbook HERE */
};
reader.readAsArrayBuffer(file);
}
input_dom_element.addEventListener("change", handleFile, false);
```
The [`oldie` demo](demos/oldie/) shows an IE-compatible fallback scenario.
</details>
<details>
<summary><b>Browser download file (ajax)</b> (click to show)</summary>
<summary><b>Fetching a file in the web browser ("Ajax")</b> (click to show)</summary>
Note: for a more complete example that works in older browsers, check the demo
at <http://oss.sheetjs.com/sheetjs/ajax.html>. The [`xhr` demo](demos/xhr/)
includes more examples with `XMLHttpRequest` and `fetch`.
For modern websites targeting Chrome 42+, `fetch` is recommended:
```js
// XLSX is a global from the standalone script
(async() => {
const url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx";
const data = await (await fetch(url)).arrayBuffer();
/* data is an ArrayBuffer */
const workbook = XLSX.read(data);
/* DO SOMETHING WITH workbook HERE */
})();
```
For broader support, the `XMLHttpRequest` approach is recommended:
```js
var url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx";
@ -90,116 +180,248 @@ req.onload = function(e) {
var workbook = XLSX.read(req.response);
/* DO SOMETHING WITH workbook HERE */
}
};
req.send();
```
The [`xhr` demo](demos/xhr/) includes a longer discussion and more examples.
<http://oss.sheetjs.com/sheetjs/ajax.html> shows fallback approaches for IE6+.
</details>
<details>
<summary><b>Browser drag-and-drop</b> (click to show)</summary>
<summary><b>Local file in a PhotoShop or InDesign plugin</b> (click to show)</summary>
For modern browsers, `Blob#arrayBuffer` can read data from files:
`readFile` wraps the `File` logic in Photoshop and other ExtendScript targets.
The specified path should be an absolute path:
```js
async function handleDropAsync(e) {
e.stopPropagation(); e.preventDefault();
const f = evt.dataTransfer.files[0];
const data = await f.arrayBuffer();
const workbook = XLSX.read(data);
#include "xlsx.extendscript.js"
/* DO SOMETHING WITH workbook HERE */
}
drop_dom_element.addEventListener('drop', handleDropAsync, false);
/* Read test.xlsx from the Documents folder */
var workbook = XLSX.readFile(Folder.myDocuments + "/test.xlsx");
```
For maximal compatibility, the `FileReader` API should be used:
The [`extendscript` demo](demos/extendscript/) includes a more complex example.
</details>
<details>
<summary><b>Local file in an Electron app</b> (click to show)</summary>
`readFile` can be used in the renderer process:
```js
function handleDrop(e) {
e.stopPropagation(); e.preventDefault();
var f = e.dataTransfer.files[0];
var reader = new FileReader();
reader.onload = function(e) {
var workbook = XLSX.read(e.target.result);
/* From the renderer process */
var XLSX = require("xlsx");
var workbook = XLSX.readFile(path);
```
Electron APIs have changed over time. The [`electron` demo](demos/electron/)
shows a complete example and details the required version-specific settings.
</details>
<details>
<summary><b>Local file in a mobile app with React Native</b> (click to show)</summary>
The [`react` demo](demos/react) includes a sample React Native app.
Since React Native does not provide a way to read files from the filesystem, a
third-party library must be used. The following libraries have been tested:
- [`react-native-file-access`](https://npm.im/react-native-file-access)
The `base64` encoding returns strings compatible with the `base64` type:
```js
import XLSX from "xlsx";
import { FileSystem } from "react-native-file-access";
const b64 = await FileSystem.readFile(path, "base64");
/* b64 is a base64 string */
const workbook = XLSX.read(b64, {type: "base64"});
```
- [`react-native-fs`](https://npm.im/react-native-fs)
The `ascii` encoding returns binary strings compatible with the `binary` type:
```js
import XLSX from "xlsx";
import { readFile } from "react-native-fs";
const bstr = await readFile(path, "ascii");
/* bstr is a binary string */
const workbook = XLSX.read(bstr, {type: "binary"});
```
</details>
<details>
<summary><b>NodeJS Server File Uploads</b> (click to show)</summary>
`read` can accept a NodeJS buffer. `readFile` can read files generated by a
HTTP POST request body parser like [`formidable`](https://npm.im/formidable):
```js
const XLSX = require("xlsx");
const http = require("http");
const formidable = require("formidable");
const server = http.createServer((req, res) => {
const form = new formidable.IncomingForm();
form.parse(req, (err, fields, files) => {
/* grab the first file */
const f = Object.entries(files)[0][1];
const path = f.filepath;
const workbook = XLSX.readFile(path);
/* DO SOMETHING WITH workbook HERE */
};
reader.readAsArrayBuffer(f);
}
drop_dom_element.addEventListener('drop', handleDrop, false);
});
}).listen(process.env.PORT || 7262);
```
The [`server` demo](demos/server) has more advanced examples.
</details>
<details>
<summary><b>Browser file upload form element</b> (click to show)</summary>
<summary><b>Download files in a NodeJS process</b> (click to show)</summary>
Data from file input elements can be processed using the same APIs as in the
drag-and-drop example.
Using `Blob#arrayBuffer`:
Node 17.5 and 18.0 have native support for fetch:
```js
async function handleFileAsync(e) {
const file = e.target.files[0];
const data = await file.arrayBuffer();
const workbook = XLSX.read(data);
const XLSX = require("xlsx");
const data = await (await fetch(url)).arrayBuffer();
/* data is an ArrayBuffer */
const workbook = XLSX.read(data);
```
For broader compatibility, third-party modules are recommended.
[`request`](https://npm.im/request) requires a `null` encoding to yield Buffers:
```js
var XLSX = require("xlsx");
var request = require("request");
request({url: url, encoding: null}, function(err, resp, body) {
var workbook = XLSX.read(body);
/* DO SOMETHING WITH workbook HERE */
}
input_dom_element.addEventListener('change', handleFileAsync, false);
});
```
Using `FileReader`:
[`axios`](https://npm.im/axios) works the same way in browser and in NodeJS:
```js
function handleFile(e) {
var files = e.target.files, f = files[0];
var reader = new FileReader();
reader.onload = function(e) {
var workbook = XLSX.read(e.target.result);
const XLSX = require("xlsx");
const axios = require("axios");
/* DO SOMETHING WITH workbook HERE */
};
reader.readAsArrayBuffer(f);
}
input_dom_element.addEventListener('change', handleFile, false);
(async() => {
const res = await axios.get(url, {responseType: "arraybuffer"});
/* res.data is a Buffer */
const workbook = XLSX.read(res.data);
/* DO SOMETHING WITH workbook HERE */
})();
```
The [`oldie` demo](demos/oldie/) shows an IE-compatible fallback scenario.
</details>
More specialized cases, including mobile app file processing, are covered in the
[included demos](demos/)
### Parsing Examples
- <https://oss.sheetjs.com/sheetjs/> HTML5 File API / Base64 Text / Web Workers
Note that older versions of IE do not support HTML5 File API, so the Base64 mode
is used for testing.
<details>
<summary><b>Get Base64 encoding on OSX / Windows</b> (click to show)</summary>
<summary><b>Download files in an Electron app</b> (click to show)</summary>
On OSX you can get the Base64 encoding with:
The `net` module in the main process can make HTTP/HTTPS requests to external
resources. Responses should be manually concatenated using `Buffer.concat`:
```bash
$ <target_file base64 | pbcopy
```js
const XLSX = require("xlsx");
const { net } = require("electron");
const req = net.request(url);
req.on("response", (res) => {
const bufs = []; // this array will collect all of the buffers
res.on("data", (chunk) => { bufs.push(chunk); });
res.on("end", () => {
const workbook = XLSX.read(Buffer.concat(bufs));
/* DO SOMETHING WITH workbook HERE */
});
});
req.end();
```
On Windows XP and up you can get the Base64 encoding using `certutil`:
```cmd
> certutil -encode target_file target_file.b64
```
(note: You have to open the file and remove the header and footer lines)
</details>
- <http://oss.sheetjs.com/sheetjs/ajax.html> XMLHttpRequest
<details>
<summary><b>Readable Streams in NodeJS</b> (click to show)</summary>
When dealing with Readable Streams, the easiest approach is to buffer the stream
and process the whole thing at the end:
```js
var fs = require("fs");
var XLSX = require("xlsx");
function process_RS(stream, cb) {
var buffers = [];
stream.on("data", function(data) { buffers.push(data); });
stream.on("end", function() {
var buffer = Buffer.concat(buffers);
var workbook = XLSX.read(buffer, {type:"buffer"});
/* DO SOMETHING WITH workbook IN THE CALLBACK */
cb(workbook);
});
}
```
</details>
<details>
<summary><b>ReadableStream in the browser</b> (click to show)</summary>
When dealing with `ReadableStream`, the easiest approach is to buffer the stream
and process the whole thing at the end:
```js
// XLSX is a global from the standalone script
async function process_RS(stream) {
/* collect data */
const buffers = [];
const reader = stream.getReader();
for(;;) {
const res = await reader.read();
if(res.value) buffers.push(res.value);
if(res.done) break;
}
/* concat */
const out = new Uint8Array(buffers.reduce((acc, v) => acc + v.length, 0));
let off = 0;
for(const u8 of arr) {
out.set(u8, off);
off += u8.length;
}
return out;
}
const data = await process_RS(stream);
/* data is Uint8Array */
const workbook = XLSX.read(data);
```
</details>
More detailed examples are covered in the [included demos](demos/)

@ -1,67 +0,0 @@
### Streaming Read
<details>
<summary><b>Why is there no Streaming Read API?</b> (click to show)</summary>
The most common and interesting formats (XLS, XLSX/M, XLSB, ODS) are ultimately
ZIP or CFB containers of files. Neither format puts the directory structure at
the beginning of the file: ZIP files place the Central Directory records at the
end of the logical file, while CFB files can place the storage info anywhere in
the file! As a result, to properly handle these formats, a streaming function
would have to buffer the entire file before commencing. That belies the
expectations of streaming, so we do not provide any streaming read API.
</details>
When dealing with Readable Streams, the easiest approach is to buffer the stream
and process the whole thing at the end. This can be done with a temporary file
or by explicitly concatenating the stream:
<details>
<summary><b>Explicitly concatenating streams</b> (click to show)</summary>
```js
var fs = require('fs');
var XLSX = require('xlsx');
function process_RS(stream/*:ReadStream*/, cb/*:(wb:Workbook)=>void*/)/*:void*/{
var buffers = [];
stream.on('data', function(data) { buffers.push(data); });
stream.on('end', function() {
var buffer = Buffer.concat(buffers);
var workbook = XLSX.read(buffer, {type:"buffer"});
/* DO SOMETHING WITH workbook IN THE CALLBACK */
cb(workbook);
});
}
```
More robust solutions are available using modules like `concat-stream`.
</details>
<details>
<summary><b>Writing to filesystem first</b> (click to show)</summary>
This example uses [`tempfile`](https://npm.im/tempfile) to generate file names:
```js
var fs = require('fs'), tempfile = require('tempfile');
var XLSX = require('xlsx');
function process_RS(stream/*:ReadStream*/, cb/*:(wb:Workbook)=>void*/)/*:void*/{
var fname = tempfile('.sheetjs');
console.log(fname);
var ostream = fs.createWriteStream(fname);
stream.pipe(ostream);
ostream.on('finish', function() {
var workbook = XLSX.readFile(fname);
fs.unlinkSync(fname);
/* DO SOMETHING WITH workbook IN THE CALLBACK */
cb(workbook);
});
}
```
</details>

182
docbits/22_ingress.md Normal file

@ -0,0 +1,182 @@
### Processing JSON and JS Data
JSON and JS data tend to represent single worksheets. This section will use a
few utility functions to generate workbooks:
_Create a new Worksheet_
```js
var workbook = XLSX.utils.book_new();
```
The `book_new` utility function creates an empty workbook with no worksheets.
_Append a Worksheet to a Workbook_
```js
XLSX.utils.book_append_sheet(workbook, worksheet, sheet_name);
```
The `book_append_sheet` utility function appends a worksheet to the workbook.
The third argument specifies the desired worksheet name. Multiple worksheets can
be added to a workbook by calling the function multiple times.
#### API
_Create a worksheet from an array of arrays of JS values_
```js
var worksheet = XLSX.utils.aoa_to_sheet(aoa, opts);
```
The `aoa_to_sheet` utility function walks an "array of arrays" in row-major
order, generating a worksheet object. The following snippet generates a sheet
with cell `A1` set to the string `A1`, cell `B1` set to `B2`, etc:
```js
var worksheet = XLSX.utils.aoa_to_sheet([
["A1", "B1", "C1"],
["A2", "B2", "C2"],
["A3", "B3", "C3"]
])
```
["Array of Arrays Input"](#array-of-arrays-input) describes the function and the
optional `opts` argument in more detail.
_Create a worksheet from an array of JS objects_
```js
var worksheet = XLSX.utils.json_to_sheet(jsa, opts);
```
The `json_to_sheet` utility function walks an array of JS objects in order,
generating a worksheet object. By default, it will generate a header row and
one row per object in the array. The optional `opts` argument has settings to
control the column order and header output.
["Array of Objects Input"](#array-of-arrays-input) describes the function and
the optional `opts` argument in more detail.
#### Examples
["Zen of SheetJS"](#the-zen-of-sheetjs) contains a detailed example "Get Data
from a JSON Endpoint and Generate a Workbook"
The [`database` demo](/demos/database/) includes examples of working with
databases and query results.
### Processing HTML Tables
#### API
_Create a worksheet by scraping an HTML TABLE in the page_
```js
var worksheet = XLSX.utils.table_to_sheet(dom_element, opts);
```
The `table_to_sheet` utility function takes a DOM TABLE element and iterates
through the rows to generate a worksheet. The `opts` argument is optional.
["HTML Table Input"](#html-table-input) describes the function in more detail.
_Create a workbook by scraping an HTML TABLE in the page_
```js
var workbook = XLSX.utils.table_to_book(dom_element, opts);
```
The `table_to_book` utility function follows the same logic as `table_to_sheet`.
After generating a worksheet, it creates a blank workbook and appends the
spreadsheet.
The options argument supports the same options as `table_to_sheet`, with the
addition of a `sheet` property to control the worksheet name. If the property
is missing or no options are specified, the default name `Sheet1` is used.
#### Examples
Here are a few common scenarios (click on each subtitle to see the code):
<details>
<summary><b>HTML TABLE element in a webpage</b> (click to show)</summary>
```html
<!-- include the standalone script and shim. this uses the UNPKG CDN -->
<script src="https://unpkg.com/xlsx/dist/shim.min.js"></script>
<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
<!-- example table with id attribute -->
<table id="tableau">
<tr><td>Sheet</td><td>JS</td></tr>
<tr><td>12345</td><td>67</td></tr>
</table>
<!-- this block should appear after the table HTML and the standalone script -->
<script type="text/javascript">
var workbook = XLSX.utils.table_to_book(document.getElementById("tableau"));
/* DO SOMETHING WITH workbook HERE */
</script>
```
Multiple tables on a web page can be converted to individual worksheets:
```js
/* create new workbook */
var workbook = XLSX.utils.book_new();
/* convert table "table1" to worksheet named "Sheet1" */
var sheet1 = XLSX.utils.table_to_sheet(document.getElementById("table1"));
XLSX.utils.book_append_sheet(workbook, sheet1, "Sheet1");
/* convert table "table2" to worksheet named "Sheet2" */
var sheet2 = XLSX.utils.table_to_sheet(document.getElementById("table2"));
XLSX.utils.book_append_sheet(workbook, sheet2, "Sheet2");
/* workbook now has 2 worksheets */
```
Alternatively, the HTML code can be extracted and parsed:
```js
var htmlstr = document.getElementById("tableau").outerHTML;
var workbook = XLSX.read(htmlstr, {type:"string"});
```
</details>
<details>
<summary><b>Chrome/Chromium Extension</b> (click to show)</summary>
The [`chrome` demo](demos/chrome/) shows a complete example and details the
required permissions and other settings.
In an extension, it is recommended to generate the workbook in a content script
and pass the object back to the extension:
```js
/* in the worker script */
chrome.runtime.onMessage.addListener(function(msg, sender, cb) {
/* pass a message like { sheetjs: true } from the extension to scrape */
if(!msg || !msg.sheetjs) return;
/* create a new workbook */
var workbook = XLSX.utils.book_new();
/* loop through each table element */
var tables = document.getElementsByTagName("table")
for(var i = 0; i < tables.length; ++i) {
var worksheet = XLSX.utils.table_to_sheet(tables[i]);
XLSX.utils.book_append_sheet(workbook, worksheet, "Table" + i);
}
/* pass back to the extension */
return cb(workbook);
});
```
</details>

File diff suppressed because it is too large Load Diff

@ -4,11 +4,18 @@
- [Getting Started](README.md#getting-started)
* [Installation](README.md#installation)
* [Usage](README.md#usage)
+ [The Zen of SheetJS](README.md#the-zen-of-sheetjs)
* [The Zen of SheetJS](README.md#the-zen-of-sheetjs)
* [JS Ecosystem Demos](README.md#js-ecosystem-demos)
- [Parsing Workbooks](README.md#parsing-workbooks)
* [Parsing Examples](README.md#parsing-examples)
* [Streaming Read](README.md#streaming-read)
- [Acquiring and Extracting Data](README.md#acquiring-and-extracting-data)
* [Parsing Workbooks](README.md#parsing-workbooks)
+ [API](README.md#api)
+ [Examples](README.md#examples)
* [Processing JSON and JS Data](README.md#processing-json-and-js-data)
+ [API](README.md#api-1)
+ [Examples](README.md#examples-1)
* [Processing HTML Tables](README.md#processing-html-tables)
+ [API](README.md#api-2)
+ [Examples](README.md#examples-2)
- [Working with the Workbook](README.md#working-with-the-workbook)
* [Parsing and Writing Examples](README.md#parsing-and-writing-examples)
- [Writing Workbooks](README.md#writing-workbooks)

21
modules/59_vba.js Normal file

@ -0,0 +1,21 @@
var CT_VBA = "application/vnd.ms-office.vbaProject";
function make_vba_xls(cfb) {
var newcfb = CFB.utils.cfb_new({ root: "R" });
cfb.FullPaths.forEach(function(p, i) {
if (p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/))
return;
var newpath = p.replace(/^[^\/]*/, "R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
});
return CFB.write(newcfb);
}
function fill_vba_xls(cfb, vba) {
vba.FullPaths.forEach(function(p, i) {
if (i == 0)
return;
var newpath = p.replace(/[^\/]*[\/]/, "/_VBA_PROJECT_CUR/");
if (newpath.slice(-1) !== "/")
CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
});
}
var VBAFMTS = ["xlsb", "xlsm", "xlam", "biff8", "xla"];

24
modules/59_vba.ts Normal file

@ -0,0 +1,24 @@
import * as CFBModule from 'cfb';
type CFBType = typeof CFBModule;
declare var CFB: CFBType;
var CT_VBA = "application/vnd.ms-office.vbaProject";
function make_vba_xls(cfb: CFBModule.CFB$Container) {
var newcfb = CFB.utils.cfb_new({root:"R"});
cfb.FullPaths.forEach(function(p, i) {
if(p.slice(-1) === "/" || !p.match(/_VBA_PROJECT_CUR/)) return;
var newpath = p.replace(/^[^\/]*/,"R").replace(/\/_VBA_PROJECT_CUR\u0000*/, "");
CFB.utils.cfb_add(newcfb, newpath, cfb.FileIndex[i].content);
});
return CFB.write(newcfb);
}
function fill_vba_xls(cfb: CFBModule.CFB$Container, vba: CFBModule.CFB$Container): void {
vba.FullPaths.forEach(function(p, i) {
if(i == 0) return;
var newpath = p.replace(/[^\/]*[\/]/, "/_VBA_PROJECT_CUR/");
if(newpath.slice(-1) !== "/") CFB.utils.cfb_add(cfb, newpath, vba.FileIndex[i].content);
});
}
var VBAFMTS: string[] = [ "xlsb", "xlsm", "xlam", "biff8", "xla" ];

1133
modules/64_ftab.js Normal file

File diff suppressed because it is too large Load Diff

1138
modules/64_ftab.ts Normal file

File diff suppressed because it is too large Load Diff

@ -2,10 +2,12 @@ LIBFILES=$(wildcard src/*.ts)
TSFILES=$(wildcard *.ts)
ENTRIES=$(subst .ts,.js,$(TSFILES))
BAREJS=04_base64.js 59_vba.js 64_ftab.js
.PHONY: all
all: $(ENTRIES)
04_base64.js: 04_base64.ts $(LIBFILES)
$(BAREJS): %.js: %.ts $(LIBFILES)
npx esbuild $< --outfile=$@ --platform=browser --target=es5
83_numbers.js: 83_numbers.ts $(LIBFILES)