This commit is contained in:
SheetJS 2022-08-21 15:43:30 -04:00
parent 4b4bf7ff54
commit 541e0a8348
9 changed files with 325 additions and 30 deletions

@ -23,12 +23,18 @@ SpreadsheetML
Unhide Unhide
VBA VBA
Visicalc Visicalc
Chartsheet
chartsheet chartsheet
Chartsheets
chartsheets chartsheets
Dialogsheet
dialogsheet dialogsheet
Dialogsheets
dialogsheets dialogsheets
dBASE dBASE
Macrosheet
macrosheet macrosheet
Macrosheets
macrosheets macrosheets
tooltip tooltip
tooltips tooltips
@ -192,3 +198,11 @@ vscode-data-preview
axios axios
superagent superagent
LLC
Bundlers
JSON
QPW
XLML
XLS
XLSB
XLSX

@ -10,6 +10,10 @@ build:
serve: serve:
cd docs; python -mSimpleHTTPServer || python3 -mhttp.server; cd - cd docs; python -mSimpleHTTPServer || python3 -mhttp.server; cd -
.PHONY: spell
spell:
npx spellchecker-cli -d .spelling -f 'docz/**/*.md*'
.PHONY: index .PHONY: index
index: readme ## Rebuild site index: readme ## Rebuild site
sed -i .bak 's/](d/](https:\/\/github.com\/SheetJS\/SheetJS\/tree\/master\/d/g' README.md sed -i .bak 's/](d/](https:\/\/github.com\/SheetJS\/SheetJS\/tree\/master\/d/g' README.md

@ -18,8 +18,8 @@ and birthdays. [Click here](#live-demo) to jump to the live demo
### Raw Data ### Raw Data
[The raw data is available in JSON form](https://theunitedstates.io/congress-legislators/executive.json). [The raw data is available in JSON form](https://theunitedstates.io/congress-legislators/data/executive.json).
For convenience, it has been [mirrored here](https://sheetjs.com/executive.json) For convenience, it has been [mirrored here](https://sheetjs.com/data/executive.json)
The data result is an Array of objects. This is the data for John Adams: The data result is an Array of objects. This is the data for John Adams:
@ -165,7 +165,7 @@ XLSX.writeFile(workbook, "Presidents.xlsx");
```jsx live ```jsx live
function Presidents() { return ( <button onClick={async () => { function Presidents() { return ( <button onClick={async () => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -213,7 +213,7 @@ hosted (no `file:///` access).
<script> <script>
(async() => { (async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -259,7 +259,7 @@ const XLSX = require("xlsx");
(async() => { (async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -309,7 +309,7 @@ const axios = require("axios");
(async() => { (async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
// highlight-next-line // highlight-next-line
const raw_data = (await axios(url, {responseType: "json"})).data; const raw_data = (await axios(url, {responseType: "json"})).data;
@ -352,7 +352,7 @@ Save the following script to `snippet.ts` and run with
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs'; import * as XLSX from 'https://cdn.sheetjs.com/xlsx-latest/package/xlsx.mjs';
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -393,7 +393,7 @@ import * as fs from 'fs';
XLSX.set_fs(fs); XLSX.set_fs(fs);
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */

@ -101,7 +101,7 @@ await db.collection('coll').insertMany(aoo, { ordered: true });
::: :::
This example will fetch <https://sheetjs.com/cd.xls>, scan the columns of the This example will fetch <https://sheetjs.com/data/cd.xls>, scan the columns of the
first worksheet to determine data types, and generate 6 PostgreSQL statements. first worksheet to determine data types, and generate 6 PostgreSQL statements.
<details><summary><b>Explanation</b> (click to show)</summary> <details><summary><b>Explanation</b> (click to show)</summary>
@ -208,7 +208,7 @@ function SheetJSQLWriter() {
if(fields.length) return `INSERT INTO \`${wsname}\` (${fields.join(", ")}) VALUES (${values.join(", ")})`; if(fields.length) return `INSERT INTO \`${wsname}\` (${fields.join(", ")}) VALUES (${values.join(", ")})`;
})).filter(x => x).slice(0, 6); })).filter(x => x).slice(0, 6);
} }
const [url, setUrl] = React.useState("https://sheetjs.com/cd.xls"); const [url, setUrl] = React.useState("https://sheetjs.com/data/cd.xls");
const set_url = React.useCallback((evt) => setUrl(evt.target.value)); const set_url = React.useCallback((evt) => setUrl(evt.target.value));
const [out, setOut] = React.useState(""); const [out, setOut] = React.useState("");
const xport = React.useCallback(async() => { const xport = React.useCallback(async() => {
@ -494,7 +494,7 @@ for(var i = 0; i < localStorage.length; ++i) aoo.push(JSON.parse(localStorage.ge
const ws = XLSX.utils.json_to_sheet(aoo); const ws = XLSX.utils.json_to_sheet(aoo);
``` ```
This example will fetch <https://sheetjs.com/cd.xls>, fill `localStorage` with This example will fetch <https://sheetjs.com/data/cd.xls>, fill `localStorage` with
rows, then generate a worksheet from the rows and write to a new file. rows, then generate a worksheet from the rows and write to a new file.
:::caution :::caution
@ -506,7 +506,7 @@ is strongly recommended to convert that array to a worksheet directly.
```jsx live ```jsx live
function SheetJStorage() { function SheetJStorage() {
const [url, setUrl] = React.useState("https://sheetjs.com/cd.xls"); const [url, setUrl] = React.useState("https://sheetjs.com/data/cd.xls");
const set_url = React.useCallback((evt) => setUrl(evt.target.value)); const set_url = React.useCallback((evt) => setUrl(evt.target.value));
const [out, setOut] = React.useState(""); const [out, setOut] = React.useState("");
const xport = React.useCallback(async() => { const xport = React.useCallback(async() => {

@ -44,7 +44,7 @@ function worksheet_to_csv_url(worksheet) {
``` ```
[This demo mirrors TFjs docs](https://js.tensorflow.org/api/latest/#data.csv), [This demo mirrors TFjs docs](https://js.tensorflow.org/api/latest/#data.csv),
fetching [an XLSX export of the example dataset](https://sheetjs.com/bht.xlsx). fetching [an XLSX export of the example dataset](https://sheetjs.com/data/bht.xlsx).
<details><summary><b>TF CSV Demo using XLSX files</b> (click to show)</summary> <details><summary><b>TF CSV Demo using XLSX files</b> (click to show)</summary>
@ -53,7 +53,7 @@ function SheetJSToTFJSCSV() {
const [output, setOutput] = React.useState(""); const [output, setOutput] = React.useState("");
const doit = React.useCallback(async () => { const doit = React.useCallback(async () => {
/* fetch file */ /* fetch file */
const f = await fetch("https://sheetjs.com/bht.xlsx"); const f = await fetch("https://sheetjs.com/data/bht.xlsx");
const ab = await f.arrayBuffer(); const ab = await f.arrayBuffer();
/* parse file and get first worksheet */ /* parse file and get first worksheet */
const wb = XLSX.read(ab); const wb = XLSX.read(ab);
@ -125,7 +125,7 @@ loads data from a JSON file:
] ]
``` ```
In real use cases, data is stored in [spreadsheets](https://sheetjs.com/cd.xls) In real use cases, data is stored in [spreadsheets](https://sheetjs.com/data/cd.xls)
![cd.xls screenshot](pathname:///files/cd.png) ![cd.xls screenshot](pathname:///files/cd.png)
@ -140,7 +140,7 @@ from the official example are highlighted below:
async function getData() { async function getData() {
// highlight-start // highlight-start
/* fetch file */ /* fetch file */
const carsDataResponse = await fetch('https://sheetjs.com/cd.xls'); const carsDataResponse = await fetch('https://sheetjs.com/data/cd.xls');
/* get file data (ArrayBuffer) */ /* get file data (ArrayBuffer) */
const carsDataAB = await carsDataResponse.arrayBuffer(); const carsDataAB = await carsDataResponse.arrayBuffer();
/* parse */ /* parse */

@ -124,7 +124,7 @@ import * as fs from 'fs';
XLSX.set_fs(fs); XLSX.set_fs(fs);
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -209,7 +209,7 @@ import { utils, version, writeFileXLSX } from 'xlsx/xlsx.mjs';
(async() => { (async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -291,7 +291,7 @@ set_fs(fs);
(async() => { (async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -369,7 +369,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("vers").innerText = version; document.getElementById("vers").innerText = version;
document.getElementById("xport").onclick = async() => { document.getElementById("xport").onclick = async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -495,7 +495,7 @@ import { utils, version, writeFileXLSX } from 'xlsx/xlsx.mjs';
document.getElementById("xport").addEventListener("click", async() => { document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -589,7 +589,7 @@ import { utils, version, writeFileXLSX } from 'xlsx/xlsx.mjs';
document.getElementById("xport").addEventListener("click", async() => { document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -694,7 +694,7 @@ import { utils, version, writeFileXLSX } from 'xlsx';
document.getElementById("xport").addEventListener("click", async() => { document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -910,7 +910,7 @@ SystemJS.config({
SystemJS.import('xlsx').then(async function(XLSX) { SystemJS.import('xlsx').then(async function(XLSX) {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -1005,7 +1005,7 @@ interface President {
async function xport() { async function xport() {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data: President[] = await (await fetch(url)).json(); const raw_data: President[] = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -1108,7 +1108,7 @@ import { utils, version, writeFileXLSX } from 'xlsx/xlsx.mjs';
document.getElementById("xport").addEventListener("click", async() => { document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */
@ -1268,7 +1268,7 @@ import { utils, version, writeFileXLSX } from 'xlsx/xlsx.mjs';
document.getElementById("xport").addEventListener("click", async() => { document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */

@ -0,0 +1,277 @@
---
sidebar_position: 25
title: Azure Cloud Services
---
Azure is a Cloud Services platform which includes traditional virtual machine
support, "Serverless Functions", cloud storage and much more.
:::caution
Azure iterates quickly and there is no guarantee that the referenced services
will be available in the future.
:::
This demo focuses on two key offerings: cloud storage ("Azure Blob Storage")
and the "Serverless Function" platform ("Azure Functions").
## Azure Functions
This discussion focuses on the "HTTP Trigger" function type.
:::info
To enable binary data processing, a setting must be changed in `function.json`:
```json title="function.json"
{
"bindings": [
{
"type": "httpTrigger",
"direction": "in",
//highlight-next-line
"dataType": "binary",
"name": "req",
```
:::
### Reading Data
`formidable` expects a stream and Azure does not present one. It can be made:
```js
const XLSX = require('xlsx');
const formidable = require('formidable');
const Readable = require('stream').Readable;
/* formidable expects the request object to be a stream */
const streamify = (req) => {
if(typeof req.on !== 'undefined') return req;
const s = new Readable();
s._read = ()=>{};
s.push(Buffer.from(req.body));
s.push(null);
Object.assign(s, req);
return s;
};
module.exports = (context, req) => {
const form = new formidable.IncomingForm();
form.parse(streamify(req), (err, fields, files) => {
/* grab the first file */
var f = files["upload"];
if(!f) {
context.res = { status: 400, body: "Must submit a file for processing!" };
} else {
/* file is stored in a temp directory, so we can point to that and read it */
const wb = XLSX.read(f.filepath, {type:"file"});
/* generate CSV from first sheet */
const csv = XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
context.res = { status: 200, body: csv };
}
context.done();
});
}
```
### Writing Data
The `body` property can be a Buffer, like those generated by `XLSX.write`:
```js
const XLSX = require('xlsx');
module.exports = (context, req) => {
// generate XLSX file in a Buffer
var ws = XLSX.utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Data");
// highlight-next-line
var buf = XLSX.write(wb, {type: "buffer", bookType: "xlsx"});
// Set the body and Content-Disposition header
// highlight-start
context.res = {
status: 200,
headers: { "Content-Disposition": `attachment; filename="SheetJSAzure.xlsx";` },
body: buf
};
// highlight-end
context.done();
};
```
### Demo
<details><summary><b>Complete Example</b> (click to show)</summary>
0) Review the quick start for JavaScript on Azure Functions. This involves
installing the Azure Functions Core Tools and other dependencies.
1) Create a new project and install dependencies:
```bash
func init sheetjs-azure --worker-runtime node --language javascript
cd sheetjs-azure
npm i
npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz formidable
```
2) Create a new "HTTP Trigger" function:
```bash
func new --template "Http Trigger" --name SheetJSAzure
```
3) Edit `SheetJSAzure/function.json` to add the `dataType: "binary"` property:
```js title="SheetJSAzure/function.json"
"direction": "in",
// highlight-next-line
"dataType": "binary",
"name": "req",
```
4) Replace `SheetJSAzure/index.js` with the following:
```js title="SheetJSAzure/index.js"
/* sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
const XLSX = require('xlsx');
const formidable = require('formidable');
const Readable = require('stream').Readable;
/* formidable expects the request object to be a stream */
const streamify = (req) => {
if(typeof req.on !== 'undefined') return req;
const s = new Readable();
s._read = ()=>{};
s.push(Buffer.from(req.body));
s.push(null);
Object.assign(s, req);
return s;
};
module.exports = (context, req) => {
if(req.method == "POST") {
const form = new formidable.IncomingForm();
form.parse(streamify(req), (err, fields, files) => {
/* grab the first file */
var f = files["upload"];
if(!f) {
context.res = { status: 400, body: "Must submit a file for processing!" };
} else {
/* file is stored in a temp directory, so we can point to that and read it */
const wb = XLSX.read(f.filepath, {type:"file"});
/* generate CSV from first sheet */
const csv = XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
context.res = { status: 200, body: csv };
}
context.done();
});
} else if(req.method == "GET") {
var ws = XLSX.utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
var wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Data");
var buf = XLSX.write(wb, {type: "buffer", bookType: "xlsx"});
context.res = {
status: 200,
headers: { "Content-Disposition": `attachment; filename="SheetJSAzure.xlsx";` },
body: buf
};
context.done();
} else {
context.res = { status: 500, body: `Unsupported method ${req.method}` };
context.done();
}
};
```
5) Test locally with `npm start`
To test uploads, download <https://sheetjs.com/pres.numbers> and run:
```bash
curl -X POST -F "upload=@pres.numbers" http://localhost:7071/api/SheetJSAzure
```
To test downloads, access http://localhost:7071/api/SheetJSAzure and download
the generated file. Confirm it is a valid file.
6) Deploy to Azure. Replace `NAME_OF_FUNCTION_APP` with the name:
```bash
func azure functionapp publish NAME_OF_FUNCTION_APP
```
Get the function url and test using the same sequence as in step 5.
</details>
## Azure Blob Storage
:::note
This was tested on 2022 August 21.
:::
The main module for Azure Blob Storage is `@azure/storage-blob`. This example
was tested using the "Connection String" authentication method. The strings
are found in the Azure Portal under "Access Keys" for the storage account.
### Reading Data
The `BlobClient#download` method returns a Stream. After collecting into a
Buffer, `XLSX.read` can parse the data:
```js title="SheetJSReadFromAzure.mjs"
import { BlobServiceClient } from "@azure/storage-blob";
import { read, utils } from "xlsx";
/* replace these constants */
const connStr = "<REPLACE WITH CONNECTION STRING>";
const containerName = "<REPLACE WITH CONTAINER NAME>";
const blobName = "<REPLACE WITH BLOB NAME>";
/* get a readable stream*/
const blobServiceClient = BlobServiceClient.fromConnectionString(connStr);
const containerClient = blobServiceClient.getContainerClient(containerName);
const blobClient = containerClient.getBlobClient(blobName);
const response = (await blobClient.download()).readableStreamBody;
/* collect data into a Buffer */
const bufs = [];
for await(const buf of response) bufs.push(buf);
const downloaded = Buffer.concat(bufs);
/* parse downloaded buffer */
const wb = read(downloaded);
/* print first worksheet */
console.log(utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
```
### Writing Data
`BlockBlobClient#upload` directly accepts a Buffer:
```js title="SheetJSWriteToAzure.mjs"
import { BlobServiceClient } from "@azure/storage-blob";
import { read, utils } from "xlsx";
/* replace these constants */
const connStr = "<REPLACE WITH CONNECTION STRING>";
const containerName = "<REPLACE WITH CONTAINER NAME>";
const blobName = "<REPLACE WITH BLOB NAME>";
/* Create a simple workbook and write XLSX to buffer */
const ws = utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
const wb = utils.book_new(); utils.book_append_sheet(wb, ws, "Sheet1");
const buf = write(wb, {type: "buffer", bookType: "xlsx"});
/* upload buffer */
const blobServiceClient = BlobServiceClient.fromConnectionString(connStr);
const containerClient = blobServiceClient.getContainerClient(containerName);
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
const uploadBlobResponse = await blockBlobClient.upload(buf, buf.length);
```

@ -36,7 +36,7 @@ The demo projects include small runnable examples and short explainers.
- [`Command-Line Tools`](./cli) - [`Command-Line Tools`](./cli)
- [`iOS and Android Mobile Applications`](./mobile) - [`iOS and Android Mobile Applications`](./mobile)
- [`NodeJS Server-Side Processing`](https://github.com/SheetJS/SheetJS/tree/master/demos/server/) - [`NodeJS Server-Side Processing`](./server#nodejs)
- [`Deno Server-Side Processing`](./server#deno) - [`Deno Server-Side Processing`](./server#deno)
- [`Content Management and Static Sites`](./content) - [`Content Management and Static Sites`](./content)
- [`Electron`](./desktop#electron) - [`Electron`](./desktop#electron)
@ -50,7 +50,7 @@ The demo projects include small runnable examples and short explainers.
- [`Excel JavaScript API`](./excel) - [`Excel JavaScript API`](./excel)
- [`Headless Automation`](./headless) - [`Headless Automation`](./headless)
- [`Other JavaScript Engines`](./engines) - [`Other JavaScript Engines`](./engines)
- [`"serverless" functions`](https://github.com/SheetJS/SheetJS/tree/master/demos/function/) - [`Azure Functions and Storage`](./azure)
- [`Databases and Structured Data Stores`](./database) - [`Databases and Structured Data Stores`](./database)
- [`NoSQL and Unstructured Data Stores`](./nosql) - [`NoSQL and Unstructured Data Stores`](./nosql)
- [`Legacy Internet Explorer`](./legacy#internet-explorer) - [`Legacy Internet Explorer`](./legacy#internet-explorer)

@ -17,7 +17,7 @@ require.config({
require(["xlsx"], function(XLSX) { require(["xlsx"], function(XLSX) {
document.getElementById("xport").addEventListener("click", async() => { document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */ /* fetch JSON data and parse */
const url = "https://sheetjs.com/executive.json"; const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json(); const raw_data = await (await fetch(url)).json();
/* filter for the Presidents */ /* filter for the Presidents */