diff --git a/docz/docs/02-getting-started/02-examples/02-export.mdx b/docz/docs/02-getting-started/02-examples/02-export.mdx
index 4b88522..a4e737e 100644
--- a/docz/docs/02-getting-started/02-examples/02-export.mdx
+++ b/docz/docs/02-getting-started/02-examples/02-export.mdx
@@ -484,8 +484,14 @@ The data is in the workbook and can be exported.
![Rough export](pathname:///example/rough.png)
There are multiple opportunities for improvement: the headers can be renamed and
-the column widths can be adjusted. [SheetJS Pro](https://sheetjs.com/pro) offers
-additional styling options like cell styling and frozen rows.
+the column widths can be adjusted.
+
+:::tip pass
+
+[SheetJS Pro](https://sheetjs.com/pro) offers additional styling options like
+cell styling and frozen rows.
+
+:::
Changing Header Names (click to show)
@@ -957,10 +963,17 @@ of the React Native documentation before testing the demo.
:::
+:::caution pass
+
+For Android testing, React Native requires Java 11. It will not work with
+current Java releases.
+
+:::
+
Create a new project by running the following commands in the Terminal:
{`\
-npx react-native@0.71 init SheetJSPres --version="0.72.0-rc.1"
+npx -y react-native@0.72.4 init SheetJSPres --version="0.72.4"
cd SheetJSPres
\n\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-native-blob-util@0.17.1`}
diff --git a/docz/docs/03-demos/01-frontend/08-bundler.md b/docz/docs/03-demos/01-frontend/08-bundler.md
index 2c652fc..fee1bf4 100644
--- a/docz/docs/03-demos/01-frontend/08-bundler.md
+++ b/docz/docs/03-demos/01-frontend/08-bundler.md
@@ -1266,7 +1266,7 @@ Access `http://localhost:8080` in your web browser and click the export button.
-:::note
+:::note pass
The [Vite section of the Content demo](/docs/demos/static/vitejs) covers asset
loaders. They are ideal for static sites pulling data from sheets at build time.
diff --git a/docz/docs/03-demos/03-net/09-dom.md b/docz/docs/03-demos/03-net/09-dom.md
index 083c490..52d9797 100644
--- a/docz/docs/03-demos/03-net/09-dom.md
+++ b/docz/docs/03-demos/03-net/09-dom.md
@@ -5,10 +5,16 @@ title: Synthetic DOM
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
-`table_to_book` / `table_to_sheet` / `sheet_add_dom` act on HTML DOM elements.
-Traditionally there is no DOM in server-side environments.
+SheetJS offers three methods to directly process HTML DOM TABLE elements[^1]:
-:::note
+- `table_to_sheet` generates a SheetJS worksheet[^2] from a TABLE element
+- `table_to_book` generates a SheetJS workbook[^3] from a TABLE element
+- `sheet_add_dom` adds data from a TABLE element to an existing worksheet
+
+These methods work in the web browser. NodeJS and other server-side platforms
+traditionally lack a DOM implementation, but third-party modules fill the gap.
+
+:::tip pass
The most robust approach for server-side processing is to automate a headless
web browser. ["Browser Automation"](/docs/demos/net/headless) includes demos.
@@ -17,23 +23,60 @@ web browser. ["Browser Automation"](/docs/demos/net/headless) includes demos.
This demo covers synthetic DOM implementations for non-browser platforms.
+## Integration Details
+
+SheetJS API methods use DOM features that may not be available.
+
+### Table rows
+
+The `rows` property of TABLE elements is a list of TR row children. This list
+automatically updates when rows are added and deleted.
+
+SheetJS does not mutate `rows`. Assuming there are no nested tables, the `rows`
+property can be created using `getElementsByTagName`:
+
+```js
+tbl.rows = Array.from(tbl.getElementsByTagName("tr"));
+```
+
+### Row cells
+
+The `cells` property of TR elements is a list of TD cell children. This list
+automatically updates when cells are added and deleted.
+
+SheetJS does not mutate `cells`. Assuming there are no nested tables, the
+`cells` property can be created using `getElementsByTagName`:
+
+```js
+tbl.rows.forEach(row => row.cells = Array.from(row.getElementsByTagName("td")));
+```
+
## NodeJS
### JSDOM
-JSDOM is a DOM implementation for NodeJS. Given an HTML string, a reference to
-the table element plays nice with the SheetJS DOM methods:
+JSDOM is a DOM implementation for NodeJS. The synthetic DOM elements are
+compatible with SheetJS methods.
-```js
+The following example scrapes the first table from the file `SheetJSTable.html`
+and generates a XLSX workbook:
+
+```js title="SheetJSDOM.js"
const XLSX = require("xlsx");
+const { readFileSync } = require("fs");
const { JSDOM } = require("jsdom");
-/* parse HTML */
-const dom = new JSDOM(html_string);
+/* obtain HTML string. This example reads from SheetJSTable.html */
+const html_str = readFileSync("SheetJSTable.html", "utf8");
+
+// highlight-start
/* get first TABLE element */
-const tbl = dom.window.document.querySelector("table");
+const doc = new JSDOM(html_str).window.document.querySelector("table");
+
/* generate workbook */
-const workbook = XLSX.utils.table_to_book(tbl);
+const workbook = XLSX.utils.table_to_book(doc);
+// highlight-end
+
XLSX.writeFile(workbook, "SheetJSDOM.xlsx");
```
@@ -41,7 +84,7 @@ XLSX.writeFile(workbook, "SheetJSDOM.xlsx");
:::note
-This demo was last tested on 2023 May 18 against JSDOM `22.0.0`
+This demo was last tested on 2023 September 10 against JSDOM `22.1.0`
:::
@@ -51,21 +94,7 @@ This demo was last tested on 2023 May 18 against JSDOM `22.0.0`
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz jsdom@22.0.0`}
-2) Save the following script to `SheetJSDOM.js`:
-
-```js title="SheetJSDOM.js"
-const XLSX = require("xlsx");
-const { readFileSync } = require("fs");
-const { JSDOM } = require("jsdom");
-
-/* obtain HTML string. This example reads from SheetJSTable.html */
-const html_str = readFileSync("SheetJSTable.html", "utf8");
-/* get first TABLE element */
-const doc = new JSDOM(html_str).window.document.querySelector("table");
-/* generate workbook */
-const workbook = XLSX.utils.table_to_book(doc);
-XLSX.writeFile(workbook, "SheetJSDOM.xlsx");
-```
+2) Save the previous codeblock to `SheetJSDOM.js`.
3) Download [the sample `SheetJSTable.html`](pathname:///dom/SheetJSTable.html):
@@ -83,48 +112,79 @@ The script will create a file `SheetJSDOM.xlsx` that can be opened.
-### XMLDOM
+### HappyDOM
-XMLDOM provides a DOM framework for NodeJS. Given an HTML string, a reference to
-the table element works with the SheetJS DOM methods after patching the object.
+HappyDOM provides a DOM framework for NodeJS. For the tested version (`11.0.2`),
+the following patches were needed:
+
+- TABLE `rows` property (explained above)
+- TR `cells` property (explained above)
Complete Demo (click to show)
:::note
-This demo was last tested on 2023 May 18 against XMLDOM `0.8.7`
+This demo was last tested on 2023 September 10 against HappyDOM `11.0.2`
+
+:::
+
+1) Install SheetJS and HappyDOM libraries:
+
+{`\
+npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz happy-dom@11.0.2`}
+
+
+2) Download [the sample script `SheetJSHappyDOM.js`](pathname:///dom/SheetJSHappyDOM.js):
+
+```bash
+curl -LO https://docs.sheetjs.com/dom/SheetJSHappyDOM.js
+```
+
+3) Run the script:
+
+```bash
+node SheetJSHappyDOM.js
+```
+
+The script will create a file `SheetJSHappyDOM.xlsx` that can be opened.
+
+
+
+### XMLDOM
+
+XMLDOM provides a DOM framework for NodeJS. For the tested version (`0.8.10`),
+the following patches were needed:
+
+- TABLE `rows` property (explained above)
+- TR `cells` property (explained above)
+- Element `innerHTML` property:
+
+```js
+Object.defineProperty(tbl.__proto__, "innerHTML", { get: function() {
+ var outerHTML = new XMLSerializer().serializeToString(this);
+ if(outerHTML.match(/]*(("[^"]*"|'[^']*')[^"'>]*)*>/, "");
+}});
+```
+
+Complete Demo (click to show)
+
+:::note
+
+This demo was last tested on 2023 September 10 against XMLDOM `0.8.10`
:::
1) Install SheetJS and XMLDOM libraries:
{`\
-npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @xmldom/xmldom@0.8.7`}
+npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @xmldom/xmldom@0.8.10`}
-2) Save the following codeblock to `SheetJSXMLDOM.js`:
+2) Download [the sample script `SheetJSXMLDOM.js`](pathname:///dom/SheetJSXMLDOM.js):
-```js title="SheetJSXMLDOM.js"
-const XLSX = require("xlsx");
-const { DOMParser, XMLSerializer } = require("@xmldom/xmldom");
-
-(async() => {
-const text = await (await fetch('https://docs.sheetjs.com/dom/SheetJSTable.html')).text();
-const doc = new DOMParser().parseFromString( text, "text/html");
-const tbl = doc.getElementsByTagName("table")[0];
-
-/* patch XMLDOM */
-tbl.rows = Array.from(tbl.getElementsByTagName("tr"));
-tbl.rows.forEach(row => row.cells = Array.from(row.getElementsByTagName("td")))
-Object.defineProperty(tbl.__proto__, "innerHTML", { get: function() {
- var outerHTML = new XMLSerializer().serializeToString(this);
- if(outerHTML.match(/]*(("[^"]*"|'[^']*')[^"'>]*)*>/, "");
-}});
-
-const workbook = XLSX.utils.table_to_book(tbl);
-XLSX.writeFile(workbook, "SheetJSXMLDOM.xlsx");
-})();
+```bash
+curl -LO https://docs.sheetjs.com/dom/SheetJSXMLDOM.js
```
3) Run the script:
@@ -135,28 +195,26 @@ node SheetJSXMLDOM.js
The script will create a file `SheetJSXMLDOM.xlsx` that can be opened.
-
### CheerioJS
-:::caution
+:::caution pass
Cheerio does not support a number of fundamental properties out of the box. They
can be shimmed, but it is strongly recommended to use a more compliant library.
:::
-CheerioJS provides a DOM-like framework for NodeJS. Given an HTML string, a
-reference to the table element works with the SheetJS DOM methods with some
-prototype fixes. [`SheetJSCheerio.js`](pathname:///dom/SheetJSCheerio.js) is a
-complete script.
+CheerioJS provides a DOM-like framework for NodeJS. Many features were missing.
+[`SheetJSCheerio.js`](pathname:///dom/SheetJSCheerio.js) implements the missing
+features to ensure that SheetJS DOM methods can process TABLE elements.
Complete Demo (click to show)
:::note
-This demo was last tested on 2023 May 18 against Cheerio `1.0.0-rc.12`
+This demo was last tested on 2023 September 10 against Cheerio `1.0.0-rc.12`
:::
@@ -192,8 +250,11 @@ The script will create a file `SheetJSCheerio.xlsx` that can be opened.
### DenoDOM
-DenoDOM provides a DOM framework for Deno. Given an HTML string, a reference to
-the table element works with the SheetJS DOM methods after patching the object.
+DenoDOM provides a DOM framework for Deno. For the tested version (`0.1.38`),
+the following patches were needed:
+
+- TABLE `rows` property (explained above)
+- TR `cells` property (explained above)
This example fetches [a sample table](pathname:///dom/SheetJSTable.html):
@@ -220,11 +281,11 @@ const workbook = XLSX.utils.table_to_book(tbl);
XLSX.writeFile(workbook, "SheetJSDenoDOM.xlsx");`}
-Complete Demo (click to hide)
+Complete Demo (click to show)
:::note
-This demo was last tested on 2023 May 18 against DenoDOM `0.1.38`
+This demo was last tested on 2023 September 10 against DenoDOM `0.1.38`
:::
@@ -238,4 +299,8 @@ deno run --allow-net --allow-write SheetJSDenoDOM.ts
The script will create a file `SheetJSDenoDOM.xlsx` that can be opened.
-
\ No newline at end of file
+
+
+[^1]: See ["HTML Table Input" in "Utilities"](/docs/api/utilities/html#html-table-input)
+[^2]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/book) for more details.
+[^3]: See ["Workbook Object" in "SheetJS Data Model"](/docs/csf/book) for more details.
diff --git a/docz/docs/03-demos/05-mobile/04-ionic.md b/docz/docs/03-demos/05-mobile/04-ionic.md
index 8369cbb..c5d7b37 100644
--- a/docz/docs/03-demos/05-mobile/04-ionic.md
+++ b/docz/docs/03-demos/05-mobile/04-ionic.md
@@ -1,5 +1,7 @@
---
-title: Ionic
+title: Data Conduction in Ionic Apps
+sidebar_label: Ionic
+description: Build data-intensive mobile apps with Ionic and Cordova. Seamlessly integrate spreadsheets into your app using SheetJS. Let data in your Excel spreadsheets shine.
pagination_prev: demos/static/index
pagination_next: demos/desktop/index
sidebar_position: 4
@@ -10,10 +12,17 @@ sidebar_custom_props:
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
-The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
-from the main entrypoint or any script in the project.
+[Ionic](https://ionicframework.com/) is a mobile app framework for building iOS
+and Android apps with the Cordova platform.
-The "Complete Example" creates an app that looks like the screenshots below:
+[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
+data from spreadsheets.
+
+This demo uses Ionic and SheetJS to process data and generate spreadsheets.
+We'll explore how to load SheetJS in an Ionic app and use Ionic APIs and plugins
+to extract data from, and write data to, spreadsheet files on the device.
+
+The ["Demo"](#demo) creates an app that looks like the screenshots below:
iOS |
@@ -28,6 +37,13 @@ The "Complete Example" creates an app that looks like the screenshots below:
+:::info pass
+
+This demo covers Ionic apps using the Cordova platform.
+
+The [CapacitorJS demo](/docs/demos/mobile/capacitor) covers CapacitorJS apps.
+
+:::
:::warning Telemetry
@@ -57,18 +73,36 @@ npx @capacitor/cli telemetry
## Integration Details
-:::caution pass
+The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
+imported from the main entrypoint or any script in the project.
-The latest version of Ionic uses CapacitorJS. These notes are for Cordova apps.
-The [CapacitorJS demo](/docs/demos/mobile/capacitor) covers CapacitorJS apps.
+### Internal State
-:::
+The ["Angular" demo](/docs/demos/frontend/angular) discusses a number of state
+representations and preview strategies.
-### Angular
+For this demo, the internal state is an "array of arrays"[^1] (`any[][]`):
-`Array>` neatly maps to a table with `ngFor`:
+```ts title="Array of Arrays state"
+import { Component } from '@angular/core';
+type AOA = any[][];
-```html
+@Component({...})
+export class SheetJSTablePage {
+ data: AOA = [
+ ["S", "h", "e", "e", "t", "J", "S"],
+ [ 5, 4, 3, 3, 7, 9, 5]
+ ];
+ // ...
+}
+```
+
+### Displaying Data
+
+`ion-grid`[^2] is a display grid component. The Angular `ngFor` directive[^3]
+simplifies iteration over the array of arrays:
+
+```html title="Template for displaying an array of arrays"
@@ -78,29 +112,62 @@ The [CapacitorJS demo](/docs/demos/mobile/capacitor) covers CapacitorJS apps.
```
-### Cordova
+### File Operations
-`@ionic-native/file` reads and writes files on devices.
+`@awesome-cordova-plugins/file` reads and writes files on devices.
-_Reading Files_
+:::info pass
-`readAsArrayBuffer` returns `ArrayBuffer` objects suitable for `array` type:
+The plugins in the `@ionic-native` scope have been deprecated. The community
+modules in the `@awesome-cordova-plugins` scope should be used.
+
+:::
+
+#### Reading Files
+
+`this.file.readAsArrayBuffer` reads file data from a specified URL and resolves
+to `ArrayBuffer` objects.
+
+These objects can be parsed with the SheetJS `read` method[^4]. The SheetJS
+`sheet_to_json` method[^5] with the option `header: 1` generates an array of
+arrays which can be assigned to the page state:
```ts
-/* read a workbook */
+/* read a workbook file */
const ab: ArrayBuffer = await this.file.readAsArrayBuffer(url, filename);
+/* parse */
const wb: XLSX.WorkBook = XLSX.read(ab, {type: 'array'});
+/* generate an array of arrays from the first worksheet */
+const ws: XLSX.WorkSheet = wb.SheetNames[wb.Sheets[0]];
+const aoa: AOA = XLSX.utils.sheet_to_json(ws, {header: 1});
+/* update state */
+this.data = aoa;
```
-_Writing Files_
+#### Writing Files
-`array` type can be converted to blobs that can be exported with `writeFile`:
+`this.file.writeFile` writes file data stored in `Blob` objects to the device.
+From the array of arrays, the SheetJS `aoa_to_sheet` method[^6] generates a
+worksheet object. The `book_new` and `book_append_sheet` helpers[^7] generate a
+workbook object. The SheetJS `write` method[^8] with the option `type: "array"`
+will generate an `ArrayBuffer`, from which a `Blob` can be created:
```ts
-/* write a workbook */
-const wbout: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
-let blob = new Blob([wbout], {type: 'application/octet-stream'});
+/* generate worksheet */
+const ws: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet(this.data);
+
+/* generate workbook and add the worksheet */
+const wb: XLSX.WorkBook = XLSX.utils.book_new();
+XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
+
+/* write XLSX to ArrayBuffer */
+const ab: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
+
+/* generate Blob */
+let blob = new Blob([ab], {type: 'application/octet-stream'});
+
+/* write Blob to device */
this.file.writeFile(url, filename, blob, {replace: true});
```
@@ -108,47 +175,72 @@ this.file.writeFile(url, filename, blob, {replace: true});
:::note
-This demo was tested on an Intel Mac on 2023 March 28 with Cordova.
-The file integration uses `@ionic-native/file` version `5.36.0`.
+The project was last tested in 2023 September 10. `ionic info` showed:
-The iOS simulator runs iOS 15.5 on an iPhone SE (3rd Generation).
+- Ionic: `@ionic/angular 7.3.3`, `@ionic/angular-toolkit 9.0.0`
+- Cordova: `cordova-lib@12.0.1`, `android 12.0.1, ios 7.0.1`
-The Android simulator runs Android 12.0 (S) API 31 on a Pixel 3.
+The file integration uses `@awesome-cordova-plugins/file` version `6.4.0`.
+
+The iOS demo was last tested on 2023 September 10 on an emulated iPhone SE
+(3rd generation) + iOS 16.4
+
+The Android demo was last tested on 2023 September 10 on an emulated Pixel 3 +
+Android 13 ("Tiramisu") API 33.
:::
+The app in this demo will display data in a table.
+
+On load, a [test file](https://sheetjs.com/pres.numbers) will be processed.
+
+When a document is selected with the file picker, it will be processed and the
+table will refresh to show the contents.
+
+"Import Data" will attempt to read `SheetJSIonic.xlsx` from a known location. An
+alert will display the expected location.
+
+"Export Data" will attempt to export the table data to `SheetJSIonic.xlsx` in a
+known location. After writing, an alert will display the location of the file.
+
+
+### Platform Setup
+
0) Disable telemetry as noted in the warning.
-Install required global dependencies:
+1) Follow the official instructions for iOS and Android development[^9].
+
+2) Install required global dependencies:
```bash
npm i -g cordova-res @angular/cli native-run @ionic/cli
```
-Follow the [React Native demo](/docs/demos/mobile/reactnative) to ensure iOS and Android sims are ready.
+### Base Project
-
-1) Create a new project:
+3) Create a new project:
```bash
ionic start SheetJSIonic blank --type angular --cordova --quiet --no-git --no-link --confirm
```
+When asked to select `NgModules` or `Standalone Components`, select `NgModules`
+
If a prompt asks to confirm Cordova use, enter `Yes` to continue.
If a prompt asks about creating an Ionic account, enter `N` to opt out.
-2) Set up Cordova:
+4) Set up Cordova:
```bash
cd SheetJSIonic
ionic cordova plugin add cordova-plugin-file
ionic cordova platform add ios --confirm
ionic cordova platform add android --confirm
-npm i --save @ionic-native/core @ionic-native/file @ionic/cordova-builders
+npm i --save @awesome-cordova-plugins/core @awesome-cordova-plugins/file @ionic/cordova-builders
```
-:::note
+:::note pass
If `cordova-plugin-file` is added before the platforms, installation may fail:
@@ -165,9 +257,9 @@ ionic cordova platform add ios --confirm
:::
-:::caution
+:::caution pass
-If the `npm i` fails due to `rxjs` resolution, add the highlighted lines
+If the `npm i` step fails due to `rxjs` resolution, add the highlighted lines
to `package.json` to force a resolution:
```js title="package.json"
@@ -180,24 +272,26 @@ to `package.json` to force a resolution:
"dependencies": {
```
+Note that the required `rxjs` version will be displayed in the error log.
+
After adding the lines, the `npm i` command will succeed.
:::
-3) Install dependencies:
+5) Install dependencies:
{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
-4) Add `@ionic-native/file` to the module. Differences highlighted below:
+6) Add `@awesome-cordova-plugins/file` to the module. Differences highlighted below:
```ts title="src/app/app.module.ts"
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
// highlight-next-line
-import { File } from '@ionic-native/file/ngx';
+import { File } from '@awesome-cordova-plugins/file/ngx';
@NgModule({
declarations: [AppComponent],
@@ -210,19 +304,41 @@ import { File } from '@ionic-native/file/ngx';
export class AppModule {}
```
-5) Download [`home.page.ts`](pathname:///ionic/home.page.ts) and replace:
+7) Download [`home.page.ts`](pathname:///ionic/home.page.ts) and replace:
```bash
curl -o src/app/home/home.page.ts -L https://docs.sheetjs.com/ionic/home.page.ts
```
-**iOS Testing**
+### iOS
+
+8) Enable file sharing and make the documents folder visible in the iOS app.
+Add the following lines to `platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist`:
+
+```xml title="platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist (add to file)"
+
+
+
+ UIFileSharingEnabled
+
+ LSSupportsOpeningDocumentsInPlace
+
+
+ CFBundleDevelopmentRegion
+```
+
+(The root element of the document is `plist` and it contains one `dict` child)
+
+9) Build the app and start the simulator
```bash
ionic cordova emulate ios
```
-:::caution
+When the app is loaded, a list of Presidents should be displayed. This list is
+dynamically generated by fetching and parsing a test file.
+
+:::caution pass
In some test runs, the `cordova build ios --emulator` step failed with error:
@@ -239,13 +355,54 @@ npm i --save cordova-ios
:::
-**Android Testing**
+:::info pass
+
+In the most recent test, the `native-run ios` command failed with
+
+```
+[native-run] ERR_UNKNOWN: Path 'platforms/ios/build/emulator/SheetJSIonic.app' not found
+```
+
+Inspecting `platforms/ios/build/`, the actual folder name was:
+
+```bash
+% ls platforms/ios/build
+#highlight-next-line
+Debug-iphonesimulator
+```
+
+The iOS simulator can be launched manually:
+
+```bash
+native-run ios --app platforms/ios/build/Debug-iphonesimulator/SheetJSIonic.app --virtual
+```
+
+:::
+
+### Android
+
+10) Enable file reading and writing in the Android app.
+
+Edit `platforms/android/app/src/main/AndroidManifest.xml` and add the following
+two lines before the `application` tag:
+
+```xml title="platforms/android/app/src/main/AndroidManifest.xml (add to file)"
+
+
+```
+
+In the `application` tag, add the attribute `android:requestLegacyExternalStorage="true"`.
+
+11) Build the app and start the emulator
```bash
ionic cordova emulate android
```
-:::caution
+When the app is loaded, a list of Presidents should be displayed. This list is
+dynamically generated by fetching and parsing a test file.
+
+:::caution pass
In some test runs, `cordova build android --emulator` step failed with error:
@@ -260,3 +417,40 @@ npm i --save cordova-android
```
:::
+
+:::caution pass
+
+In some tests, the build failed with a Gradle error:
+
+```
+Could not find an installed version of Gradle either in Android Studio,
+or on your system to install the gradle wrapper. Please include gradle
+in your path or install Android Studio
+```
+
+On macOS, this issue was resolved by installing gradle with Homebrew manager:
+
+```bash
+brew install gradle
+```
+
+:::
+
+:::warning pass
+
+When the demo was last tested on Android, reading files worked as expected.
+However, the generated files were not externally visible from the Files app.
+
+**This is a known bug with Android SDK 33 and the underlying file plugins!**
+
+:::
+
+[^1]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays)
+[^2]: See [`ion-grid`](https://ionicframework.com/docs/api/grid) in the Ionic documentation.
+[^3]: See [`ngFor`](https://angular.io/api/common/NgFor) in the Angular documentation.
+[^4]: See [`read` in "Reading Files"](/docs/api/parse-options)
+[^5]: See ["Array Output" in "Utility Functions"](/docs/api/utilities/array#array-output)
+[^6]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
+[^7]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
+[^8]: See [`write` in "Writing Files"](/docs/api/write-options)
+[^9]: See ["Developing for iOS"](https://ionicframework.com/docs/v6/developing/ios) and ["Developing for Android"](https://ionicframework.com/docs/v6/developing/android) in the v6 Ionic framework documentation.
\ No newline at end of file
diff --git a/docz/docs/03-demos/05-mobile/05-capacitor.md b/docz/docs/03-demos/05-mobile/05-capacitor.md
index d7252b3..bd26b1c 100644
--- a/docz/docs/03-demos/05-mobile/05-capacitor.md
+++ b/docz/docs/03-demos/05-mobile/05-capacitor.md
@@ -200,7 +200,7 @@ after the `Permissions` comment:
-
+
diff --git a/docz/static/dom/SheetJSDOM.js b/docz/static/dom/SheetJSDOM.js
new file mode 100644
index 0000000..7120a54
--- /dev/null
+++ b/docz/static/dom/SheetJSDOM.js
@@ -0,0 +1,11 @@
+const XLSX = require("xlsx");
+const { readFileSync } = require("fs");
+const { JSDOM } = require("jsdom");
+
+/* obtain HTML string. This example reads from SheetJSTable.html */
+const html_str = readFileSync("SheetJSTable.html", "utf8");
+/* get first TABLE element */
+const doc = new JSDOM(html_str).window.document.querySelector("table");
+/* generate workbook */
+const workbook = XLSX.utils.table_to_book(doc);
+XLSX.writeFile(workbook, "SheetJSDOM.xlsx");
\ No newline at end of file
diff --git a/docz/static/dom/SheetJSHappyDOM.js b/docz/static/dom/SheetJSHappyDOM.js
new file mode 100644
index 0000000..5c1c6e0
--- /dev/null
+++ b/docz/static/dom/SheetJSHappyDOM.js
@@ -0,0 +1,23 @@
+const XLSX = require("xlsx");
+const { readFileSync } = require("fs");
+const { Window } = require("happy-dom");
+
+/* obtain HTML string. This example reads from SheetJSTable.html */
+const html_str = readFileSync("SheetJSTable.html", "utf8");
+
+/* get table element */
+const window = new Window({
+ url: "https://localhost:8080",
+ width: 1024,
+ height: 768
+});
+window.document.body.innerHTML = html_str;
+const tbl = window.document.body.getElementsByTagName("table")[0];
+
+/* add `rows` and `cells` properties */
+tbl.rows = Array.from(tbl.getElementsByTagName("tr"));
+tbl.rows.forEach(row => row.cells = Array.from(row.getElementsByTagName("td")))
+
+/* generate workbook */
+const workbook = XLSX.utils.table_to_book(tbl);
+XLSX.writeFile(workbook, "SheetJSHappyDOM.xlsx");
\ No newline at end of file
diff --git a/docz/static/dom/SheetJSXMLDOM.js b/docz/static/dom/SheetJSXMLDOM.js
new file mode 100644
index 0000000..dc53342
--- /dev/null
+++ b/docz/static/dom/SheetJSXMLDOM.js
@@ -0,0 +1,20 @@
+const XLSX = require("xlsx");
+const { DOMParser, XMLSerializer } = require("@xmldom/xmldom");
+
+(async() => {
+const text = await (await fetch('https://docs.sheetjs.com/dom/SheetJSTable.html')).text();
+const doc = new DOMParser().parseFromString( text, "text/html");
+const tbl = doc.getElementsByTagName("table")[0];
+
+/* patch XMLDOM */
+tbl.rows = Array.from(tbl.getElementsByTagName("tr"));
+tbl.rows.forEach(row => row.cells = Array.from(row.getElementsByTagName("td")));
+Object.defineProperty(tbl.__proto__, "innerHTML", { get: function() {
+ var outerHTML = new XMLSerializer().serializeToString(this);
+ if(outerHTML.match(/]*(("[^"]*"|'[^']*')[^"'>]*)*>/, "");
+}});
+
+const workbook = XLSX.utils.table_to_book(tbl);
+XLSX.writeFile(workbook, "SheetJSXMLDOM.xlsx");
+})();
\ No newline at end of file
diff --git a/docz/static/flutter/ios.png b/docz/static/flutter/ios.png
index c5ba8b7..9b5e8c3 100644
Binary files a/docz/static/flutter/ios.png and b/docz/static/flutter/ios.png differ
diff --git a/docz/static/ionic/home.page.ts b/docz/static/ionic/home.page.ts
index 1be82e7..83e254d 100644
--- a/docz/static/ionic/home.page.ts
+++ b/docz/static/ionic/home.page.ts
@@ -1,7 +1,7 @@
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
/* vim: set ts=2: */
import { Component, OnInit } from '@angular/core';
-import { File } from '@ionic-native/file/ngx';
+import { File } from '@awesome-cordova-plugins/file/ngx';
import * as XLSX from 'xlsx';
type AOA = any[][];
@@ -42,7 +42,7 @@ type AOA = any[][];
})
export class HomePage implements OnInit {
- data: any[][] = [[1,2,3],[4,5,6]];
+ data: AOA = [[1,2,3],[4,5,6]];
constructor(public file: File) {}
async ngOnInit(): Promise {