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.
 
 
 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.
 
 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.
+
+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.
 
-
 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.
 
-
   | 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 {