gtm
@ -47,22 +47,22 @@ export default function BasicTable() {
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
This demo was last run on 2023 May 11 against Material UI 5.13.0 paired with
 | 
			
		||||
Emotion 11.11.0
 | 
			
		||||
This demo was last run on 2023 October 12 against Material UI 5.14.13 paired
 | 
			
		||||
with Emotion 11.11.1
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
1) Create a new TypeScript `create-react-app` app:
 | 
			
		||||
1) Create a new app using `vite`:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npx create-react-app sheetjs-mui --template typescript
 | 
			
		||||
npm create vite@latest sheetjs-mui -- --template react-ts
 | 
			
		||||
cd sheetjs-mui
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
2) Install dependencies:
 | 
			
		||||
 | 
			
		||||
<CodeBlock language="bash">{`\
 | 
			
		||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material@5.13.0 @emotion/react@11.11.0 @emotion/styled@11.11.0`}
 | 
			
		||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/material@5.14.13 @emotion/react@11.11.1 @emotion/styled@11.11.0`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
 | 
			
		||||
3) Download [`App.tsx`](pathname:///mui/table/App.tsx) and replace `src/App.tsx`.
 | 
			
		||||
@ -74,7 +74,7 @@ curl -L -o src/App.tsx https://docs.sheetjs.com/mui/table/App.tsx
 | 
			
		||||
4) Start the development server:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npm start
 | 
			
		||||
npm run dev
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The script should open the live demo in a web browser. Click the "Export" button
 | 
			
		||||
@ -194,22 +194,22 @@ export default function App() {
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
This demo was last run on 2023 May 11 against MUI data grid 6.3.1 paired with
 | 
			
		||||
Emotion 11.11.0
 | 
			
		||||
This demo was last run on 2023 October 12 against MUI data grid 6.3.1 paired
 | 
			
		||||
with Emotion 11.11.1
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
1) Create a new TypeScript `create-react-app` app:
 | 
			
		||||
1) Create a new app using `vite`:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npx create-react-app sheetjs-muidg --template typescript
 | 
			
		||||
npm create vite@latest sheetjs-muidg -- --template react-ts
 | 
			
		||||
cd sheetjs-muidg
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
2) Install dependencies:
 | 
			
		||||
 | 
			
		||||
<CodeBlock language="bash">{`\
 | 
			
		||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid @emotion/react @emotion/styled`}
 | 
			
		||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @mui/x-data-grid@6.16.1 @emotion/react@11.11.1 @emotion/styled@11.11.0`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
 | 
			
		||||
3) Download [`App.tsx`](pathname:///mui/dg/App.tsx) and replace `src/App.tsx`.
 | 
			
		||||
@ -221,7 +221,7 @@ curl -L -o src/App.tsx https://docs.sheetjs.com/mui/dg/App.tsx
 | 
			
		||||
4) Start the development server:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npm start
 | 
			
		||||
npm run dev
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
When the page loads, it will fetch and process <https://sheetjs.com/pres.numbers>
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ download and parse remote workbook files.
 | 
			
		||||
- ["Local Files"](#local-files) uses native libraries to read and write files on
 | 
			
		||||
the device.
 | 
			
		||||
 | 
			
		||||
The "Complete Example" creates an app that looks like the screenshots below:
 | 
			
		||||
The "Local Files" example creates an app that looks like the screenshots below:
 | 
			
		||||
 | 
			
		||||
<table><thead><tr>
 | 
			
		||||
  <th><a href="#demo">iOS</a></th>
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
---
 | 
			
		||||
title: NativeScript
 | 
			
		||||
title: Native Sheets in NativeScript
 | 
			
		||||
sidebar_label: NativeScript
 | 
			
		||||
pagination_prev: demos/static/index
 | 
			
		||||
pagination_next: demos/desktop/index
 | 
			
		||||
sidebar_position: 2
 | 
			
		||||
@ -10,14 +11,25 @@ sidebar_custom_props:
 | 
			
		||||
import current from '/version.js';
 | 
			
		||||
import CodeBlock from '@theme/CodeBlock';
 | 
			
		||||
 | 
			
		||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
 | 
			
		||||
imported from any component or script in the app.
 | 
			
		||||
export const g = {style: {color:"green"}};
 | 
			
		||||
export const r = {style: {color:"red"}};
 | 
			
		||||
export const y = {style: {color:"yellow"}};
 | 
			
		||||
 | 
			
		||||
[NativeScript](https://nativescript.org/) is a mobile app framework. It builds
 | 
			
		||||
iOS and Android apps that use JavaScript for describing layouts and events.
 | 
			
		||||
 | 
			
		||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
 | 
			
		||||
data from spreadsheets.
 | 
			
		||||
 | 
			
		||||
This demo uses NativeScript and SheetJS to process and generate spreadsheets.
 | 
			
		||||
We'll explore how to load SheetJS in a NativeScript app; parse and generate
 | 
			
		||||
spreadsheets stored on the device; and fetch and parse remote files.
 | 
			
		||||
 | 
			
		||||
The "Complete Example" creates an app that looks like the screenshots below:
 | 
			
		||||
 | 
			
		||||
<table><thead><tr>
 | 
			
		||||
  <th><a href="#demo">iOS</a></th>
 | 
			
		||||
  <th><a href="#demo">Android</a></th>
 | 
			
		||||
  <th><a href="#complete-example">iOS</a></th>
 | 
			
		||||
  <th><a href="#complete-example">Android</a></th>
 | 
			
		||||
</tr></thead><tbody><tr><td>
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
@ -28,11 +40,39 @@ The "Complete Example" creates an app that looks like the screenshots below:
 | 
			
		||||
 | 
			
		||||
</td></tr></tbody></table>
 | 
			
		||||
 | 
			
		||||
## Integration Details
 | 
			
		||||
:::info pass
 | 
			
		||||
 | 
			
		||||
The discussion covers the NativeScript + Angular integration.  Familiarity with
 | 
			
		||||
Angular and TypeScript is assumed.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
:::warning Telemetry
 | 
			
		||||
 | 
			
		||||
Before starting this demo, manually disable telemetry.
 | 
			
		||||
 | 
			
		||||
NativeScript 8.6.0 split the telemetry into two parts: "usage" and "error". Both
 | 
			
		||||
must be disabled separately:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npx -p nativescript ns usage-reporting disable
 | 
			
		||||
npx -p nativescript ns error-reporting disable
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To verify telemetry was disabled:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npx -p nativescript ns usage-reporting status
 | 
			
		||||
npx -p nativescript ns error-reporting status
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Integration Details
 | 
			
		||||
 | 
			
		||||
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
 | 
			
		||||
imported from any component or script in the app.
 | 
			
		||||
 | 
			
		||||
The `@nativescript/core/file-system` package provides classes for file access.
 | 
			
		||||
The `File` class does not support binary data, but the file access singleton
 | 
			
		||||
from `@nativescript/core` does support reading and writing `ArrayBuffer`.
 | 
			
		||||
@ -51,7 +91,8 @@ function get_url_for_filename(filename: string): string {
 | 
			
		||||
 | 
			
		||||
### Reading Local Files
 | 
			
		||||
 | 
			
		||||
`getFileAccess().readBufferAsync` can read data:
 | 
			
		||||
`getFileAccess().readBufferAsync` can read data into an `ArrayBuffer` object.
 | 
			
		||||
The SheetJS `read` method[^1] can parse this data into a workbook object.[^2]
 | 
			
		||||
 | 
			
		||||
```ts
 | 
			
		||||
import { getFileAccess } from '@nativescript/core';
 | 
			
		||||
@ -67,10 +108,27 @@ const ab: ArrayBuffer = await getFileAccess().readBufferAsync(url);
 | 
			
		||||
const wb = read(ab);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
After parsing into a workbook, the `sheet_to_json`[^3] method can generate row
 | 
			
		||||
data objects:
 | 
			
		||||
 | 
			
		||||
```ts
 | 
			
		||||
import { utils } from 'xlsx';
 | 
			
		||||
 | 
			
		||||
/* grab first sheet */
 | 
			
		||||
const wsname: string = wb.SheetNames[0];
 | 
			
		||||
const ws = wb.Sheets[wsname];
 | 
			
		||||
 | 
			
		||||
/* generate array of row objects */
 | 
			
		||||
const data = utils.sheet_to_json(ws);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Writing Local Files
 | 
			
		||||
 | 
			
		||||
`getFileAccess().writeBufferAsync` can write data. iOS supports `Uint8Array`
 | 
			
		||||
directly but Android requires a true array of numbers:
 | 
			
		||||
The SheetJS `write` method[^4] with the option `type: "binary"` will generate
 | 
			
		||||
`Uint8Array` objects. `getFileAccess().writeBufferAsync` can write data from a
 | 
			
		||||
`Uint8Array` object to the device.
 | 
			
		||||
 | 
			
		||||
iOS supports `Uint8Array` directly but Android requires a true array of numbers:
 | 
			
		||||
 | 
			
		||||
```ts
 | 
			
		||||
import { getFileAccess } from '@nativescript/core';
 | 
			
		||||
@ -86,10 +144,15 @@ const u8: Uint8Array = write(wb, { bookType: 'xls', type: 'binary' });
 | 
			
		||||
await getFileAccess().writeBufferAsync(url, global.isAndroid ? (Array.from(u8) as any) : u8);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A worksheet can be generated from an array of row objects with the SheetJS
 | 
			
		||||
`json_to_sheet` method[^5]. After generating an array, the `book_new` and
 | 
			
		||||
`book_append_sheet` methods[^6] can create the workbook.
 | 
			
		||||
 | 
			
		||||
### Fetching Remote Files
 | 
			
		||||
 | 
			
		||||
`getFile` from `@nativescript/core/http` can download files. After storing the
 | 
			
		||||
file in a temporary folder, `getFileAccess().readBufferAsync` can read the data:
 | 
			
		||||
file in a temporary folder, `getFileAccess().readBufferAsync` can read the data
 | 
			
		||||
and the SheetJS `read` method[^7] can parse the file:
 | 
			
		||||
 | 
			
		||||
```ts
 | 
			
		||||
import { knownFolders, path, getFileAccess } from '@nativescript/core'
 | 
			
		||||
@ -109,34 +172,120 @@ const ab: ArrayBuffer = await getFileAccess().readBufferAsync(file.path);
 | 
			
		||||
const wb = read(ab);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Demo
 | 
			
		||||
## Complete Example
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
This demo was tested on an Intel Mac on 2023 May 21.  NativeScript version
 | 
			
		||||
(as verified with `ns --version`) is `8.5.3`.
 | 
			
		||||
The project was last tested on 2023 October 12. NativeScript version
 | 
			
		||||
(as verified with `npx -p nativescript ns --version`) was `8.6.0`.
 | 
			
		||||
 | 
			
		||||
The iOS simulator runs iOS 16.2 on an iPhone 14 Pro Max.
 | 
			
		||||
The iOS demo was last tested on 2023-10-12 with `@nativescript/ios`
 | 
			
		||||
version `8.6.1` on an emulated iPhone 15 Pro Max + iOS 17.0
 | 
			
		||||
 | 
			
		||||
The Android simulator runs Android 12.0 (S) API 31 on a Pixel 3.
 | 
			
		||||
The Android demo was last tested on 2023-10-12 with `@nativescript/android`
 | 
			
		||||
version `8.6.2` on an emulated Pixel 3 + Android 13 ("Tiramisu") API 33.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
0) Follow the official Environment Setup instructions
 | 
			
		||||
### Platform Configuration
 | 
			
		||||
 | 
			
		||||
0) Disable telemetry:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npx -p nativescript ns usage-reporting disable
 | 
			
		||||
npx -p nativescript ns error-reporting disable
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
1) Follow the official Environment Setup instructions[^8].
 | 
			
		||||
 | 
			
		||||
:::caution pass
 | 
			
		||||
 | 
			
		||||
When the demo was last tested, the latest version of the Android API was 34.
 | 
			
		||||
NativeScript did not support that API level. The exact error message from
 | 
			
		||||
`npx -p nativescript ns doctor ios` clearly stated supported versions:
 | 
			
		||||
 | 
			
		||||
(x is red, body text is yellow)
 | 
			
		||||
```
 | 
			
		||||
✖ No compatible version of the Android SDK Build-tools are installed on your system. You can install any version in the following range: '>=23 <=33'.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The SDK Platform `Android 13.0 ("Tiramisu")` was compatible with NativeScript.
 | 
			
		||||
Until NativeScript properly supports API level 34, "Tiramisu" must be used.
 | 
			
		||||
This requires installing the following packages from Android Studio:
 | 
			
		||||
 | 
			
		||||
- `Android 13.0 ("Tiramisu")` API Level `33`
 | 
			
		||||
- `Android SDK Build-Tools` Version `33.0.2`
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
2) Test the local system configuration for Android development:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npx -p nativescript ns doctor android
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In the last macOS test, the following output was displayed:
 | 
			
		||||
 | 
			
		||||
<details open><summary><b>Expected output</b> (click to hide)</summary>
 | 
			
		||||
 | 
			
		||||
<pre>
 | 
			
		||||
<span {...g}>✔</span> Getting environment information{'\n'}
 | 
			
		||||
{'\n'}
 | 
			
		||||
<b>No issues were detected.</b>{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Your ANDROID_HOME environment variable is set and points to correct directory.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Your adb from the Android SDK is correctly installed.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> The Android SDK is installed.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> A compatible Android SDK for compilation is found.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Javac is installed and is configured properly.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> The Java Development Kit (JDK) is installed and is configured properly.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Getting NativeScript components versions information...{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Component nativescript has 8.6.0 version and is up to date.
 | 
			
		||||
</pre>
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
3) Test the local system configuration for iOS development (macOS only):
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npx -p nativescript ns doctor ios
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In the last macOS test, the following output was displayed:
 | 
			
		||||
 | 
			
		||||
<details open><summary><b>Expected output</b> (click to hide)</summary>
 | 
			
		||||
 | 
			
		||||
<pre>
 | 
			
		||||
<span {...g}>✔</span> Getting environment information{'\n'}
 | 
			
		||||
{'\n'}
 | 
			
		||||
No issues were detected.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Xcode is installed and is configured properly.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> xcodeproj is installed and is configured properly.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> CocoaPods are installed.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> CocoaPods update is not required.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> CocoaPods are configured properly.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Your current CocoaPods version is newer than 1.0.0.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Python installed and configured correctly.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> The Python 'six' package is found.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Xcode version 15.0.0 satisfies minimum required version 10.{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Getting NativeScript components versions information...{'\n'}
 | 
			
		||||
<span {...g}>✔</span> Component nativescript has 8.6.0 version and is up to date.
 | 
			
		||||
</pre>
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
### Base Project
 | 
			
		||||
 | 
			
		||||
1) Create a skeleton NativeScript + Angular app:
 | 
			
		||||
4) Create a skeleton NativeScript + Angular app:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
ns create SheetJSNS --ng
 | 
			
		||||
npx -p nativescript ns create SheetJSNS --ng
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
2) Launch the app in the iOS simulator to verify that the demo built properly:
 | 
			
		||||
5) Launch the app in the android simulator to verify the app:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
cd SheetJSNS
 | 
			
		||||
ns run ios
 | 
			
		||||
npx -p nativescript ns run android
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
(this may take a while)
 | 
			
		||||
@ -144,16 +293,22 @@ ns run ios
 | 
			
		||||
Once the simulator launches and the test app is displayed, end the script by
 | 
			
		||||
selecting the terminal and entering the key sequence `CTRL + C`
 | 
			
		||||
 | 
			
		||||
3) From the project folder, install the library:
 | 
			
		||||
6) From the project folder, install the library:
 | 
			
		||||
 | 
			
		||||
<CodeBlock language="bash">{`\
 | 
			
		||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
 | 
			
		||||
4) To confirm the library was loaded, change the title to show the version.  The
 | 
			
		||||
differences are highlighted.
 | 
			
		||||
### Add SheetJS
 | 
			
		||||
 | 
			
		||||
`src/app/item/items.component.ts` should import the version string:
 | 
			
		||||
:::note pass
 | 
			
		||||
 | 
			
		||||
The goal of this section is to display the SheetJS library version number.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
7) Edit `src/app/item/items.component.ts` so that the component imports the
 | 
			
		||||
SheetJS version string and adds it to a `version` variable in the component:
 | 
			
		||||
 | 
			
		||||
```ts title="src/app/item/items.component.ts"
 | 
			
		||||
// highlight-next-line
 | 
			
		||||
@ -171,7 +326,8 @@ export class ItemsComponent implements OnInit {
 | 
			
		||||
// ...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`src/app/item/items.component.html` should use the version in the title:
 | 
			
		||||
8) Edit the template `src/app/item/items.component.html` to reference `version`
 | 
			
		||||
in the title of the action bar:
 | 
			
		||||
 | 
			
		||||
```xml title="src/app/item/items.component.html"
 | 
			
		||||
<!-- highlight-next-line -->
 | 
			
		||||
@ -181,13 +337,19 @@ export class ItemsComponent implements OnInit {
 | 
			
		||||
<!-- ... -->
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Relaunch the app with `ns run ios` and the title bar should show the version.
 | 
			
		||||
9) Relaunch the app in the Android simulator:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
```bash
 | 
			
		||||
npx -p nativescript ns run android
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The title bar should show the version.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### Local Files
 | 
			
		||||
 | 
			
		||||
5) Add the Import and Export buttons to the template:
 | 
			
		||||
10) Add the Import and Export buttons to the template:
 | 
			
		||||
 | 
			
		||||
```xml title="src/app/item/items.component.html"
 | 
			
		||||
<ActionBar [title]="version"></ActionBar>
 | 
			
		||||
@ -206,6 +368,8 @@ Relaunch the app with `ns run ios` and the title bar should show the version.
 | 
			
		||||
</StackLayout>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
11) Add the `import` and `export` methods in the component script:
 | 
			
		||||
 | 
			
		||||
```ts title="src/app/item/items.component.ts"
 | 
			
		||||
// highlight-start
 | 
			
		||||
import { version, utils, read, write } from 'xlsx';
 | 
			
		||||
@ -250,11 +414,11 @@ export class ItemsComponent implements OnInit {
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Restart the app process and two buttons should show up at the top:
 | 
			
		||||
12) Restart the app process. Two buttons should show up at the top:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
6) Implement import and export by adding the highlighted lines:
 | 
			
		||||
13) Implement import and export by adding the highlighted lines:
 | 
			
		||||
 | 
			
		||||
```ts title="src/app/item/items.component.ts"
 | 
			
		||||
  /* Import button */
 | 
			
		||||
@ -306,51 +470,33 @@ Restart the app process and two buttons should show up at the top:
 | 
			
		||||
  }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### iOS
 | 
			
		||||
 | 
			
		||||
Relaunch the app with `ns run ios`
 | 
			
		||||
 | 
			
		||||
The app can be tested with the following sequence in the simulator:
 | 
			
		||||
 | 
			
		||||
- Tap "Export File".  A dialog will print where the file was written
 | 
			
		||||
 | 
			
		||||
- Open the file with a spreadsheet editor.
 | 
			
		||||
 | 
			
		||||
After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
id | name       | role
 | 
			
		||||
 0 | SheetJS    | Library
 | 
			
		||||
 1 | Ter Stegen | Goalkeeper
 | 
			
		||||
 3 | Piqué      | Defender
 | 
			
		||||
...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Restart the app after saving the file.
 | 
			
		||||
 | 
			
		||||
- Tap "Import File".  A dialog will print the path of the file that was read.
 | 
			
		||||
  The first item in the list will change:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### Android
 | 
			
		||||
 | 
			
		||||
Launch the app with `ns run android`.  If the app does not automatically launch,
 | 
			
		||||
manually open the `SheetJSNS` app.
 | 
			
		||||
14) Launch the app in the Android Simulator:
 | 
			
		||||
 | 
			
		||||
The app can be tested with the following sequence in the simulator:
 | 
			
		||||
```bash
 | 
			
		||||
npx -p nativescript ns run android
 | 
			
		||||
````
 | 
			
		||||
 | 
			
		||||
- Tap "Export File". A dialog will print where the file was written. Typically
 | 
			
		||||
If the app does not automatically launch, manually open the `SheetJSNS` app.
 | 
			
		||||
 | 
			
		||||
15) Tap "Export File". A dialog will print where the file was written. Typically
 | 
			
		||||
the URL is `/data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls`
 | 
			
		||||
 | 
			
		||||
- Pull the file from the simulator:
 | 
			
		||||
16) Pull the file from the simulator:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
adb root
 | 
			
		||||
adb pull /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls SheetJSNS.xls
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- Open `SheetJSNS.xls` with a spreadsheet editor.
 | 
			
		||||
If the emulator cannot be rooted:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
adb shell "run-as org.nativescript.SheetJSNS cat /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls" > SheetJSNS.xls
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
17) Open `SheetJSNS.xls` with a spreadsheet editor.
 | 
			
		||||
 | 
			
		||||
After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:
 | 
			
		||||
 | 
			
		||||
@ -362,18 +508,53 @@ id | name       | role
 | 
			
		||||
...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- Push the file back to the simulator:
 | 
			
		||||
18) Push the file back to the simulator:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
adb push SheetJSNS.xls /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- Tap "Import File".  A dialog will print the path of the file that was read.
 | 
			
		||||
  The first item in the list will change.
 | 
			
		||||
If the emulator cannot be rooted:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
dd if=SheetJSNS.xls | adb shell "run-as org.nativescript.SheetJSNS dd of=/data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls"
 | 
			
		||||
 ```
 | 
			
		||||
 | 
			
		||||
19) Tap "Import File".  A dialog will print the path of the file that was read.
 | 
			
		||||
The first item in the list will change.
 | 
			
		||||
 | 
			
		||||
### iOS
 | 
			
		||||
 | 
			
		||||
20) Launch the app in the iOS Simulator:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npx -p nativescript ns run ios
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
21) Tap "Export File". A dialog will print where the file was written.
 | 
			
		||||
 | 
			
		||||
22) Open the file with a spreadsheet editor.
 | 
			
		||||
 | 
			
		||||
After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
id | name       | role
 | 
			
		||||
 0 | SheetJS    | Library
 | 
			
		||||
 1 | Ter Stegen | Goalkeeper
 | 
			
		||||
 3 | Piqué      | Defender
 | 
			
		||||
...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
23) Restart the app after saving the file.
 | 
			
		||||
 | 
			
		||||
24) Tap "Import File".  A dialog will print the path of the file that was read.
 | 
			
		||||
The first item in the list will change:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### Fetching Files
 | 
			
		||||
 | 
			
		||||
7) In `src/app/item/items.component.ts`, make `ngOnInit` asynchronous:
 | 
			
		||||
25) In `src/app/item/items.component.ts`, make `ngOnInit` asynchronous:
 | 
			
		||||
 | 
			
		||||
```ts title="src/app/item/items.component.ts"
 | 
			
		||||
  async ngOnInit(): Promise<void> {
 | 
			
		||||
@ -381,7 +562,7 @@ adb push SheetJSNS.xls /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.x
 | 
			
		||||
  }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
8) Replace `item.service.ts` with the following:
 | 
			
		||||
26) Replace `item.service.ts` with the following:
 | 
			
		||||
 | 
			
		||||
```ts title="src/app/item/item.service.ts"
 | 
			
		||||
import { Injectable } from '@angular/core'
 | 
			
		||||
@ -414,4 +595,19 @@ export class ItemService {
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Relaunching the app in iOS or Android simulator should show Presidential data.
 | 
			
		||||
27) Relaunch the app in the Android simulator:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npx -p nativescript ns run android
 | 
			
		||||
````
 | 
			
		||||
 | 
			
		||||
The app should show Presidential data.
 | 
			
		||||
 | 
			
		||||
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
 | 
			
		||||
[^2]: See ["Workbook Object"](/docs/csf/book)
 | 
			
		||||
[^3]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
 | 
			
		||||
[^4]: See [`write` in "Writing Files"](/docs/api/write-options)
 | 
			
		||||
[^5]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
 | 
			
		||||
[^6]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
 | 
			
		||||
[^7]: See [`read` in "Reading Files"](/docs/api/parse-options)
 | 
			
		||||
[^8]: See ["Local setup"](https://docs.nativescript.org/setup/#local-setup) in the NativeScript documentation. For Windows and Linux, follow the "Android" instructions. For macOS, follow both the iOS and Android instructions.
 | 
			
		||||
@ -50,7 +50,7 @@ This demo was tested in the following deployments:
 | 
			
		||||
 | 
			
		||||
| Architecture | Version | Node Target | Date       |
 | 
			
		||||
|:-------------|:--------|:------------|:-----------|
 | 
			
		||||
| `darwin-x64` | `5.8.1` | `18.5.0`    | 2023-05-08 |
 | 
			
		||||
| `darwin-x64` | `5.8.1` | `18.5.0`    | 2023-10-11 |
 | 
			
		||||
| `darwin-arm` | `5.8.1` | `18.5.0`    | 2023-09-25 |
 | 
			
		||||
| `win10-x64`  | `5.8.1` | `18.5.0`    | 2023-10-09 |
 | 
			
		||||
| `win11-arm`  | `5.8.1` | `18.5.0`    | 2023-09-25 |
 | 
			
		||||
@ -72,7 +72,7 @@ This demo was tested in the following deployments:
 | 
			
		||||
 | 
			
		||||
| Architecture | Version | Node Target | Date       |
 | 
			
		||||
|:-------------|:--------|:------------|:-----------|
 | 
			
		||||
| `darwin-x64` | `2.0.1` | `20.1.0`    | 2023-05-08 |
 | 
			
		||||
| `darwin-x64` | `2.1.2` | `20.8.0`    | 2023-10-12 |
 | 
			
		||||
| `darwin-arm` | `2.1.1` | `20.7.0`    | 2023-09-25 |
 | 
			
		||||
| `win10-x64`  | `2.1.2` | `16.20.2`   | 2023-10-09 |
 | 
			
		||||
| `linux-x64`  | `2.1.2` | `20.8.0`    | 2023-10-11 |
 | 
			
		||||
@ -239,7 +239,7 @@ This demo was last tested in the following deployments:
 | 
			
		||||
 | 
			
		||||
| Architecture | V8 Version    | Crate    | Date       |
 | 
			
		||||
|:-------------|:--------------|:---------|:-----------|
 | 
			
		||||
| `darwin-x64` | `11.4.183.2`  | `0.71.2` | 2023-05-22 |
 | 
			
		||||
| `darwin-x64` | `11.8.172.13` | `0.79.2` | 2023-10-12 |
 | 
			
		||||
| `darwin-arm` | `11.4.183.2`  | `0.71.2` | 2023-05-22 |
 | 
			
		||||
| `win10-x64`  | `11.8.172.13` | `0.79.2` | 2023-10-09 |
 | 
			
		||||
| `win11-x64`  | `11.7.439.6`  | `0.75.1` | 2023-08-31 |
 | 
			
		||||
@ -350,7 +350,7 @@ This demo was last tested in the following deployments:
 | 
			
		||||
 | 
			
		||||
| Architecture | Version  | Date       |
 | 
			
		||||
|:-------------|:---------|:-----------|
 | 
			
		||||
| `darwin-x64` | `1.33.2` | 2023-05-08 |
 | 
			
		||||
| `darwin-x64` | `1.37.1` | 2023-10-12 |
 | 
			
		||||
| `darwin-arm` | `1.34.1` | 2023-06-05 |
 | 
			
		||||
| `win10-x64`  | `1.37.1` | 2023-10-09 |
 | 
			
		||||
| `win11-arm`  | `1.37.0` | 2023-09-26 |
 | 
			
		||||
 | 
			
		||||
@ -9,31 +9,59 @@ sidebar_custom_props:
 | 
			
		||||
import current from '/version.js';
 | 
			
		||||
import CodeBlock from '@theme/CodeBlock';
 | 
			
		||||
 | 
			
		||||
MongoDB is a popular document-oriented database engine.
 | 
			
		||||
[MongoDB](https://mongodb.github.io/node-mongodb-native/) is a document-oriented
 | 
			
		||||
database engine.
 | 
			
		||||
 | 
			
		||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
 | 
			
		||||
data from spreadsheets.
 | 
			
		||||
 | 
			
		||||
This demo uses SheetJS to exchange data between spreadsheets and MongoDB. We'll
 | 
			
		||||
explore how to use save tables from a MongoDB collection to spreadsheets and how
 | 
			
		||||
to add data from spreadsheets into a collection.
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
This demo was last tested on 2023 October 12 with MongoDB CE 7.0.2, MongoDB
 | 
			
		||||
connector module 5.7.0 and NodeJS 20.8.0
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Integration Details
 | 
			
		||||
 | 
			
		||||
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
 | 
			
		||||
loaded in NodeJS scripts that use the `mongodb` NodeJS connector library.
 | 
			
		||||
 | 
			
		||||
It is straightforward to treat collections as worksheets.  Each object maps to
 | 
			
		||||
a row in the table.
 | 
			
		||||
 | 
			
		||||
## Integration Details
 | 
			
		||||
 | 
			
		||||
The official NodeJS connector is `mongodb`.
 | 
			
		||||
 | 
			
		||||
#### Importing Data
 | 
			
		||||
 | 
			
		||||
Data stored in an array of objects can be added to MongoDB Collections using
 | 
			
		||||
`Collection#insertMany`.  `sheet_to_json` can generate data from worksheets:
 | 
			
		||||
`Collection#insertMany`[^1]. The SheetJS `sheet_to_json` method[^2] can generate
 | 
			
		||||
data from worksheets:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
/* import data from a worksheet to a collection */
 | 
			
		||||
const aoo = XLSX.utils.sheet_to_json(ws);
 | 
			
		||||
await collection.insertMany(aoo, {ordered: true});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Typically worksheet objects are extracted from workbook objects[^3] generated
 | 
			
		||||
from the SheetJS `read` or `readFile` methods[^4].
 | 
			
		||||
 | 
			
		||||
#### Exporting Data
 | 
			
		||||
 | 
			
		||||
`Collection#find` can pull an array of objects from a Mongo Collection.
 | 
			
		||||
`Collection#find`[^5] can pull an array of objects from a Mongo Collection.
 | 
			
		||||
 | 
			
		||||
The SheetJS `json_to_sheet` method[^6] can take the result and generate a
 | 
			
		||||
worksheet object.
 | 
			
		||||
 | 
			
		||||
:::info pass
 | 
			
		||||
 | 
			
		||||
Normally the method adds a `_id` field to each object.  The recommended way to
 | 
			
		||||
remove the field is to use a `projection` to suppress the ID:
 | 
			
		||||
remove the field is to use a `projection` to suppress the ID.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
/* generate an array of objects from a collection */
 | 
			
		||||
@ -43,16 +71,12 @@ const aoo = await collection.find({}, {projection:{_id:0}}).toArray();
 | 
			
		||||
const ws = utils.json_to_sheet(aoo);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Using `book_new` and `book_append_sheet`[^7], a workbook object can be created.
 | 
			
		||||
This workbook is typically exported to the filesystem with `writeFile`[^8].
 | 
			
		||||
 | 
			
		||||
## Complete Example
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
This demo was last tested on 2023 August 20 with MongoDB CE 7.0.0, MongoDB
 | 
			
		||||
connector module 5.7.0 and NodeJS 20.5.1.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
0) Install MongoDB 6.0 Community Edition. The macOS steps required `brew`:
 | 
			
		||||
0) Install MongoDB 7.0 Community Edition[^9]. The macOS steps required `brew`:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
brew tap mongodb/brew
 | 
			
		||||
@ -61,12 +85,23 @@ brew install mongodb-community
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
1) Start a MongoDB server on `localhost` (follow official instructions).
 | 
			
		||||
 | 
			
		||||
:::note pass
 | 
			
		||||
 | 
			
		||||
If `brew` was used to install MongoDB, the following command starts a server:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
/usr/local/opt/mongodb-community/bin/mongod --config /usr/local/etc/mongod.conf
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If Homebrew is configured to use `/opt/homebrew`, the command is:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
/opt/homebrew/opt/mongodb-community/bin/mongod --config /opt/homebrew/etc/mongod.conf
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
2) Create base project and install the dependencies:
 | 
			
		||||
 | 
			
		||||
<CodeBlock language="bash">{`\
 | 
			
		||||
@ -122,4 +157,21 @@ This script:
 | 
			
		||||
- creates a SheetJS worksheet from the collection (highlighted in the snippet)
 | 
			
		||||
- creates a SheetJS workbook, adds the worksheet, and exports to XLSX
 | 
			
		||||
 | 
			
		||||
4) Run `node SheetJSMongoCRUD.mjs` and open `SheetJSMongoCRUD.xlsx`
 | 
			
		||||
4) Run the script:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
node SheetJSMongoCRUD.mjs
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
There should be no errors in the terminal. The script will generate the file
 | 
			
		||||
`SheetJSMongoCRUD.xlsx`. That file can be opened in a spreadsheet editor.
 | 
			
		||||
 | 
			
		||||
[^1]: See [`insertMany`](https://mongodb.github.io/node-mongodb-native/5.7/classes/Collection.html#insertMany) in the MongoDB documentation.
 | 
			
		||||
[^2]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
 | 
			
		||||
[^3]: See ["Workbook Object"](/docs/csf/book)
 | 
			
		||||
[^4]: See [`read` and `readFile` in "Reading Files"](/docs/api/parse-options)
 | 
			
		||||
[^5]: See [`find`](https://mongodb.github.io/node-mongodb-native/5.7/classes/Collection.html#find) in the MongoDB documentation.
 | 
			
		||||
[^6]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
 | 
			
		||||
[^7]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
 | 
			
		||||
[^8]: See [`writeFile` in "Writing Files"](/docs/api/write-options)
 | 
			
		||||
[^9]: See ["Install MongoDB Community Edition"](https://www.mongodb.com/docs/manual/administration/install-community/#std-label-install-mdb-community-edition) in the MongoDB documentation.
 | 
			
		||||
@ -332,6 +332,49 @@ includes a live demo.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
#### Google Tag Manager
 | 
			
		||||
 | 
			
		||||
:::caution pass
 | 
			
		||||
 | 
			
		||||
Google Tag Manager is known to intercept and corrupt links. This issue will
 | 
			
		||||
manifest as UUID file names like `01234567-89ab-cdef-0123-456789abcdef` .
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
For sites using GTM, it is recommended to patch `document.createElement` and
 | 
			
		||||
revert after performing the export.
 | 
			
		||||
 | 
			
		||||
<details><summary><b>GTM Workaround</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
The workaround is to ensure new `A` elements created by `document.createElement`
 | 
			
		||||
have the `target` attribute set to `_blank`.
 | 
			
		||||
 | 
			
		||||
After calling `writeFile`, the old version of the method should be restored.
 | 
			
		||||
 | 
			
		||||
```js title="GTM Workaround"
 | 
			
		||||
/* preparation */
 | 
			
		||||
document.createElement2 = document.createElement;
 | 
			
		||||
document.createElement = function(...args) {
 | 
			
		||||
  if(args.length == 1 && args[0].toLowerCase() == "a") {
 | 
			
		||||
    const a = document.createElement2("a");
 | 
			
		||||
    a.target = "_blank";
 | 
			
		||||
    return a;
 | 
			
		||||
  }
 | 
			
		||||
  return document.createElement2.call(this, ...args);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* export (XLSX.writeFile) */
 | 
			
		||||
XLSX.writeFile(wb, "SheetJS.xlsx");
 | 
			
		||||
 | 
			
		||||
/* cleanup */
 | 
			
		||||
setTimeout(() => {
 | 
			
		||||
  document.createElement = document.createElement2;
 | 
			
		||||
  delete document.createElement2;
 | 
			
		||||
}, 1000);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
### File API
 | 
			
		||||
 | 
			
		||||
_Reading Files_
 | 
			
		||||
 | 
			
		||||
@ -288,7 +288,13 @@ JS and the DOM API do not have a standard approach for creating files. There was
 | 
			
		||||
a `saveAs` proposal as part of "File API: Writer" but it was abandoned in 2014.
 | 
			
		||||
 | 
			
		||||
The library integrates a number of platform-specific techniques for different
 | 
			
		||||
environments. In modern web browsers, the library uses the `download` attribute.
 | 
			
		||||
environments. In modern web browsers, the library creates an `A` element with
 | 
			
		||||
the `download` attribute and clicks the link. A full analysis is included in the
 | 
			
		||||
["Local File Access" demo](/docs/demos/local/file#html5-download-attribute)
 | 
			
		||||
 | 
			
		||||
If the filename looks like a UUID (hexadecimal characters and hyphens), this is
 | 
			
		||||
a known issue with Google Tag Manager (GTM) rewriting links. There is a special
 | 
			
		||||
[workaround](/docs/demos/local/file#google-tag-manager) for sites that use GTM.
 | 
			
		||||
 | 
			
		||||
Third party libraries like `FileSaver.js` provide an implementation of `saveAs`
 | 
			
		||||
that include more browser-specific workarounds.
 | 
			
		||||
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 25 KiB  | 
| 
		 Before Width: | Height: | Size: 35 KiB  | 
| 
		 Before Width: | Height: | Size: 42 KiB  | 
| 
		 Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 63 KiB  | 
| 
		 Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 78 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docz/static/nativescript/step4.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						| 
		 After Width: | Height: | Size: 17 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docz/static/nativescript/step5.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						| 
		 After Width: | Height: | Size: 27 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docz/static/nativescript/step7.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						| 
		 After Width: | Height: | Size: 40 KiB  |