This commit is contained in:
SheetJS 2023-04-18 16:26:59 -04:00
parent 5ff93dc4b7
commit e197ed24c8
5 changed files with 257 additions and 10 deletions

View File

@ -64,12 +64,12 @@ This data loader returns Base64 strings:
```ts title="src/env.d.ts"
/// <reference types="astro/client" />
declare module '*.numbers' {
const data: string;
export default data;
const data: string;
export default data;
}
declare module '*.xlsx' {
const data: string;
export default data;
const data: string;
export default data;
}
```

View File

@ -183,10 +183,10 @@ The following lines must be added to `ios/App/App/Info.plist`:
<plist version="1.0">
<dict>
<!-- highlight-start -->
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<!-- highlight-end -->
<key>CFBundleDevelopmentRegion</key>
```

View File

@ -77,7 +77,7 @@ function SheetJSAlaSQL() {
if(typeof alasql=="undefined") return setRows([{Nom:"alasql undefined"}]);
const blob = await (await fetch(url)).blob();
const data = URL.createObjectURL(blob);
const res = await alasql.promise(q1,[data]);
const res = await alasql.promise(q1,[data]);
setRows(res);
await alasql.promise(q2, [res]);
}, []);

View File

@ -0,0 +1,247 @@
---
title: AppleScript and OSA
pagination_prev: demos/cloud/index
pagination_next: demos/bigdata/index
---
Open Scripting Architecture (OSA), a built-in feature in macOS introduced in
1993, enables users to communicate with applications with a standardized
language and grammar. macOS releases starting from Yosemite (OSX 10.10) include
native support for scripting with JavaScript.
The [Standalone scripts](/docs/getting-started/installation/standalone) can be
parsed and evaluated from the JS engine. Once evaluated, the `XLSX` variable is
available as a global. A JS stub can expose methods from AppleScript scripts.
:::note
This demo was last tested on 2022 April 18 in macOS Monterey.
:::
## Integration details
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs groupId="osa">
<TabItem value="js" label="JavaScript">
The following snippet reads a file into a binary string:
```js
ObjC.import("Foundation");
function get_bstr(path) {
/* create NSString from the file contents using a binary encoding */
var str = $.NSString.stringWithContentsOfFileEncodingError(path, $.NSISOLatin1StringEncoding, null);
/* return the value as a JS object */
return ObjC.unwrap(str);
}
```
_Loading the Library_
Assuming the standalone library is in the same directory as the source file,
the script can be evaluated with `eval`:
```js
var src = get_bstr("./xlsx.full.min.js");
eval(src);
```
_Parsing Files_
The same method can be used to read binary strings and parse with `type: "binary"`:
```js
var file = get_bstr("./pres.numbers");
var wb = XLSX.read(file);
```
</TabItem>
<TabItem value="as" label="AppleScript">
The core idea is to push the processing logic to a stub JS file.
_JS Stub_
The JS stub will be evaluated in the JavaScript context. The same technique from
the JavaScript section works in the stub:
```js
ObjC.import("Foundation");
function get_bstr(path) {
var str = $.NSString.stringWithContentsOfFileEncodingError(path, $.NSISOLatin1StringEncoding, null);
return ObjC.unwrap(str);
}
/* this will be called when AppleScript initializes the JS engine */
eval(get_bstr("./xlsx.full.min.js"));
```
It is more efficient to offload as much work as possible into the stub. For
example, this function parses a workbook file from the filesystem and generates
a CSV without passing intermediate values back to AppleScript:
```js
/* this method will be exposed as `wb_to_csv` */
function wb_to_csv(path) {
/* read file */
var filedata = get_bstr(path);
var wb = XLSX.read(filedata, { type: "binary" });
return XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
}
```
_Loading the Stub_
Assuming the stub is saved to `xlsx.stub.js`, the following handler creates a
context and evaluates the standalone library:
```applescript
on getContext()
-- get contents of xlsx.stub.js
set UnixPath to POSIX path of ((path to me as text) & "::")
set libpath to POSIX path of (UnixPath & "xlsx.stub.js")
set {src, err} to current application's NSString's stringWithContentsOfFile:libpath encoding:(current application's NSISOLatin1StringEncoding) |error|:(reference)
if src is missing value then error (err's localizedDescription()) as text
-- create scripting context and evaluate the stub
set lang to current application's OSALanguage's languageForName:"JavaScript"
set osa to current application's OSAScript's alloc()'s initWithSource:src language:lang
return osa
end getContext
```
_Evaluating JS Code_
When calling a function, the result is an array whose first item is the value of
the evaluated code. A small helper function extracts the raw result:
```applescript
on extractResult(res)
return item 1 of ((current application's NSArray's arrayWithObject:res) as list)
end extractResult
```
With everything defined, `executeHandlerWithName` will run functions defined in
the stub. For example:
```applescript
set osa to getContext()
set {res, err} to osa's executeHandlerWithName:"wb_to_csv" arguments:{"pres.numbers"} |error|:(reference)
extractResult(res)
```
</TabItem>
</Tabs>
## Complete Demo
This example will read from a specified filename and print the first worksheet
data in CSV format.
0) Download the standalone script and test file:
```bash
curl -LO https://sheetjs.com/pres.numbers
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js
```
<Tabs groupId="osa">
<TabItem value="js" label="JavaScript">
1) Save the following script to `sheetosa.js`:
```js title="sheetosa.js"
#!/usr/bin/env osascript -l JavaScript
ObjC.import("Foundation");
function get_bstr(path) {
var str = $.NSString.stringWithContentsOfFileEncodingError(path, $.NSISOLatin1StringEncoding, null);
return ObjC.unwrap(str);
}
eval(get_bstr("./xlsx.full.min.js"));
function run(argv) {
var filedata = get_bstr(argv[0]);
var wb = XLSX.read(filedata, { type: "binary" });
console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
}
```
2) Make the script executable:
```bash
chmod +x sheetosa.js
```
3) Run the script, passing the path to the test file as an argument:
```bash
./sheetosa.js pres.numbers
```
</TabItem>
<TabItem value="as" label="AppleScript">
1) Save the following script to `xlsx.stub.js`:
```js title="xlsx.stub.js"
ObjC.import("Foundation");
function get_bstr(path) {
var str = $.NSString.stringWithContentsOfFileEncodingError(path, $.NSISOLatin1StringEncoding, null);
return ObjC.unwrap(str);
}
eval(get_bstr("./xlsx.full.min.js"));
function wb_to_csv(path) {
var filedata = get_bstr(path);
var wb = XLSX.read(filedata, { type: "binary" });
return XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
}
```
2) Save the following script to `sheetosa.scpt`:
```applescript title="sheetosa.scpt"
#!/usr/bin/env osascript
use AppleScript version "2.7"
use scripting additions
use framework "Foundation"
use framework "OSAKit"
set osa to getContext()
set {res, err} to osa's executeHandlerWithName:"wb_to_csv" arguments:{"pres.numbers"} |error|:(reference)
extractResult(res)
on getContext()
set UnixPath to POSIX path of ((path to me as text) & "::")
set libpath to POSIX path of (UnixPath & "xlsx.shim.js")
set {src, err} to current application's NSString's stringWithContentsOfFile:libpath encoding:(current application's NSISOLatin1StringEncoding) |error|:(reference)
set lang to current application's OSALanguage's languageForName:"JavaScript"
set osa to current application's OSAScript's alloc()'s initWithSource:src language:lang
return osa
end getContext
on extractResult(res)
return item 1 of ((current application's NSArray's arrayWithObject:res) as list)
end extractResult
```
3) Make the script executable:
```bash
chmod +x sheetosa.scpt
```
3) Run the script (it is hardcoded to read `pres.numbers`):
```bash
./sheetosa.scpt
```
</TabItem>
</Tabs>

View File

@ -142,7 +142,7 @@ const config = {
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
additionalLanguages: [ "swift", "java", "csharp", "perl", "ruby", "cpp" ],
additionalLanguages: [ "swift", "java", "csharp", "perl", "ruby", "cpp", "applescript" ],
},
liveCodeBlock: {
playgroundPosition: 'top'