5 |
6 |
13 |
14 |
15 | ZetaJS Ping Tool (Calc Demo with Vue.js-3)
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/letter-address-tool/README.md:
--------------------------------------------------------------------------------
1 | An example of a Web form letter demo, using a stripped-down, standalone Writer document canvas
2 | without any surrounding menubars, toolbars, side panels, etc.
3 |
4 | See config.sample.js for configuration options.
5 |
6 | [online demo](https://zetaoffice.net/demos/letter-address-tool/)
7 |
8 | # Run local for development
9 |
10 | To run the example, do
11 | ```
12 | npm install
13 | npm start
14 | ```
15 |
16 | # Using with a web server
17 |
18 | For getting files you can put on a web server, do
19 | ```
20 | npm install
21 | npm run dist
22 | ```
23 |
24 | The following HTTP headers must be set in the web server configuration.
25 | ```
26 | Cross-Origin-Opener-Policy "same-origin"
27 | Cross-Origin-Embedder-Policy "require-corp"
28 | ```
29 |
30 | Attention: When using in production, replace the zetajs 'file:' link in `package.json` with a proper version from npmjs.com.
31 |
--------------------------------------------------------------------------------
/examples/letter-address-vuejs3/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
14 |
15 | ZetaJS: Letter Address Vue.js-3 (Writer Demo with Vue.js-3)
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/ping-monitor/README.md:
--------------------------------------------------------------------------------
1 | An example of a simple Calc document displaying a single chart with data being added on the fly.
2 | All surrounding UI elements are hidden in this demo. Can be installed as PWA.
3 |
4 | See config.sample.js for configuration options.
5 |
6 | [online demo](https://zetaoffice.net/demos/ping-monitor/)
7 |
8 | # Run local for development
9 |
10 | To run the example, do
11 | ```
12 | npm install
13 | npm start
14 | ```
15 |
16 | # Using with a web server
17 |
18 | For getting files you can put on a web server, do
19 | ```
20 | npm install
21 | npm run dist
22 | ```
23 |
24 | The following HTTP headers must be set in the web server configuration.
25 | ```
26 | Cross-Origin-Opener-Policy "same-origin"
27 | Cross-Origin-Embedder-Policy "require-corp"
28 | ```
29 |
30 | Attention: When using in production, replace the zetajs 'file:' link in `package.json` with a proper version from npmjs.com.
31 |
--------------------------------------------------------------------------------
/examples/standalone/README.md:
--------------------------------------------------------------------------------
1 | An example of a stripped-down, standalone Writer document canvas without any surrounding menubars,
2 | toolbars, side panels, etc. Includes HTML buttons to for simple formatting (bold, italic, underlined).
3 |
4 | See config.sample.js for configuration options.
5 |
6 | [online demo](https://zetaoffice.net/demos/standalone/)
7 |
8 | ## Run local for development
9 |
10 | To run the example, do
11 | ```
12 | npm install
13 | npm start
14 | ```
15 |
16 | ## Using with a web server
17 |
18 | For getting files you can put on a web server, do
19 | ```
20 | npm install
21 | npm run dist
22 | ```
23 |
24 | The following HTTP headers must be set in the web server configuration.
25 | ```
26 | Cross-Origin-Opener-Policy "same-origin"
27 | Cross-Origin-Embedder-Policy "require-corp"
28 | ```
29 |
30 | Attention: When using in production, replace the zetajs 'file:' link in `package.json` with a proper version from npmjs.com.
31 |
--------------------------------------------------------------------------------
/examples/convertpdf/README.md:
--------------------------------------------------------------------------------
1 | An example of a local file to PDF conversion service. If the "Download" button is checked, the
2 | converted PDF document is downloaded to the local file system, in addition to being shown in an
3 | iframe on the web page.
4 |
5 | See config.sample.js for configuration options.
6 |
7 | [online demo](https://zetaoffice.net/demos/convertpdf/)
8 |
9 | ## Run local for development
10 |
11 | To run the example, do
12 | ```
13 | npm install
14 | npm start
15 | ```
16 |
17 | ## Using with a web server
18 |
19 | For getting files you can put on a web server, do
20 | ```
21 | npm install
22 | npm run dist
23 | ```
24 |
25 | The following HTTP headers must be set in the web server configuration.
26 | ```
27 | Cross-Origin-Opener-Policy "same-origin"
28 | Cross-Origin-Embedder-Policy "require-corp"
29 | ```
30 |
31 | Attention: When using in production, replace the zetajs 'file:' link in `package.json` with a proper version from npmjs.com.
32 |
--------------------------------------------------------------------------------
/examples/vuejs3-ping-tool/README.md:
--------------------------------------------------------------------------------
1 | # A ZetaJS Ping Tool (Calc Demo using Vue.js-3)
2 |
3 | [online demo](https://zetaoffice.net/demos/vuejs3-ping-tool/)
4 |
5 | For Vue.js-3 you'll need nodejs.
6 | And either you also install npm system wide or you use:
7 | `nodeenv --node=system --with-npm nodeenv/`
8 | https://packages.debian.org/bookworm/nodejs
9 | https://packages.debian.org/bookworm/nodeenv
10 |
11 | # vuejs3
12 |
13 | ## Customize configuration
14 |
15 | See [Vite Configuration Reference](https://vitejs.dev/config/).
16 |
17 | ## Project Setup
18 |
19 | ```sh
20 | npm install
21 | ```
22 |
23 | ### Compile and Hot-Reload for Development
24 |
25 | ```sh
26 | npm start
27 | ```
28 |
29 | ### Compile and Minify for Production
30 |
31 | ```sh
32 | npm run build
33 | ```
34 |
35 | The following HTTP headers must be set in the web server configuration.
36 | ```
37 | Cross-Origin-Opener-Policy "same-origin"
38 | Cross-Origin-Embedder-Policy "require-corp"
39 | ```
40 |
41 | ### Lint with [ESLint](https://eslint.org/)
42 |
43 | ```sh
44 | npm run lint
45 | ```
46 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.2.0
4 | * Added zetaHelper TypeScript library to combine commonly used code
5 | * used for examples: convertpdf, letter-address-tool, letter-address-vuejs3, ping-monitor, vuejs3-ping-tool
6 | * disabled HTML page scrolling when the mouse cursor is above the canvas
7 | * examples:
8 | * improved example layouts and zooming
9 | * improved initialisation
10 | * simplified and unified code
11 | * examples: fixed crash on non uniformly formatted selections
12 | * using Chromium's "File System Access API" in the web-office exmaple
13 | * many small fixes
14 |
15 | ## 1.1.0
16 | * Let zetajs return unwrapped ANY representations
17 | * Technically backwards-incompatible with old code that relied on unspecified implementation details, like relying on receiving wrapped rather than unwrapped UNO ANY representations.
18 | * Converted most examples to NPM
19 |
20 | ## 1.0.3
21 | * Fix version number clash on NPM
22 |
23 | ## 1.0.2
24 | * Add SPDX-License-Identifier
25 |
26 | ## 1.0.1
27 | * Fix domain name
28 |
29 | ## 1.0.0
30 | * Initial release
31 |
--------------------------------------------------------------------------------
/test/e2e/test.js:
--------------------------------------------------------------------------------
1 | describe("convert", () => {
2 | beforeAll(async () => {
3 | // Wait a bit to give time for the server to start
4 | await new Promise(r => setTimeout(r, 10000));
5 | await page.goto("http://127.0.0.1:3000");
6 | }, 30000);
7 |
8 | it('should convert an odt file to pdf', async () => {
9 | // Wait for zetaoffice to load
10 | await new Promise(r => {
11 | const waitFunc = async () => {
12 | const success = await page.evaluate(() => {
13 | return document.querySelector('input').disabled === false;
14 | });
15 | success ? r() : setTimeout(waitFunc, 1000);
16 | };
17 | waitFunc();
18 | });
19 |
20 | // Convert test/test.odt to pdf and make sure it opens in new tab
21 | const download = await page.$("#download");
22 | download.click();
23 | const input = await page.$('#input');
24 | await input.uploadFile('test/e2e/test.odt');
25 | const newTarget = await browser.waitForTarget(target => target.opener() === page.target());
26 | await newTarget.page();
27 | }, 30000);
28 | });
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 allotropia software GmbH and contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/letter-address-vuejs3/README.md:
--------------------------------------------------------------------------------
1 | # A ZetaJS Letter Address Tool (Writer Demo using Vue.js-3)
2 |
3 | An example of a Web form letter demo, using a stripped-down, standalone Writer document canvas
4 | without any surrounding menubars, toolbars, side panels, etc. This version uses Vue.js-3.
5 |
6 | [online demo](https://zetaoffice.net/demos/letter-address-vuejs3/)
7 |
8 | For Vue.js-3 you'll need nodejs.
9 | And either you also install npm system wide or you use:
10 | `nodeenv --node=system --with-npm nodeenv/`
11 | https://packages.debian.org/bookworm/nodejs
12 | https://packages.debian.org/bookworm/nodeenv
13 |
14 | # vuejs3
15 |
16 | ## Customize configuration
17 |
18 | See [Vite Configuration Reference](https://vitejs.dev/config/).
19 |
20 | ## Project Setup
21 |
22 | ```sh
23 | npm install
24 | ```
25 |
26 | ### Compile and Hot-Reload for Development
27 |
28 | ```sh
29 | npm start
30 | ```
31 |
32 | ### Compile and Minify for Production
33 |
34 | ```sh
35 | npm run build
36 | ```
37 |
38 | The following HTTP headers must be set in the web server configuration.
39 | ```
40 | Cross-Origin-Opener-Policy "same-origin"
41 | Cross-Origin-Embedder-Policy "require-corp"
42 | ```
43 |
44 | ### Lint with [ESLint](https://eslint.org/)
45 |
46 | ```sh
47 | npm run lint
48 | ```
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zetajs",
3 | "version": "1.2.0",
4 | "description": "Access ZetaOffice in the Browser from JavaScript via UNO",
5 | "exports": {
6 | "./zeta.js": "./source/zeta.js",
7 | "./zetaHelper.js": "./source/zetaHelper.js",
8 | "./zetaHelper.ts": "./source/zetaHelper.ts"
9 | },
10 | "directories": {
11 | "doc": "docs",
12 | "example": "examples",
13 | "test": "test"
14 | },
15 | "files": [
16 | "/CHANGELOG.md",
17 | "/LICENSE",
18 | "/source/zeta.js",
19 | "/source/zetaHelper.js"
20 | ],
21 | "scripts": {
22 | "build": "tsc",
23 | "prepare": "npm run build",
24 | "test": "jest test/e2e/",
25 | "test:convertpdf": "cd examples/convertpdf && npm install && npm run start-nobrowser"
26 | },
27 | "repository": {
28 | "type": "git",
29 | "url": "git+https://github.com/allotropia/zetajs.git"
30 | },
31 | "keywords": [
32 | "libreoffice",
33 | "office",
34 | "zetaoffice",
35 | "allotropia",
36 | "collabora",
37 | "wasm",
38 | "lowa",
39 | "typescript"
40 | ],
41 | "author": "allotropia software GmbH",
42 | "license": "MIT",
43 | "bugs": {
44 | "url": "https://github.com/allotropia/zetajs/issues"
45 | },
46 | "homepage": "https://github.com/allotropia/zetajs#readme",
47 | "devDependencies": {
48 | "@types/node": "^22.13.10",
49 | "concurrently": "^9.1.0",
50 | "jest": "^29.7.0",
51 | "jest-puppeteer": "^11.0.0",
52 | "puppeteer": "^23.11.1",
53 | "typescript": "^5.8.2"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/examples/vuejs3-ping-tool/src/components/ControlBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
62 |
--------------------------------------------------------------------------------
/test/README.md:
--------------------------------------------------------------------------------
1 | # zetajs Smoketesting
2 |
3 | *Note:* Running `smoketest.js` requires a LibreOffice configured with `--enable-dbgutil` to have
4 | the `org.libreoffice.embindtest` UNOIDL entities available.
5 |
6 | ## Running the smoketest
7 |
8 | Put this directory into a webservers webroot.
9 |
10 | The following HTTP headers must be set in the webserver configuration.
11 | ```
12 | Cross-Origin-Opener-Policy "same-origin"
13 | Cross-Origin-Embedder-Policy "require-corp"
14 | ```
15 |
16 | You might configure a webserver like Apache for this. Or you use emrun which comes has Emscripten and sets this headers by default.
17 | ```
18 | $ emrun rainbow_writer.html
19 | ```
20 |
21 | Additionally you need to provide the following files. in the webroot:
22 | ```
23 | soffice.data
24 | soffice.data.js.metadata
25 | soffice.js
26 | soffice.wasm
27 | zeta.js
28 | ```
29 |
30 | ### Alternative: Build LOWA with EMSCRIPTEN_EXTRA_SOFFICE_PRE_JS
31 |
32 | One way to run some of the provided example and test code is to serve those files next to `qt_soffice.html`, along with some `include.js` that looks like
33 | ```
34 | Module.uno_scripts = [
35 | 'zetajs/source/zeta.js',
36 | 'zetajs/test/smoketest.js'];
37 | ```
38 | (or whatever the paths where you serve them, relative to `qt_soffice.html`; `zeta.js` always needs to come first), and to build LOWA with an `EMSCRIPTEN_EXTRA_SOFFICE_PRE_JS=/path/to/include.js` configuration option (e.g., as a line in `autogen.input`), with `/path/to` adapted accordingly. (The `test/smoketest.js` code requires a LibreOffice configured with `--enable-dbgutil` to have the `org.libreoffice.embindtest` UNOIDL entities available.)
39 |
--------------------------------------------------------------------------------
/examples/convertpdf/config.sample.js:
--------------------------------------------------------------------------------
1 | //// IMPORTANT - Usage:
2 | //// COPY THIS FILE to config.js to use it.
3 | ////
4 | //// gulpfile.js will automatically include config.js if it exists.
5 |
6 |
7 |
8 |
9 | //////// SIMPLE OPTIONS
10 |
11 | // Serve the LOWA files from a custom URL.
12 | // E.g. for testing on localhost / 127.0.0.1.
13 | //
14 | // When serving the LOWA files from foreign origins, these HTTP headers are needed:
15 | // Cross-Origin-Resource-Policy: cross-origin
16 | // Access-Control-Allow-Origin: *
17 | // Access-Control-Allow-Methods: *
18 | //
19 | // '' will assume the LOWA files in the same directory as the web app.
20 | // IMPORTANT: Place soffice.{data,data.js.metadata,js,wasm} in the public/ folder.
21 | // Use with "npm run debug". (other tasks may clean the public/ folder)
22 | soffice_base_url = '';
23 | // current limitations:
24 | // - Relative URLs may not work. Use absolute URLs instead!
25 | // - For example: http://127.0.0.1:8080/lowa_build_subdir/
26 | // - Also always append the trailing slash!
27 |
28 | // Disable cleaning the public folder for all tasks.
29 | clean_disabled = true;
30 |
31 | // Custom webserver port.
32 | //custom_port = "8080";
33 |
34 | // Which web browser to start with "npm run start".
35 | //custom_browser = "chromium";
36 |
37 |
38 |
39 |
40 | //////// ADVANCED OPTIONS
41 |
42 | //// You may modify the debug target.
43 | // exports.debug = gulp.series(gulp.parallel(compileHTML, compileJS, copyVendors));
44 |
45 | //// Pick a custom browser and launch it automatically.
46 | //custom_browser = "chromium"; // ["google chrome", "firefox"]
47 | //exports.debug = gulp.series(gulp.parallel(compileHTML, compileJS, copyVendors),
48 | // gulp.parallel(watchFiles, initBrowserSync) );
49 |
--------------------------------------------------------------------------------
/examples/ping-monitor/config.sample.js:
--------------------------------------------------------------------------------
1 | //// IMPORTANT - Usage:
2 | //// COPY THIS FILE to config.js to use it.
3 | ////
4 | //// gulpfile.js will automatically include config.js if it exists.
5 |
6 |
7 |
8 |
9 | //////// SIMPLE OPTIONS
10 |
11 | // Serve the LOWA files from a custom URL.
12 | // E.g. for testing on localhost / 127.0.0.1.
13 | //
14 | // When serving the LOWA files from foreign origins, these HTTP headers are needed:
15 | // Cross-Origin-Resource-Policy: cross-origin
16 | // Access-Control-Allow-Origin: *
17 | // Access-Control-Allow-Methods: *
18 | //
19 | // '' will assume the LOWA files in the same directory as the web app.
20 | // IMPORTANT: Place soffice.{data,data.js.metadata,js,wasm} in the public/ folder.
21 | // Use with "npm run debug". (other tasks may clean the public/ folder)
22 | soffice_base_url = '';
23 | // current limitations:
24 | // - Relative URLs may not work. Use absolute URLs instead!
25 | // - For example: http://127.0.0.1:8080/lowa_build_subdir/
26 | // - Also always append the trailing slash!
27 |
28 | // Disable cleaning the public folder for all tasks.
29 | clean_disabled = true;
30 |
31 | // Custom webserver port.
32 | //custom_port = "8080";
33 |
34 | // Which web browser to start with "npm run start".
35 | //custom_browser = "chromium";
36 |
37 |
38 |
39 |
40 | //////// ADVANCED OPTIONS
41 |
42 | //// You may modify the debug target.
43 | // exports.debug = gulp.series(gulp.parallel(compileHTML, compileJS, copyVendors));
44 |
45 | //// Pick a custom browser and launch it automatically.
46 | //custom_browser = "chromium"; // ["google chrome", "firefox"]
47 | //exports.debug = gulp.series(gulp.parallel(compileHTML, compileJS, copyVendors),
48 | // gulp.parallel(watchFiles, initBrowserSync) );
49 |
--------------------------------------------------------------------------------
/examples/standalone/config.sample.js:
--------------------------------------------------------------------------------
1 | //// IMPORTANT - Usage:
2 | //// COPY THIS FILE to config.js to use it.
3 | ////
4 | //// gulpfile.js will automatically include config.js if it exists.
5 |
6 |
7 |
8 |
9 | //////// SIMPLE OPTIONS
10 |
11 | // Serve the LOWA files from a custom URL.
12 | // E.g. for testing on localhost / 127.0.0.1.
13 | //
14 | // When serving the LOWA files from foreign origins, these HTTP headers are needed:
15 | // Cross-Origin-Resource-Policy: cross-origin
16 | // Access-Control-Allow-Origin: *
17 | // Access-Control-Allow-Methods: *
18 | //
19 | // '' will assume the LOWA files in the same directory as the web app.
20 | // IMPORTANT: Place soffice.{data,data.js.metadata,js,wasm} in the public/ folder.
21 | // Use with "npm run debug". (other tasks may clean the public/ folder)
22 | soffice_base_url = '';
23 | // current limitations:
24 | // - Relative URLs may not work. Use absolute URLs instead!
25 | // - For example: http://127.0.0.1:8080/lowa_build_subdir/
26 | // - Also always append the trailing slash!
27 |
28 | // Disable cleaning the public folder for all tasks.
29 | clean_disabled = true;
30 |
31 | // Custom webserver port.
32 | //custom_port = "8080";
33 |
34 | // Which web browser to start with "npm run start".
35 | //custom_browser = "chromium";
36 |
37 |
38 |
39 |
40 | //////// ADVANCED OPTIONS
41 |
42 | //// You may modify the debug target.
43 | // exports.debug = gulp.series(gulp.parallel(compileHTML, compileJS, copyVendors));
44 |
45 | //// Pick a custom browser and launch it automatically.
46 | //custom_browser = "chromium"; // ["google chrome", "firefox"]
47 | //exports.debug = gulp.series(gulp.parallel(compileHTML, compileJS, copyVendors),
48 | // gulp.parallel(watchFiles, initBrowserSync) );
49 |
--------------------------------------------------------------------------------
/examples/web-office/config.sample.js:
--------------------------------------------------------------------------------
1 | //// IMPORTANT - Usage:
2 | //// COPY THIS FILE to config.js to use it.
3 | ////
4 | //// gulpfile.js will automatically include config.js if it exists.
5 |
6 |
7 |
8 |
9 | //////// SIMPLE OPTIONS
10 |
11 | // Serve the LOWA files from a custom URL.
12 | // E.g. for testing on localhost / 127.0.0.1.
13 | //
14 | // When serving the LOWA files from foreign origins, these HTTP headers are needed:
15 | // Cross-Origin-Resource-Policy: cross-origin
16 | // Access-Control-Allow-Origin: *
17 | // Access-Control-Allow-Methods: *
18 | //
19 | // '' will assume the LOWA files in the same directory as the web app.
20 | // IMPORTANT: Place soffice.{data,data.js.metadata,js,wasm} in the public/ folder.
21 | // Use with "npm run debug". (other tasks may clean the public/ folder)
22 | soffice_base_url = '';
23 | // current limitations:
24 | // - Relative URLs may not work. Use absolute URLs instead!
25 | // - For example: http://127.0.0.1:8080/lowa_build_subdir/
26 | // - Also always append the trailing slash!
27 |
28 | // Disable cleaning the public folder for all tasks.
29 | clean_disabled = true;
30 |
31 | // Custom webserver port.
32 | //custom_port = "8080";
33 |
34 | // Which web browser to start with "npm run start".
35 | //custom_browser = "chromium";
36 |
37 |
38 |
39 |
40 | //////// ADVANCED OPTIONS
41 |
42 | //// You may modify the debug target.
43 | // exports.debug = gulp.series(gulp.parallel(compileHTML, compileJS, copyVendors));
44 |
45 | //// Pick a custom browser and launch it automatically.
46 | //custom_browser = "chromium"; // ["google chrome", "firefox"]
47 | //exports.debug = gulp.series(gulp.parallel(compileHTML, compileJS, copyVendors),
48 | // gulp.parallel(watchFiles, initBrowserSync) );
49 |
--------------------------------------------------------------------------------
/examples/letter-address-tool/config.sample.js:
--------------------------------------------------------------------------------
1 | //// IMPORTANT - Usage:
2 | //// COPY THIS FILE to config.js to use it.
3 | ////
4 | //// gulpfile.js will automatically include config.js if it exists.
5 |
6 |
7 |
8 |
9 | //////// SIMPLE OPTIONS
10 |
11 | // Serve the LOWA files from a custom URL.
12 | // E.g. for testing on localhost / 127.0.0.1.
13 | //
14 | // When serving the LOWA files from foreign origins, these HTTP headers are needed:
15 | // Cross-Origin-Resource-Policy: cross-origin
16 | // Access-Control-Allow-Origin: *
17 | // Access-Control-Allow-Methods: *
18 | //
19 | // '' will assume the LOWA files in the same directory as the web app.
20 | // IMPORTANT: Place soffice.{data,data.js.metadata,js,wasm} in the public/ folder.
21 | // Use with "npm run debug". (other tasks may clean the public/ folder)
22 | soffice_base_url = '';
23 | // current limitations:
24 | // - Relative URLs may not work. Use absolute URLs instead!
25 | // - For example: http://127.0.0.1:8080/lowa_build_subdir/
26 | // - Also always append the trailing slash!
27 |
28 | // Disable cleaning the public folder for all tasks.
29 | clean_disabled = true;
30 |
31 | // Custom webserver port.
32 | //custom_port = "8080";
33 |
34 | // Which web browser to start with "npm run start".
35 | //custom_browser = "chromium";
36 |
37 |
38 |
39 |
40 | //////// ADVANCED OPTIONS
41 |
42 | //// You may modify the debug target.
43 | // exports.debug = gulp.series(gulp.parallel(compileHTML, compileJS, copyVendors));
44 |
45 | //// Pick a custom browser and launch it automatically.
46 | //custom_browser = "chromium"; // ["google chrome", "firefox"]
47 | //exports.debug = gulp.series(gulp.parallel(compileHTML, compileJS, copyVendors),
48 | // gulp.parallel(watchFiles, initBrowserSync) );
49 |
--------------------------------------------------------------------------------
/examples/simple-examples/find_and_replace.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | // Debugging note:
5 | // Switch the web worker in the browsers debug tab to debug this code.
6 | // It's the "em-pthread" web worker with the most memory usage, where "zetajs" is defined.
7 |
8 | "use strict";
9 |
10 |
11 | // global variables - zetajs environment:
12 | let zetajs, css;
13 |
14 | // = global variables (some are global for easier debugging) =
15 | // common variables:
16 | let context, desktop, xModel, toolkit;
17 | // example specific:
18 | let searchDescriptor, xTextCursor;
19 |
20 |
21 | function demo() {
22 | // Replaces every occurence of "LibreOffice" with "LIBRE-OFFICE YEAH".
23 | console.log('PLUS: execute example code');
24 |
25 | context = zetajs.getUnoComponentContext();
26 | desktop = css.frame.Desktop.create(context);
27 | const in_path = 'file:///android/default-document/example.odt'
28 | xModel = desktop.loadComponentFromURL(in_path, '_default', 0, []);
29 |
30 | toolkit = css.awt.Toolkit.create(context);
31 | setInterval(function() {try {toolkit.getActiveTopWindow().FullScreen = true} catch {}}, 1000);
32 |
33 | searchDescriptor = xModel.createSearchDescriptor();
34 | searchDescriptor.setSearchString('LibreOffice');
35 | xTextCursor = xModel.findFirst(searchDescriptor);
36 | while (xTextCursor !== null) {
37 | //xTextCursor.goRight(0, false); // Appending instead of replacing.
38 | xTextCursor.setString("LIBRE-OFFICE YEAH");
39 | xTextCursor = xModel.findNext(xTextCursor, searchDescriptor);
40 | }
41 | }
42 |
43 |
44 | Module.zetajs.then(function(pZetajs) {
45 | // initializing zetajs environment:
46 | zetajs = pZetajs;
47 | css = zetajs.uno.com.sun.star;
48 | demo(); // launching demo
49 | });
50 |
51 | /* vim:set shiftwidth=2 softtabstop=2 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
52 |
--------------------------------------------------------------------------------
/examples/simple-examples/README.md:
--------------------------------------------------------------------------------
1 | These are small examples that showcase code snippets how to programmatically interact with
2 | documents.
3 |
4 | ## Online demos
5 |
6 | - [find_and_replace.html](https://zetaoffice.net/demos/simple-examples/find_and_replace.html)
7 | - [rainbow_writer.html](https://zetaoffice.net/demos/simple-examples/rainbow_writer.html)
8 | - [simple.html](https://zetaoffice.net/demos/simple-examples/simple.html)
9 | - [simple_key_handler.html](https://zetaoffice.net/demos/simple-examples/simple_key_handler.html)
10 |
11 | ## Running the examples
12 |
13 | Put the whole Git repo into a webservers webroot. The whole Git repo is needed, because this directory has a symlink to "../../source/zeta.js".
14 |
15 | The following HTTP headers must be set in the webserver configuration.
16 | ```
17 | Cross-Origin-Opener-Policy "same-origin"
18 | Cross-Origin-Embedder-Policy "require-corp"
19 | ```
20 |
21 | You might configure a webserver like Apache for this. Or you use emrun which comes has Emscripten and sets this headers by default. (replace rainbow_writer.html with the simple-example you like to run)
22 | ```
23 | $ emrun rainbow_writer.html
24 | ```
25 |
26 | ### Alternative: Build LOWA with EMSCRIPTEN_EXTRA_SOFFICE_PRE_JS
27 |
28 | One way to run some of the provided example and test code is to serve those files next to `qt_soffice.html`, along with some `include.js` that looks like
29 | ```
30 | Module.uno_scripts = [
31 | 'zetajs/source/zeta.js',
32 | 'zetajs/examples/rainbow_writer.js'];
33 | ```
34 | (or whatever the paths where you serve them, relative to `qt_soffice.html`; `zeta.js` always needs to come first), and to build LOWA with an `EMSCRIPTEN_EXTRA_SOFFICE_PRE_JS=/path/to/include.js` configuration option (e.g., as a line in `autogen.input`), with `/path/to` adapted accordingly. (The `test/smoketest.js` code requires a LibreOffice configured with `--enable-dbgutil` to have the `org.libreoffice.embindtest` UNOIDL entities available.)
35 |
--------------------------------------------------------------------------------
/examples/web-office/office_thread.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | // Debugging note:
5 | // Switch the web worker in the browsers debug tab to debug this code.
6 | // It's the "em-pthread" web worker with the most memory usage, where "zetajs" is defined.
7 |
8 | 'use strict';
9 |
10 |
11 | // global variables - zetajs environment:
12 | let zetajs, css;
13 |
14 | // = global variables (some are global for easier debugging) =
15 | // common variables:
16 | let context, desktop, xModel, ctrl;
17 |
18 |
19 | function demo() {
20 | context = zetajs.getUnoComponentContext();
21 | const bean_overwrite = new css.beans.PropertyValue({Name: 'Overwrite', Value: true});
22 | const bean_odt_export = new css.beans.PropertyValue({Name: 'FilterName', Value: 'writer8'});
23 | desktop = css.frame.Desktop.create(context);
24 |
25 | zetajs.mainPort.onmessage = function (e) {
26 | switch (e.data.cmd) {
27 | case 'upload':
28 | loadFile(e.data.filename);
29 | break;
30 | case 'download':
31 | xModel.store();
32 | zetajs.mainPort.postMessage({cmd: 'download', id: e.data.id});
33 | break;
34 | default:
35 | throw Error('Unknown message command: ' + e.data.cmd);
36 | }
37 | }
38 | zetajs.mainPort.postMessage({cmd: 'thr_running'});
39 | }
40 |
41 | function loadFile(filename) {
42 | const in_path = 'file:///tmp/office/' + filename;
43 | xModel = desktop.loadComponentFromURL(in_path, '_default', 0, []);
44 | ctrl = xModel.getCurrentController();
45 | ctrl.getFrame().getContainerWindow().FullScreen = true;
46 | zetajs.mainPort.postMessage({cmd: 'ui_ready'});
47 | }
48 |
49 | Module.zetajs.then(function(pZetajs) {
50 | // initializing zetajs environment:
51 | zetajs = pZetajs;
52 | css = zetajs.uno.com.sun.star;
53 | demo(); // launching demo
54 | });
55 |
56 | /* vim:set shiftwidth=2 softtabstop=2 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
57 |
--------------------------------------------------------------------------------
/examples/simple-examples/simple.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | // Debugging note:
5 | // Switch the web worker in the browsers debug tab to debug this code.
6 | // It's the "em-pthread" web worker with the most memory usage, where "zetajs" is defined.
7 |
8 | 'use strict';
9 |
10 |
11 | // global variables - zetajs environment:
12 | let zetajs, css;
13 |
14 | // = global variables (some are global for easier debugging) =
15 | // common variables:
16 | let context, desktop, xModel, toolkit;
17 | // example specific:
18 | let xText, xTextCursor;
19 |
20 |
21 | function demo() {
22 | // Adapted sample code from static/README.wasm.md:
23 | console.log('PLUS: execute example code');
24 |
25 | // load document
26 | context = zetajs.getUnoComponentContext();
27 | desktop = css.frame.Desktop.create(context);
28 | xModel = desktop.getCurrentFrame().getController().getModel();
29 | if (!xModel?.queryInterface(zetajs.type.interface(css.text.XTextDocument))) {
30 | xModel = desktop.loadComponentFromURL(
31 | 'file:///android/default-document/example.odt', '_default', 0, []);
32 | }
33 | toolkit = css.awt.Toolkit.create(context);
34 | setInterval(function() {try {toolkit.getActiveTopWindow().FullScreen = true} catch {}}, 1000);
35 | xText = xModel.getText();
36 |
37 | // insert string
38 | xTextCursor = xText.createTextCursor();
39 | xTextCursor.setString("string here!");
40 |
41 | // colorize paragraphs
42 | const xParaEnumeration = xText.createEnumeration();
43 | for (const xParagraph of xParaEnumeration) {
44 | const color = Math.floor(Math.random() * 0xFFFFFF);
45 | try {
46 | xParagraph.setPropertyValue("CharColor", color);
47 | } catch (e) {
48 | if (zetajs.getAnyType(zetajs.catchUnoException(e)) !=
49 | 'com.sun.star.lang.IllegalArgumentException')
50 | {
51 | throw e;
52 | }
53 | }
54 | }
55 | }
56 |
57 |
58 | Module.zetajs.then(function(pZetajs) {
59 | // initializing zetajs environment:
60 | zetajs = pZetajs;
61 | css = zetajs.uno.com.sun.star;
62 | demo(); // launching demo
63 | });
64 |
65 | /* vim:set shiftwidth=2 softtabstop=2 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
66 |
--------------------------------------------------------------------------------
/examples/vuejs3-ping-tool/src/assets/base.css:
--------------------------------------------------------------------------------
1 | /* color palette from */
2 | :root {
3 | --vt-c-white: #ffffff;
4 | --vt-c-white-soft: #f8f8f8;
5 | --vt-c-white-mute: #f2f2f2;
6 |
7 | --vt-c-black: #181818;
8 | --vt-c-black-soft: #222222;
9 | --vt-c-black-mute: #282828;
10 |
11 | --vt-c-indigo: #2c3e50;
12 |
13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
17 |
18 | --vt-c-text-light-1: var(--vt-c-indigo);
19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
20 | --vt-c-text-dark-1: var(--vt-c-white);
21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
22 | }
23 |
24 | /* semantic color variables for this project */
25 | :root {
26 | --color-background: var(--vt-c-white);
27 | --color-background-soft: var(--vt-c-white-soft);
28 | --color-background-mute: var(--vt-c-white-mute);
29 |
30 | --color-border: var(--vt-c-divider-light-2);
31 | --color-border-hover: var(--vt-c-divider-light-1);
32 |
33 | --color-heading: var(--vt-c-text-light-1);
34 | --color-text: var(--vt-c-text-light-1);
35 |
36 | --section-gap: 160px;
37 | }
38 |
39 | @media (prefers-color-scheme: dark) {
40 | :root {
41 | --color-background: var(--vt-c-black);
42 | --color-background-soft: var(--vt-c-black-soft);
43 | --color-background-mute: var(--vt-c-black-mute);
44 |
45 | --color-border: var(--vt-c-divider-dark-2);
46 | --color-border-hover: var(--vt-c-divider-dark-1);
47 |
48 | --color-heading: var(--vt-c-text-dark-1);
49 | --color-text: var(--vt-c-text-dark-2);
50 | }
51 | }
52 |
53 | *,
54 | *::before,
55 | *::after {
56 | box-sizing: border-box;
57 | margin: 0;
58 | font-weight: normal;
59 | }
60 |
61 | body {
62 | min-height: 100vh;
63 | color: var(--color-text);
64 | background: var(--color-background);
65 | transition:
66 | color 0.5s,
67 | background-color 0.5s;
68 | line-height: 1.6;
69 | font-family:
70 | Inter,
71 | -apple-system,
72 | BlinkMacSystemFont,
73 | 'Segoe UI',
74 | Roboto,
75 | Oxygen,
76 | Ubuntu,
77 | Cantarell,
78 | 'Fira Sans',
79 | 'Droid Sans',
80 | 'Helvetica Neue',
81 | sans-serif;
82 | font-size: 15px;
83 | text-rendering: optimizeLegibility;
84 | -webkit-font-smoothing: antialiased;
85 | -moz-osx-font-smoothing: grayscale;
86 | }
87 |
--------------------------------------------------------------------------------
/examples/letter-address-vuejs3/src/assets/base.css:
--------------------------------------------------------------------------------
1 | /* color palette from */
2 | :root {
3 | --vt-c-white: #ffffff;
4 | --vt-c-white-soft: #f8f8f8;
5 | --vt-c-white-mute: #f2f2f2;
6 |
7 | --vt-c-black: #181818;
8 | --vt-c-black-soft: #222222;
9 | --vt-c-black-mute: #282828;
10 |
11 | --vt-c-indigo: #2c3e50;
12 |
13 | --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
14 | --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
15 | --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
16 | --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
17 |
18 | --vt-c-text-light-1: var(--vt-c-indigo);
19 | --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
20 | --vt-c-text-dark-1: var(--vt-c-white);
21 | --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
22 | }
23 |
24 | /* semantic color variables for this project */
25 | :root {
26 | --color-background: var(--vt-c-white);
27 | --color-background-soft: var(--vt-c-white-soft);
28 | --color-background-mute: var(--vt-c-white-mute);
29 |
30 | --color-border: var(--vt-c-divider-light-2);
31 | --color-border-hover: var(--vt-c-divider-light-1);
32 |
33 | --color-heading: var(--vt-c-text-light-1);
34 | --color-text: var(--vt-c-text-light-1);
35 |
36 | --section-gap: 160px;
37 | }
38 |
39 | @media (prefers-color-scheme: dark) {
40 | :root {
41 | --color-background: var(--vt-c-black);
42 | --color-background-soft: var(--vt-c-black-soft);
43 | --color-background-mute: var(--vt-c-black-mute);
44 |
45 | --color-border: var(--vt-c-divider-dark-2);
46 | --color-border-hover: var(--vt-c-divider-dark-1);
47 |
48 | --color-heading: var(--vt-c-text-dark-1);
49 | --color-text: var(--vt-c-text-dark-2);
50 | }
51 | }
52 |
53 | *,
54 | *::before,
55 | *::after {
56 | box-sizing: border-box;
57 | margin: 0;
58 | font-weight: normal;
59 | }
60 |
61 | body {
62 | min-height: 100vh;
63 | color: var(--color-text);
64 | background: var(--color-background);
65 | transition:
66 | color 0.5s,
67 | background-color 0.5s;
68 | line-height: 1.6;
69 | font-family:
70 | Inter,
71 | -apple-system,
72 | BlinkMacSystemFont,
73 | 'Segoe UI',
74 | Roboto,
75 | Oxygen,
76 | Ubuntu,
77 | Cantarell,
78 | 'Fira Sans',
79 | 'Droid Sans',
80 | 'Helvetica Neue',
81 | sans-serif;
82 | font-size: 15px;
83 | text-rendering: optimizeLegibility;
84 | -webkit-font-smoothing: antialiased;
85 | -moz-osx-font-smoothing: grayscale;
86 | }
87 |
--------------------------------------------------------------------------------
/examples/convertpdf/office_thread.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | // Debugging note:
5 | // Switch the web worker in the browsers debug tab to debug this code.
6 | // It's the "em-pthread" web worker with the most memory usage, where "zetajs" is defined.
7 |
8 | // JS mode: module
9 | import { ZetaHelperThread } from './assets/vendor/zetajs/zetaHelper.js';
10 |
11 |
12 | // global variables - zetajs environment:
13 | const zHT = new ZetaHelperThread();
14 | const zetajs = zHT.zetajs;
15 | const css = zHT.css;
16 |
17 | // = global variables (some are global for easier debugging) =
18 | // common variables:
19 | let xModel;
20 | // example specific:
21 | let bean_hidden, bean_overwrite, bean_pdf_export, from, to;
22 |
23 | // Export variables for debugging. Available for debugging via:
24 | // globalThis.zetajsStore.threadJsContext
25 | export { zHT, xModel, bean_hidden, bean_overwrite, bean_pdf_export, from, to };
26 |
27 |
28 | function demo() {
29 | bean_hidden = new css.beans.PropertyValue({Name: 'Hidden', Value: true});
30 | bean_overwrite = new css.beans.PropertyValue({Name: 'Overwrite', Value: true});
31 | bean_pdf_export = new css.beans.PropertyValue({Name: 'FilterName', Value: 'writer_pdf_Export'});
32 |
33 | zHT.thrPort.onmessage = (e) => {
34 | switch (e.data.cmd) {
35 | case 'convert':
36 | try {
37 | // Close old document in advance. Keep document open afterwards for debugging.
38 | if (xModel !== undefined &&
39 | xModel.queryInterface(zetajs.type.interface(css.util.XCloseable))) {
40 | xModel.close(false);
41 | }
42 | from = e.data.from;
43 | to = e.data.to;
44 | xModel = zHT.desktop.loadComponentFromURL('file://' + from, '_blank', 0, [bean_hidden]);
45 | xModel.storeToURL( 'file://' + to, [bean_overwrite, bean_pdf_export]);
46 | zetajs.mainPort.postMessage({cmd: 'converted', name: e.data.name, from, to});
47 | } catch (e) {
48 | const exc = zetajs.catchUnoException(e);
49 | console.log('TODO', zetajs.getAnyType(exc), exc.Message);
50 | }
51 | break;
52 | default:
53 | throw Error('Unknown message command: ' + e.data.cmd);
54 | }
55 | }
56 |
57 | zHT.thrPort.postMessage({cmd: 'start'});
58 | }
59 |
60 | demo(); // launching demo
61 |
62 | /* vim:set shiftwidth=2 softtabstop=2 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
63 |
--------------------------------------------------------------------------------
/examples/simple-examples/simple_key_handler.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | // Debugging note:
5 | // Switch the web worker in the browsers debug tab to debug this code.
6 | // It's the "em-pthread" web worker with the most memory usage, where "zetajs" is defined.
7 |
8 | "use strict";
9 |
10 |
11 | // global variables - zetajs environment:
12 | let zetajs, css;
13 |
14 | // = global variables (some are global for easier debugging) =
15 | // common variables:
16 | let context, desktop, xModel, toolkit;
17 | // example specific:
18 | let myXKeyHandler, evtPressed, evtReleased, xController;
19 |
20 |
21 | function demo() {
22 | console.log('PLUS: execute example code');
23 |
24 | /* Implements com.sun.star.awt.XKeyHandler
25 | * Outputs printable characters typed into the OfficeDocument.
26 | * Browser console is used for output.
27 | */
28 | myXKeyHandler = zetajs.unoObject(
29 | [css.awt.XKeyHandler],
30 | {
31 | keyPressed(e) {
32 | console.log('key pressed (' + e.KeyCode + "): " + e.KeyChar);
33 | evtPressed = e;
34 | return false; // false: don't consume (run other event handlers)
35 | },
36 | keyReleased(e) {
37 | console.log('key released (' + e.KeyCode + "): " + e.KeyChar);
38 | evtReleased = e;
39 | return false; // false: don't consume (run other event handlers)
40 | }
41 | });
42 |
43 | context = zetajs.getUnoComponentContext();
44 | desktop = css.frame.Desktop.create(context);
45 | // Open a new writer document.
46 | // xModel is somethink like: SwXTextDocument, ScModelObj, SdXImpressDocument
47 | xModel = desktop.loadComponentFromURL('private:factory/swriter', '_default', 0, []);
48 | xController = xModel.getCurrentController();
49 |
50 | toolkit = css.awt.Toolkit.create(context);
51 | setInterval(function() {try {toolkit.getActiveTopWindow().FullScreen = true} catch {}}, 1000);
52 |
53 | xController.addKeyHandler(myXKeyHandler);
54 | // addKeyListener != addKeyHandler (that's something very DIFFERENT)
55 |
56 | const xText = xModel.getText();
57 | const xTextCursor = xText.createTextCursor();
58 | xTextCursor.setString("Open browser console and type something in LibreOffice!");
59 | }
60 |
61 |
62 | Module.zetajs.then(function(pZetajs) {
63 | // initializing zetajs environment:
64 | zetajs = pZetajs;
65 | css = zetajs.uno.com.sun.star;
66 | demo(); // launching demo
67 | });
68 |
69 | /* vim:set shiftwidth=2 softtabstop=2 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
70 |
--------------------------------------------------------------------------------
/examples/simple-examples/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 | ZetaJS Demo: Simple Example
14 |
18 |
19 |
20 |
21 |
29 |
37 |
38 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/examples/simple-examples/rainbow_writer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 | ZetaJS Demo: Rainbow Writer
14 |
18 |
19 |
20 |
21 |
29 |
37 |
38 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/examples/simple-examples/find_and_replace.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 | ZetaJS Demo: Find and Replace
14 |
18 |
19 |
20 |
21 |
29 |
37 |
38 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/examples/simple-examples/simple_key_handler.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 | ZetaJS Demo: Simple Key Handler
14 |
18 |
19 |
20 |
21 |
29 |
37 |
38 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/examples/standalone/office_thread.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | // Debugging note:
5 | // Switch the web worker in the browsers debug tab to debug this code.
6 | // It's the "em-pthread" web worker with the most memory usage, where "zetajs" is defined.
7 |
8 | 'use strict';
9 |
10 |
11 | // global variables - zetajs environment:
12 | let zetajs, css;
13 |
14 | // = global variables (some are global for easier debugging) =
15 | // common variables:
16 | let context, desktop, xModel, ctrl;
17 |
18 |
19 | function demo() {
20 | context = zetajs.getUnoComponentContext();
21 |
22 | // Turn off toolbars:
23 | const config = css.configuration.ReadWriteAccess.create(context, 'en-US');
24 | const uielems = config.getByHierarchicalName(
25 | '/org.openoffice.Office.UI.WriterWindowState/UIElements/States');
26 | for (const i of uielems.getElementNames()) {
27 | const uielem = uielems.getByName(i);
28 | if (uielem.getByName('Visible')) {
29 | uielem.setPropertyValue('Visible', false);
30 | }
31 | }
32 | config.commitChanges();
33 |
34 | desktop = css.frame.Desktop.create(context);
35 | xModel = desktop.loadComponentFromURL('private:factory/swriter', '_default', 0, [])
36 | ctrl = xModel.getCurrentController();
37 | ctrl.getFrame().getContainerWindow().FullScreen = true;
38 |
39 | ctrl.getFrame().LayoutManager.hideElement("private:resource/menubar/menubar");
40 |
41 | // Turn off sidebar:
42 | dispatch('.uno:Sidebar');
43 |
44 | for (const id of 'Bold Italic Underline'.split(' ')) {
45 | const urlObj = transformUrl('.uno:' + id);
46 | const listener = zetajs.unoObject([css.frame.XStatusListener], {
47 | disposing: function(source) {},
48 | statusChanged: function(state) {
49 | state = zetajs.fromAny(state.State);
50 | // Behave like desktop UI if a non uniformly formatted area is selected.
51 | if (typeof state !== 'boolean') state = false; // like desktop UI
52 | zetajs.mainPort.postMessage({cmd: 'setFormat', id, state});
53 | }
54 | });
55 | queryDispatch(urlObj).addStatusListener(listener, urlObj);
56 | }
57 |
58 | zetajs.mainPort.onmessage = function (e) {
59 | switch (e.data.cmd) {
60 | case 'toggleFormatting':
61 | dispatch('.uno:' + e.data.id);
62 | break;
63 | default:
64 | throw Error('Unknown message command: ' + e.data.cmd);
65 | }
66 | }
67 | zetajs.mainPort.postMessage({cmd: 'ui_ready'});
68 | }
69 |
70 | function transformUrl(unoUrl) {
71 | const ioparam = {val: new css.util.URL({Complete: unoUrl})};
72 | css.util.URLTransformer.create(context).parseStrict(ioparam);
73 | return ioparam.val;
74 | }
75 |
76 | function queryDispatch(urlObj) {
77 | return ctrl.queryDispatch(urlObj, '_self', 0);
78 | }
79 |
80 | function dispatch(unoUrl) {
81 | const urlObj = transformUrl(unoUrl);
82 | queryDispatch(urlObj).dispatch(urlObj, []);
83 | }
84 |
85 | Module.zetajs.then(function(pZetajs) {
86 | // initializing zetajs environment:
87 | zetajs = pZetajs;
88 | css = zetajs.uno.com.sun.star;
89 | demo(); // launching demo
90 | });
91 |
92 | /* vim:set shiftwidth=2 softtabstop=2 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
93 |
--------------------------------------------------------------------------------
/examples/ping-monitor/office_thread.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | // Debugging note:
5 | // Switch the web worker in the browsers debug tab to debug this code.
6 | // It's the "em-pthread" web worker with the most memory usage, where "zetajs" is defined.
7 |
8 | // JS mode: module
9 | import { ZetaHelperThread } from './assets/vendor/zetajs/zetaHelper.js';
10 |
11 |
12 | // global variables - zetajs environment:
13 | const zHT = new ZetaHelperThread();
14 | const zetajs = zHT.zetajs;
15 |
16 | // = global variables (some are global for easier debugging) =
17 | // common variables:
18 | let xModel, ctrl;
19 | // example specific:
20 | let activeSheet, cellRange, dataAry, oldUrl;
21 |
22 | // Export variables for debugging. Available for debugging via:
23 | // globalThis.zetajsStore.threadJsContext
24 | export { zHT, xModel, ctrl, activeSheet, cellRange, dataAry, oldUrl };
25 |
26 | const max_values = 20; // setting, adjust as needed
27 |
28 |
29 | function demo() {
30 | zHT.configDisableToolbars(['Calc']);
31 | xModel = zHT.desktop.loadComponentFromURL('file:///tmp/ping_monitor.ods', '_default', 0, []);
32 | ctrl = xModel.getCurrentController();
33 |
34 | // Turn off UI elements:
35 | zHT.dispatch(ctrl, 'Sidebar');
36 | zHT.dispatch(ctrl, 'InputLineVisible'); // FormulaBar at the top
37 | zHT.dispatch(ctrl, 'ViewRowColumnHeaders');
38 | const frame = ctrl.getFrame();
39 | frame.LayoutManager.hideElement("private:resource/statusbar/statusbar");
40 | frame.LayoutManager.hideElement("private:resource/menubar/menubar");
41 | ctrl.setPropertyValue('SheetTabs', false);
42 | ctrl.setPropertyValue('HasHorizontalScrollBar', false);
43 | ctrl.setPropertyValue('HasVerticalScrollBar', false);
44 | ctrl.getFrame().getContainerWindow().FullScreen = true;
45 |
46 | activeSheet = ctrl.getActiveSheet();
47 | cellRange = activeSheet.getCellRangeByPosition(0, 1, 0, max_values+1);
48 | dataAry = cellRange.getDataArray(); // 2 dimensional array
49 | zHT.thrPort.onmessage = (e) => {
50 | switch (e.data.cmd) {
51 | case 'ping_result':
52 | const newUrl = e.data.url;
53 | if (newUrl == oldUrl) {
54 | moveRows(dataAry);
55 | } else {
56 | clearRows(dataAry);
57 | }
58 | oldUrl = newUrl;
59 | setCell(dataAry[max_values-1], e.data.ping_value);
60 | cellRange.setDataArray(dataAry);
61 | break;
62 | default:
63 | throw Error('Unknown message command: ' + e.data.cmd);
64 | }
65 | }
66 | zHT.thrPort.postMessage({cmd: 'ui_ready'});
67 | }
68 |
69 |
70 | function moveRows(ary) {
71 | for (let i = 0; i < max_values-1; i++) {
72 | const writeCell = ary[i];
73 | const readCell = ary[i+1];
74 | const ping_value = readCell[0];
75 | setCell(writeCell, ping_value);
76 | }
77 | }
78 |
79 | function clearRows(ary) {
80 | for (let i = 0; i < max_values-1; i++) {
81 | setCell(ary[i], '');
82 | }
83 | }
84 |
85 | function setCell(cell, value) {
86 | let num = value; // keep original value
87 | if (typeof(value) === 'number' || !isNaN(num=parseFloat(value))) {
88 | cell[0] = num;
89 | } else {
90 | cell[0] = value.toString();
91 | }
92 | }
93 |
94 | demo(); // launching demo
95 |
96 | /* vim:set shiftwidth=2 softtabstop=2 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
97 |
--------------------------------------------------------------------------------
/examples/convertpdf/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/examples/vuejs3-ping-tool/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
ZetaJS Ping Tool
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
51 |
53 |
54 |
ZetaOffice is loading...
55 |
56 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
94 |
--------------------------------------------------------------------------------
/examples/simple-examples/rainbow_writer.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | // Debugging note:
5 | // Switch the web worker in the browsers debug tab to debug this code.
6 | // It's the "em-pthread" web worker with the most memory usage, where "zetajs" is defined.
7 |
8 | "use strict";
9 |
10 |
11 | // global variables - zetajs environment:
12 | let zetajs, css;
13 |
14 | // = global variables (some are global for easier debugging) =
15 | // common variables:
16 | let context, desktop, xModel, toolkit;
17 | // example specific:
18 | let uno_bold, uno_font_monospace;
19 |
20 | // [ red, orange, yellow, green, blue, violet]
21 | const rainbow = [0xE50000, 0xF08500, 0xFFEE00, 0x008121, 0x004CFF, 0x760188];
22 |
23 |
24 | function demo() {
25 | console.log('PLUS: execute example code');
26 |
27 | uno_bold = css.awt.FontWeight.BOLD;
28 | uno_font_monospace = "Monospace";
29 |
30 | context = zetajs.getUnoComponentContext();
31 | desktop = css.frame.Desktop.create(context);
32 | //// Open a new writer document.
33 | xModel = desktop.loadComponentFromURL('private:factory/swriter', '_default', 0, []);
34 |
35 | toolkit = css.awt.Toolkit.create(context);
36 | setInterval(function() {try {toolkit.getActiveTopWindow().FullScreen = true} catch {}}, 1000);
37 |
38 | const xController = xModel.getCurrentController();
39 | const xKeyHandler = zetajs.unoObject([css.awt.XKeyHandler], new ColorXKeyHandler(xModel));
40 | xController.addKeyHandler(xKeyHandler); // XUserInputInterception.addKeyHandler()
41 |
42 | const xTextCursor = xModel.getText().createTextCursor(); // XTextDocument.getText()
43 | xTextCursor.setPropertyValue("CharWeight", uno_bold); // XPropertySet.setPropertyValue()
44 | xTextCursor.setString("Please type something!\n\n");
45 | }
46 |
47 |
48 | function ColorXKeyHandler(xModel) {
49 | this.rainbow_i = 0;
50 | this.xModel = null;
51 | this.keyPressed = function(e) { return false; };
52 | this.keyReleased = function(e) {
53 | if (e.KeyChar === "\x00") { return false; } // non symbol keys (e.g. arrow keys)
54 |
55 | const xController = this.xModel.getCurrentController();
56 | const xTextViewCursor = xController.getViewCursor(); // XTextViewCursorSupplier.getViewCursor()
57 | const xText = this.xModel.getText(); // XTextDocument.getText()
58 | const xTextCursor = xText.createTextCursorByRange(xTextViewCursor.getStart());
59 | xTextCursor.goLeft(1, true);
60 |
61 | // Walk the rainbow ;-)
62 | const color = rainbow[this.rainbow_i];
63 | this.rainbow_i++;
64 | if (this.rainbow_i >= rainbow.length) { this.rainbow_i = 0; }
65 |
66 | xTextCursor.setPropertyValue("CharBackColor", color); // xPropertySet.setPropertyValue
67 | xTextCursor.setPropertyValue("CharWeight", uno_bold);
68 | xTextCursor.setPropertyValue("CharFontName", uno_font_monospace);
69 | // More properties:
70 | // https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1style_1_1CharacterProperties.html
71 |
72 | return false;
73 | };
74 |
75 | const xModel_types = xModel.getTypes(); // XTypeProvider.getTypes()
76 | for (let i=0; i'", quoted_url) )
49 | .pipe(gulp.dest(distDir))
50 | .pipe(browserSync.stream());
51 | }
52 |
53 |
54 | // Task: Compile JS
55 | function compileJS() {
56 | return gulp.src( './office_thread.js') //
57 | .pipe(beautify.js({ indent_size: 2, max_preserve_newlines: 2}))
58 | .pipe(gulp.dest(distDir))
59 | .pipe(browserSync.stream());
60 | }
61 |
62 |
63 | // Task: Copy Vendors
64 | function copyVendors() {
65 | let stream = mergeStream();
66 |
67 | stream.add( gulp.src( 'node_modules/zetajs/source/zeta.js' ).pipe( gulp.dest( distDir + 'assets/vendor/zetajs/' ) ) );
68 | stream.add( gulp.src( 'node_modules/zetajs/source/zetaHelper.js' ).pipe( gulp.dest( distDir + 'assets/vendor/zetajs/' ) ) );
69 |
70 | return stream;
71 | }
72 |
73 |
74 | // Init live server browser sync
75 | function initBrowserSync(done) {
76 | browserSync.init({
77 | server: {
78 | baseDir: distDir,
79 | middleware: function (req, res, next) {
80 | // required for WASM (SharedArray support)
81 | res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
82 | res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
83 | next();
84 | }
85 | },
86 | listen: custom_listen, // host ip
87 | port: custom_port,
88 | notify: false,
89 | browser: custom_browser,
90 | open: !no_browser
91 | });
92 | done();
93 | }
94 |
95 |
96 | // Watch files
97 | function watchFiles() {
98 | gulp.watch('*.html', compileHTML);
99 | gulp.watch('*.js', compileJS);
100 | }
101 |
102 |
103 | function setDebug(done) {
104 | soffice_base_url = '';
105 | done();
106 | }
107 |
108 |
109 | // Export tasks
110 | const dist = gulp.series(clean, gulp.parallel(compileHTML, compileJS, copyVendors) );
111 |
112 | exports.watch = gulp.series(dist, watchFiles);
113 | exports.start = gulp.series(dist, gulp.parallel(watchFiles, initBrowserSync) );
114 | exports.debug = gulp.series(setDebug, gulp.parallel(compileHTML, compileJS, copyVendors) );
115 | exports.default = dist;
116 |
117 | const fs = require('fs');
118 | const gulpfile_optional = 'config.js';
119 | if (fs.existsSync(gulpfile_optional)) {
120 | eval(fs.readFileSync(gulpfile_optional)+'');
121 | }
122 |
--------------------------------------------------------------------------------
/examples/web-office/gulpfile.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Load Plugins
4 | const browserSync = require("browser-sync").create();
5 | const del = require('del');
6 | const gulp = require('gulp');
7 | const argv = require('yargs').argv;
8 | const beautify = require('gulp-beautify');
9 | const inject = require('gulp-inject-string');
10 | const mergeStream = require('merge-stream');
11 |
12 | /**
13 | * Set the destination/production directory
14 | * This is where the project is compiled and exported for production.
15 | * This folder is auto created and managed by gulp.
16 | * Do not add/edit/save any files or folders iside this folder. They will be deleted by the gulp tasks.
17 | */
18 | const distDir = './public/';
19 |
20 | // Variables can be adjusted via command line arguments. Example:
21 | // npm run start -- --clean_disabled --port 8080 --browser chromium
22 | // Overwrites in config.js have priority over command line arguments.
23 |
24 | var soffice_base_url = argv.soffice_base_url;
25 | // Set "" for same server. But empty strings are falsy, so check "undefined".
26 | if (typeof soffice_base_url === "undefined") soffice_base_url = 'https://cdn.zetaoffice.net/zetaoffice_latest/';
27 |
28 | var custom_browser = argv.browser; // else use default system browser
29 | var custom_listen = argv.listen || '127.0.0.1';
30 | var custom_port = argv.port || 3000;
31 | var clean_disabled = argv.clean_disabled;
32 | var no_browser = argv.nobrowser;
33 |
34 |
35 | // Clean up the dist folder before running any task
36 | function clean() {
37 | if (clean_disabled) return Promise.resolve();
38 | return del(distDir + '**/*');
39 | }
40 |
41 |
42 | // Task: Compile HTML
43 | function compileHTML() {
44 | let css_links ='';
45 |
46 | return gulp.src(['index.html'])
47 | .pipe( inject.replace('', css_links) )
48 | .pipe( inject.replace('', soffice_base_url) )
49 | .pipe(gulp.dest(distDir))
50 | .pipe(browserSync.stream());
51 | }
52 |
53 |
54 | // Task: Compile JS
55 | function compileJS() {
56 | return gulp.src( './office_thread.js') //
57 | .pipe(beautify.js({ indent_size: 2, max_preserve_newlines: 2}))
58 | .pipe(gulp.dest(distDir))
59 | .pipe(browserSync.stream());
60 | }
61 |
62 |
63 | // Task: Copy Vendors
64 | function copyVendors() {
65 | let stream = mergeStream();
66 |
67 | stream.add( gulp.src( 'node_modules/zetajs/source/zeta.js' ).pipe( gulp.dest( distDir + 'assets/vendor/zetajs/' ) ) );
68 | stream.add( gulp.src( 'node_modules/bootstrap/dist/css/bootstrap.min.css' ).pipe( gulp.dest( distDir + 'assets/vendor/bootstrap/' ) ) );
69 |
70 | return stream;
71 | }
72 |
73 |
74 | // Init live server browser sync
75 | function initBrowserSync(done) {
76 | browserSync.init({
77 | server: {
78 | baseDir: distDir,
79 | middleware: function (req, res, next) {
80 | // required for WASM (SharedArray support)
81 | res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
82 | res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
83 | next();
84 | }
85 | },
86 | listen: custom_listen, // host ip
87 | port: custom_port,
88 | notify: false,
89 | browser: custom_browser,
90 | open: !no_browser
91 | });
92 | done();
93 | }
94 |
95 |
96 | // Watch files
97 | function watchFiles() {
98 | gulp.watch('*.html', compileHTML);
99 | gulp.watch('*.js', compileJS);
100 | }
101 |
102 |
103 | function setDebug(done) {
104 | soffice_base_url = '';
105 | done();
106 | }
107 |
108 |
109 | // Export tasks
110 | const dist = gulp.series(clean, gulp.parallel(compileHTML, compileJS, copyVendors) );
111 |
112 | exports.watch = gulp.series(dist, watchFiles);
113 | exports.start = gulp.series(dist, gulp.parallel(watchFiles, initBrowserSync) );
114 | exports.debug = gulp.series(setDebug, gulp.parallel(compileHTML, compileJS, copyVendors) );
115 | exports.default = dist;
116 |
117 | const fs = require('fs');
118 | const gulpfile_optional = 'config.js';
119 | if (fs.existsSync(gulpfile_optional)) {
120 | eval(fs.readFileSync(gulpfile_optional)+'');
121 | }
122 |
--------------------------------------------------------------------------------
/examples/standalone/gulpfile.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Load Plugins
4 | const browserSync = require("browser-sync").create();
5 | const del = require('del');
6 | const gulp = require('gulp');
7 | const argv = require('yargs').argv;
8 | const beautify = require('gulp-beautify');
9 | const inject = require('gulp-inject-string');
10 | const mergeStream = require('merge-stream');
11 |
12 | /**
13 | * Set the destination/production directory
14 | * This is where the project is compiled and exported for production.
15 | * This folder is auto created and managed by gulp.
16 | * Do not add/edit/save any files or folders iside this folder. They will be deleted by the gulp tasks.
17 | */
18 | const distDir = './public/';
19 |
20 | // Variables can be adjusted via command line arguments. Example:
21 | // npm run start -- --clean_disabled --port 8080 --browser chromium
22 | // Overwrites in config.js have priority over command line arguments.
23 |
24 | var soffice_base_url = argv.soffice_base_url;
25 | // Set "" for same server. But empty strings are falsy, so check "undefined".
26 | if (typeof soffice_base_url === "undefined") soffice_base_url = 'https://cdn.zetaoffice.net/zetaoffice_latest/';
27 |
28 | var custom_browser = argv.browser; // else use default system browser
29 | var custom_listen = argv.listen || '127.0.0.1';
30 | var custom_port = argv.port || 3000;
31 | var clean_disabled = argv.clean_disabled;
32 | var no_browser = argv.nobrowser;
33 |
34 |
35 | // Clean up the dist folder before running any task
36 | function clean() {
37 | if (clean_disabled) return Promise.resolve();
38 | return del(distDir + '**/*');
39 | }
40 |
41 | // Task: Compile HTML
42 | function compileHTML() {
43 | let css_links ='';
44 | let js_links = '';
45 |
46 | return gulp.src(['index.html'])
47 | .pipe( inject.replace('', css_links) )
48 | .pipe( inject.replace('', js_links) )
49 | .pipe( inject.replace('', soffice_base_url) )
50 | .pipe(gulp.dest(distDir))
51 | .pipe(browserSync.stream());
52 | }
53 |
54 | // Task: Compile JS
55 | function compileJS() {
56 | return gulp.src( './office_thread.js') //
57 | .pipe(beautify.js({ indent_size: 2, max_preserve_newlines: 2}))
58 | .pipe(gulp.dest(distDir))
59 | .pipe(browserSync.stream());
60 | }
61 |
62 | // Task: Copy Vendors
63 | function copyVendors() {
64 | let stream = mergeStream();
65 |
66 | stream.add( gulp.src( 'node_modules/zetajs/source/zeta.js' ).pipe( gulp.dest( distDir + 'assets/vendor/zetajs/' ) ) );
67 | stream.add( gulp.src( 'node_modules/bootstrap/dist/css/bootstrap.min.css' ).pipe( gulp.dest( distDir + 'assets/vendor/bootstrap/' ) ) );
68 |
69 | return stream;
70 | }
71 |
72 | // Init live server browser sync
73 | function initBrowserSync(done) {
74 | browserSync.init({
75 | server: {
76 | baseDir: distDir,
77 | middleware: function (req, res, next) {
78 | // required for WASM (SharedArray support)
79 | res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
80 | res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
81 | next();
82 | }
83 | },
84 | listen: custom_listen, // host ip
85 | port: custom_port,
86 | notify: false,
87 | browser: custom_browser,
88 | open: !no_browser
89 | });
90 | done();
91 | }
92 |
93 | // Watch files
94 | function watchFiles() {
95 | gulp.watch('*.html', compileHTML);
96 | gulp.watch('*.js', compileJS);
97 | }
98 |
99 | function setDebug(done) {
100 | soffice_base_url = '';
101 | done();
102 | }
103 |
104 | // Export tasks
105 | const dist = gulp.series(clean, gulp.parallel(compileHTML, compileJS, copyVendors) );
106 |
107 | exports.watch = gulp.series(dist, watchFiles);
108 | exports.start = gulp.series(dist, gulp.parallel(watchFiles, initBrowserSync) );
109 | exports.debug = gulp.series(setDebug, gulp.parallel(compileHTML, compileJS, copyVendors) );
110 | exports.default = dist;
111 |
112 | const fs = require('fs');
113 | const gulpfile_optional = 'config.js';
114 | if (fs.existsSync(gulpfile_optional)) {
115 | eval(fs.readFileSync(gulpfile_optional)+'');
116 | }
117 |
--------------------------------------------------------------------------------
/examples/vuejs3-ping-tool/public/office_thread.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | // Debugging note:
5 | // Switch the web worker in the browsers debug tab to debug this code.
6 | // It's the "em-pthread" web worker with the most memory usage, where "zetajs" is defined.
7 |
8 | // JS mode: module
9 | import { ZetaHelperThread } from './assets/vendor/zetajs/zetaHelper.js';
10 |
11 |
12 | // global variables - zetajs environment:
13 | const zHT = new ZetaHelperThread();
14 | const zetajs = zHT.zetajs;
15 | const css = zHT.css;
16 | const desktop = zHT.desktop;
17 |
18 | // = global variables =
19 | // common variables:
20 | let xModel, ctrl;
21 | // example specific:
22 | let ping_line, xComponent, charLocale, formatNumber, formatText, activeSheet, cell;
23 |
24 | // Export variables for debugging. Available for debugging via:
25 | // globalThis.zetajsStore.threadJsContext
26 | export { zHT, xModel, ctrl, ping_line, xComponent, charLocale, formatNumber, formatText, activeSheet, cell };
27 |
28 |
29 | function demo() {
30 | zHT.configDisableToolbars(["Calc"]);
31 |
32 | xModel = desktop.loadComponentFromURL('file:///tmp/calc_ping_example.ods', '_default', 0, []);
33 | ctrl = xModel.getCurrentController();
34 | ctrl.getFrame().getContainerWindow().FullScreen = true;
35 |
36 | xComponent = ctrl.getModel();
37 | charLocale = xComponent.getPropertyValue('CharLocale');
38 | formatNumber = xComponent.getNumberFormats().
39 | queryKey('0', charLocale, false);
40 | formatText = xComponent.getNumberFormats().
41 | queryKey('@', charLocale, false);
42 |
43 | // Turn off UI elements:
44 | zHT.dispatch(ctrl, 'Sidebar');
45 | zHT.dispatch(ctrl, 'InputLineVisible'); // FormulaBar at the top
46 | ctrl.getFrame().LayoutManager.hideElement("private:resource/statusbar/statusbar");
47 | ctrl.getFrame().LayoutManager.hideElement("private:resource/menubar/menubar");
48 |
49 | for (const id of 'Bold Italic Underline'.split(' ')) {
50 | const urlObj = zHT.transformUrl(id);
51 | const listener = zetajs.unoObject([css.frame.XStatusListener], {
52 | disposing: function(source) {},
53 | statusChanged: function(state) {
54 | state = zetajs.fromAny(state.State);
55 | // Behave like desktop UI if a non uniformly formatted area is selected.
56 | if (typeof state !== 'boolean') state = false; // like desktop UI
57 | zetajs.mainPort.postMessage({cmd: 'setFormat', id, state: state});
58 | }
59 | });
60 | zHT.queryDispatch(ctrl, urlObj).addStatusListener(listener, urlObj);
61 | }
62 |
63 | activeSheet = ctrl.getActiveSheet();
64 | zHT.thrPort.onmessage = function (e) {
65 | switch (e.data.cmd) {
66 | case 'toggleFormatting':
67 | zHT.dispatch(ctrl, e.data.id);
68 | break;
69 | case 'ping_result':
70 | if (ping_line === undefined) {
71 | ping_line = 1; // start at line 1 (line 0 is the header)
72 | } else {
73 | ping_line = findEmptyRowInCol1(activeSheet);
74 | }
75 |
76 | const url = e.data.id['url'];
77 | cell = activeSheet.getCellByPosition(0, ping_line);
78 | cell.setPropertyValue('NumberFormat', formatText); // optional
79 | cell.setString((new URL(url)).hostname);
80 |
81 | cell = activeSheet.getCellByPosition(1, ping_line);
82 | let ping_value = String(e.data.id['data']);
83 | if (!isNaN(ping_value)) {
84 | cell.setPropertyValue('NumberFormat', formatNumber); // optional
85 | cell.setValue(parseFloat(ping_value));
86 | } else {
87 | // in case e.data.id['data'] contains an error message
88 | cell.setPropertyValue('NumberFormat', formatText); // optional
89 | cell.setString(ping_value);
90 | }
91 | break;
92 | default:
93 | throw Error('Unknown message command: ' + e.data.cmd);
94 | }
95 | }
96 | zHT.thrPort.postMessage({cmd: 'ui_ready'});
97 | }
98 |
99 | function findEmptyRowInCol1(activeSheet) {
100 | let str;
101 | let line = 0;
102 | while (str != "") {
103 | line++;
104 | str = activeSheet.getCellByPosition(0, line).getString();
105 | }
106 | return line;
107 | }
108 |
109 | demo(); // launching demo
110 |
111 | /* vim:set shiftwidth=2 softtabstop=2 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
112 |
--------------------------------------------------------------------------------
/examples/letter-address-tool/gulpfile.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Load Plugins
4 | const browserSync = require("browser-sync").create();
5 | const del = require('del');
6 | const gulp = require('gulp');
7 | const argv = require('yargs').argv;
8 | const beautify = require('gulp-beautify');
9 | const inject = require('gulp-inject-string');
10 | const mergeStream = require('merge-stream');
11 |
12 | /**
13 | * Set the destination/production directory
14 | * This is where the project is compiled and exported for production.
15 | * This folder is auto created and managed by gulp.
16 | * Do not add/edit/save any files or folders iside this folder. They will be deleted by the gulp tasks.
17 | */
18 | const distDir = './public/';
19 |
20 | // Variables can be adjusted via command line arguments. Example:
21 | // npm run start -- --clean_disabled --port 8080 --browser chromium
22 | // Overwrites in config.js have priority over command line arguments.
23 |
24 | var soffice_base_url = argv.soffice_base_url;
25 |
26 | var custom_browser = argv.browser; // else use default system browser
27 | var custom_listen = argv.listen || '127.0.0.1';
28 | var custom_port = argv.port || 3000;
29 | var clean_disabled = argv.clean_disabled;
30 | var no_browser = argv.nobrowser;
31 |
32 |
33 | // Clean up the dist folder before running any task
34 | function clean() {
35 | if (clean_disabled) return Promise.resolve();
36 | return del(distDir + '**/*');
37 | }
38 |
39 |
40 | // Task: Compile HTML
41 | function compileHTML() {
42 | let css_links ='';
43 |
44 | // Set "" for same server. But empty strings are falsy, so check "undefined".
45 | // If undefined, it's set to null without quotes to let zetaHelper set the URL.
46 | let quoted_url = null; // null, prevents additional quoting on "npm run start" reloads
47 | if (typeof soffice_base_url !== "undefined") quoted_url = "'" + soffice_base_url + "'";
48 |
49 | return gulp.src(['index.html'])
50 | .pipe( inject.replace('', css_links) )
51 | .pipe( inject.replace("''", quoted_url) )
52 | .pipe(gulp.dest(distDir))
53 | .pipe(browserSync.stream());
54 | }
55 |
56 |
57 | // Task: Compile JS
58 | function compileJS() {
59 | return gulp.src( './office_thread.js') //
60 | .pipe(beautify.js({ indent_size: 2, max_preserve_newlines: 2}))
61 | .pipe(gulp.dest(distDir))
62 | .pipe(browserSync.stream());
63 | }
64 |
65 |
66 | // Task: Copy Vendors
67 | function copyVendors() {
68 | let stream = mergeStream();
69 |
70 | stream.add( gulp.src( 'node_modules/zetajs/source/zeta.js' ).pipe( gulp.dest( distDir + 'assets/vendor/zetajs/' ) ) );
71 | stream.add( gulp.src( 'node_modules/zetajs/source/zetaHelper.js' ).pipe( gulp.dest( distDir + 'assets/vendor/zetajs/' ) ) );
72 | stream.add( gulp.src( 'node_modules/w3-css/3/w3.css' ).pipe( gulp.dest( distDir + 'assets/vendor/w3/' ) ) );
73 |
74 | stream.add( gulp.src( 'Modern_business_letter_sans_serif.ott' ).pipe( gulp.dest( distDir + 'assets/' ) ) );
75 |
76 | return stream;
77 | }
78 |
79 |
80 | // Init live server browser sync
81 | function initBrowserSync(done) {
82 | browserSync.init({
83 | server: {
84 | baseDir: distDir,
85 | middleware: function (req, res, next) {
86 | // required for WASM (SharedArray support)
87 | res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
88 | res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
89 | next();
90 | }
91 | },
92 | listen: custom_listen, // host ip
93 | port: custom_port,
94 | notify: false,
95 | browser: custom_browser,
96 | open: !no_browser
97 | });
98 | done();
99 | }
100 |
101 |
102 | // Watch files
103 | function watchFiles() {
104 | gulp.watch('*.html', compileHTML);
105 | gulp.watch('*.js', compileJS);
106 | }
107 |
108 |
109 | function setDebug(done) {
110 | soffice_base_url = '';
111 | done();
112 | }
113 |
114 |
115 | // Export tasks
116 | const dist = gulp.series(clean, gulp.parallel(compileHTML, compileJS, copyVendors) );
117 |
118 | exports.watch = gulp.series(dist, watchFiles);
119 | exports.start = gulp.series(dist, gulp.parallel(watchFiles, initBrowserSync) );
120 | exports.debug = gulp.series(setDebug, gulp.parallel(compileHTML, compileJS, copyVendors) );
121 | exports.default = dist;
122 |
123 | const fs = require('fs');
124 | const gulpfile_optional = 'config.js';
125 | if (fs.existsSync(gulpfile_optional)) {
126 | eval(fs.readFileSync(gulpfile_optional)+'');
127 | }
128 |
--------------------------------------------------------------------------------
/examples/ping-monitor/gulpfile.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Load Plugins
4 | const browserSync = require("browser-sync").create();
5 | const del = require('del');
6 | const gulp = require('gulp');
7 | const argv = require('yargs').argv;
8 | const beautify = require('gulp-beautify');
9 | const inject = require('gulp-inject-string');
10 | const mergeStream = require('merge-stream');
11 |
12 | /**
13 | * Set the destination/production directory
14 | * This is where the project is compiled and exported for production.
15 | * This folder is auto created and managed by gulp.
16 | * Do not add/edit/save any files or folders iside this folder. They will be deleted by the gulp tasks.
17 | */
18 | const distDir = './public/';
19 |
20 | // Variables can be adjusted via command line arguments. Example:
21 | // npm run start -- --clean_disabled --port 8080 --browser chromium
22 | // Overwrites in config.js have priority over command line arguments.
23 |
24 | var soffice_base_url = argv.soffice_base_url;
25 |
26 | var custom_browser = argv.browser; // else use default system browser
27 | var custom_listen = argv.listen || '127.0.0.1';
28 | var custom_port = argv.port || 3000;
29 | var clean_disabled = argv.clean_disabled;
30 | var no_browser = argv.nobrowser;
31 |
32 |
33 | // Clean up the dist folder before running any task
34 | function clean() {
35 | if (clean_disabled) return Promise.resolve();
36 | return del(distDir + '**/*');
37 | }
38 |
39 |
40 | // Task: Compile HTML
41 | function compileHTML() {
42 | let css_links ='';
43 | let js_links ='';
44 |
45 | // Set "" for same server. But empty strings are falsy, so check "undefined".
46 | // If undefined, it's set to null without quotes to let zetaHelper set the URL.
47 | let quoted_url = null; // null, prevents additional quoting on "npm run start" reloads
48 | if (typeof soffice_base_url !== "undefined") quoted_url = "'" + soffice_base_url + "'";
49 |
50 | return gulp.src(['index.html'])
51 | .pipe( inject.replace('', css_links) )
52 | .pipe( inject.replace('', js_links) )
53 | .pipe( inject.replace("''", quoted_url) )
54 | .pipe(gulp.dest(distDir))
55 | .pipe(browserSync.stream());
56 | }
57 |
58 |
59 | // Task: Compile JS
60 | function compileJS() {
61 | return gulp.src( './office_thread.js') //
62 | .pipe(beautify.js({ indent_size: 2, max_preserve_newlines: 2}))
63 | .pipe(gulp.dest(distDir))
64 | .pipe(browserSync.stream());
65 | }
66 |
67 |
68 | // Task: Copy Vendors
69 | function copyVendors() {
70 | let stream = mergeStream();
71 |
72 | stream.add( gulp.src( 'node_modules/zetajs/source/zeta.js' ).pipe( gulp.dest( distDir + 'assets/vendor/zetajs/' ) ) );
73 | stream.add( gulp.src( 'node_modules/zetajs/source/zetaHelper.js' ).pipe( gulp.dest( distDir + 'assets/vendor/zetajs/' ) ) );
74 | stream.add( gulp.src( 'node_modules/ping.js/dist/ping.js' ).pipe( gulp.dest( distDir + 'assets/vendor/ping/' ) ) );
75 | stream.add( gulp.src( 'node_modules/bootstrap/dist/css/bootstrap.min.css' ).pipe( gulp.dest( distDir + 'assets/vendor/bootstrap/' ) ) );
76 |
77 | stream.add( gulp.src( 'pwa-icon-192x192.png' ).pipe( gulp.dest( distDir + 'assets/' ) ) );
78 | stream.add( gulp.src( 'pwa-manifest.json' ).pipe( gulp.dest( distDir + 'assets/' ) ) );
79 | stream.add( gulp.src( 'ping_monitor.ods' ).pipe( gulp.dest( distDir + 'assets/' ) ) );
80 |
81 | return stream;
82 | }
83 |
84 |
85 | // Init live server browser sync
86 | function initBrowserSync(done) {
87 | browserSync.init({
88 | server: {
89 | baseDir: distDir,
90 | middleware: function (req, res, next) {
91 | // required for WASM (SharedArray support)
92 | res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
93 | res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
94 | next();
95 | }
96 | },
97 | listen: custom_listen, // host ip
98 | port: custom_port,
99 | notify: false,
100 | browser: custom_browser,
101 | open: !no_browser
102 | });
103 | done();
104 | }
105 |
106 |
107 | // Watch files
108 | function watchFiles() {
109 | gulp.watch('*.html', compileHTML);
110 | gulp.watch('*.js', compileJS);
111 | }
112 |
113 |
114 | function setDebug(done) {
115 | soffice_base_url = '';
116 | done();
117 | }
118 |
119 |
120 | // Export tasks
121 | const dist = gulp.series(clean, gulp.parallel(compileHTML, compileJS, copyVendors) );
122 |
123 | exports.watch = gulp.series(dist, watchFiles);
124 | exports.start = gulp.series(dist, gulp.parallel(watchFiles, initBrowserSync) );
125 | exports.debug = gulp.series(setDebug, gulp.parallel(compileHTML, compileJS, copyVendors) );
126 | exports.default = dist;
127 |
128 | const fs = require('fs');
129 | const gulpfile_optional = 'config.js';
130 | if (fs.existsSync(gulpfile_optional)) {
131 | eval(fs.readFileSync(gulpfile_optional)+'');
132 | }
133 |
--------------------------------------------------------------------------------
/docs/uno.md:
--------------------------------------------------------------------------------
1 | # The zetajs UNO Mapping
2 |
3 | The mapping between [UNO types](http://www.openoffice.org/udk/common/man/typesystem.html) and zetajs JavaScript types is as follows:
4 |
5 | - UNO `VOID`: As a UNO interface method return type it maps to JavaScript `void`. As a value type contained in a UNO `ANY` value it maps to the JavaScript Undefined type.
6 |
7 | - UNO `BOOLEAN` maps to the JavaScript Boolean type.
8 |
9 | - UNO `BYTE` maps to the JavaScript Number type's integer values in the interval from −27 (inclusive) to 27 (exclusive).
10 |
11 | - UNO `SHORT` maps to the JavaScript Number type's integer values in the interval from −215 (inclusive) to 215 (exclusive).
12 |
13 | - UNO `UNSIGNED SHORT` maps to the JavaScript Number type's integer values in the interval from 0 (inclusive) to 216 (exclusive).
14 |
15 | - UNO `LONG` maps to the JavaScript Number type's integer values in the interval from −231 (inclusive) to 231 (exclusive).
16 |
17 | - UNO `UNSIGNED LONG` maps to the JavaScript Number type's integer values in the interval from 0 (inclusive) to 232 (exclusive).
18 |
19 | - UNO `HYPER` maps to the JavaScript BigInt type's values in the interval from −263 (inclusive) to 263 (exclusive).
20 |
21 | - UNO `UNSIGNED HYPER` maps to the JavaScript BigInt type's values in the interval from 0 (inclusive) to 264 (exclusive).
22 |
23 | - UNO `FLOAT` maps to the JavaScript Number type`s values that correspond to values of the IEEE 754 binary32 interchange format.
24 |
25 | - UNO `DOUBLE` maps to the JavaScript Number type.
26 |
27 | - UNO `CHAR` maps to the JavaScript String type's one-element values.
28 |
29 | - UNO `STRING` maps to the JavaScript String type's values that correspond to valid UTF-16 strings, up to the JavaScript length limit of 253−1 UTF16 code units.
30 |
31 | - UNO `TYPE` maps to opaque JavaScript objects, see the documentation of `zetajs.type` at [Starting Points: Using zetajs](start.md#using-zetajs).
32 |
33 | - UNO `ANY` maps to the combined set of wrapped and unwrapped representations:
34 |
35 | - Any value of UNO type `ANY` can map to and from a wrapped representation, which is an opaque JavaScript object that has a `type` property (containing a zetajs representation of a value of UNO type `TYPE`) and a `val` property (containing a zetajs representation of a value of the given UNO type).
36 |
37 | - A value of UNO type `ANY` where the contained UNO value is of any of the UNO types `VOID`, `BOOLEAN`, `LONG`, `HYPER`, `STRING`, `TYPE`, a UNO enum type, a UNO struct type, or a UNO exception type, can also map to and from an unwrapped representation, which directly maps to the JavaScript representation of the contained UNO value.
38 |
39 | - A value of UNO type `ANY` where the contained UNO value is of any of the UNO types `BYTE`, `SHORT`, `UNSIGNED SHORT`, `UNSIGNED LONG`, `UNSIGNED HYPER`, `FLOAT`, `DOUBLE`, `CHAR`, a UNO sequence type, or a UNO interface type, can also map to an unwrapped representation, which directly maps to the JavaScript representation of the contained UNO value. In the opposite direction:
40 |
41 | - JavaScript Number values with integer values in the interval 263 (inclusive) to 264 (exclusive) map to UNO `ANY` values with contained values of UNO type `UNSIGNED LONG`.
42 |
43 | - JavaScript Number values not covered by the preceding cases for `LONG` and `UNSIGNED LONG` target types map to UNO `ANY` values with contained values of UNO type `DOUBLE`.
44 |
45 | - JavaScript BigInt values that are larger than 263 − 1 map to UNO `ANY` values with contained values of UNO type `UNSIGNED HYPER`.
46 |
47 | - JavaScript Array values map to UNO `ANY` values with contained values of UNO type “sequence of `ANY`”.
48 |
49 | - JavaScript Null values and JavaScript objects representing UNO objects map to UNO `ANY` values with contained values of UNO interface type `com.sun.star.uno.XInterface`.
50 |
51 | Also see the documentation of `zetajs.Any`, `zetajs.getAnyType`, and `zetajs.fromAny` at [Starting Points: Using zetajs](start.md#using-zetajs).
52 |
53 | - UNO sequence types map to JavaScript Arrays with corresponding element value constraints, up to the JavaScript length limit of 232−1 elements.
54 |
55 | - UNO struct and exception types map to opaque JavaScript objects, where each member with name N maps to a property with property name N and with corresponding value constraints. See the documentation of the `zetajs.uno` dictionary at [Starting Points: Using zetajs](start.md#using-zetajs) for corresponding constructor functions.
56 |
57 | - UNO interface types map to the JavaScript Null type for null references, and to opaque JavaScript objects for non-null references. (A zetajs representation referencing a given UNO object has direct access to all the UNO interface methods and attribute getters and setters implemented by that object. Thus, such zetajs representations are not tied to specific UNO interface types.)
58 |
--------------------------------------------------------------------------------
/examples/letter-address-tool/office_thread.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | // Debugging note:
5 | // Switch the web worker in the browsers debug tab to debug this code.
6 | // It's the "em-pthread" web worker with the most memory usage, where "zetajs" is defined.
7 |
8 | // JS mode: script
9 | 'use strict';
10 |
11 |
12 | // global variables - zetajs environment:
13 | let zetajs, css;
14 |
15 | // = global variables (some are global for easier debugging) =
16 | // common variables:
17 | let zHT, desktop, xModel, ctrl;
18 |
19 |
20 | function demo() {
21 | const bean_overwrite = new css.beans.PropertyValue({Name: 'Overwrite', Value: true});
22 | const bean_odt_export = new css.beans.PropertyValue({Name: 'FilterName', Value: 'writer8'});
23 | const bean_pdf_export = new css.beans.PropertyValue({Name: 'FilterName', Value: 'writer_pdf_Export'});
24 | zHT.configDisableToolbars(["Writer"]);
25 |
26 | loadFile();
27 | // Turn off UI elements.
28 | // Permanant settings. Don't run again on a document reload.
29 | zHT.dispatch(ctrl, 'Sidebar');
30 | zHT.dispatch(ctrl, 'Ruler');
31 |
32 | zHT.thrPort.onmessage = function (e) {
33 | switch (e.data.cmd) {
34 | case 'download':
35 | const format = e.data.id === 'btnOdt' ? bean_odt_export : bean_pdf_export;
36 | xModel.storeToURL( 'file:///tmp/output', [bean_overwrite, format]);
37 | zetajs.mainPort.postMessage({cmd: 'download', id: e.data.id});
38 | break;
39 | case 'reload':
40 | xModel.close(true)
41 | loadFile();
42 | break;
43 | case 'toggleFormat':
44 | zHT.dispatch(ctrl, e.data.id);
45 | break;
46 | case 'insert_address':
47 | const recipient = e.data.recipient;
48 | const fieldsEnum = xModel.getTextFields().createEnumeration();
49 | let state_count=0, city_count=0, postal_code_count=0, street_count=0;
50 | while (fieldsEnum.hasMoreElements()) {
51 | const field = fieldsEnum.nextElement().getAnchor();
52 | switch (field.getString()) {
53 | case "": // additional space is needed
54 | field.setString(recipient.title === '' ? '' : recipient.title+' '); // recipient
55 | break;
56 | case "":
57 | field.setString(recipient.name);
58 | break;
59 | case "":
60 | field.setString(recipient.street);
61 | break;
62 | case "": // additional space is needed
63 | field.setString(recipient.postal_code+' ');
64 | break;
65 | case "":
66 | field.setString(recipient.city);
67 | break;
68 | case "":
69 | field.setString(recipient.state);
70 | break;
71 | case "":
72 | field.setString("Dent, Arthur Phillip");
73 | break;
74 | case "":
75 | field.setString("Cottingshire Radio");
76 | break;
77 | case "":
78 | field.setString("155 Country Lane");
79 | break;
80 | case "": // additional space is needed
81 | field.setString("2A 2A2A"+' ');
82 | break;
83 | case "":
84 | field.setString("Cottington");
85 | break;
86 | case "":
87 | field.setString("Cottingshire County");
88 | break;
89 | }
90 | }
91 | break;
92 | default:
93 | throw Error('Unknown message command: ' + e.data.cmd);
94 | }
95 | }
96 | }
97 |
98 | function loadFile() {
99 | const in_path = 'file:///tmp/Modern_business_letter_sans_serif.ott'
100 | xModel = desktop.loadComponentFromURL(in_path, '_default', 0, []);
101 | ctrl = xModel.getCurrentController();
102 | const frame = ctrl.getFrame();
103 |
104 | // Turn off UI elements (idempotent operations):
105 | frame.LayoutManager.hideElement("private:resource/statusbar/statusbar");
106 | frame.LayoutManager.hideElement("private:resource/menubar/menubar");
107 |
108 | frame.getContainerWindow().FullScreen = true;
109 |
110 | for (const id of 'Bold Italic Underline'.split(' ')) {
111 | const urlObj = zHT.transformUrl(id);
112 | const listener = zetajs.unoObject([css.frame.XStatusListener], {
113 | disposing: function(source) {},
114 | statusChanged: function(state) {
115 | state = zetajs.fromAny(state.State);
116 | // Behave like desktop UI if a non uniformly formatted area is selected.
117 | if (typeof state !== 'boolean') state = false;
118 | zetajs.mainPort.postMessage({cmd: 'setFormat', id, state});
119 | }
120 | });
121 | zHT.queryDispatch(ctrl, urlObj).addStatusListener(listener, urlObj);
122 | }
123 | zetajs.mainPort.postMessage({cmd: 'ui_ready'});
124 | }
125 |
126 |
127 | import('./assets/vendor/zetajs/zetaHelper.js').then(zetaHelper => {
128 | zHT = new zetaHelper.ZetaHelperThread();
129 | zetajs = zHT.zetajs;
130 | css = zHT.css;
131 | desktop = zHT.desktop;
132 | demo(); // launching demo
133 | });
134 |
135 | /* vim:set shiftwidth=2 softtabstop=2 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
136 |
--------------------------------------------------------------------------------
/examples/vuejs3-ping-tool/public/pre_soffice.js:
--------------------------------------------------------------------------------
1 | /* -*- Mode: JS; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2; fill-column: 100 -*- */
2 | // SPDX-License-Identifier: MIT
3 |
4 | import { ZetaHelperMain } from './assets/vendor/zetajs/zetaHelper.js';
5 |
6 |
7 | let thrPort; // zetajs thread communication
8 | let tbDataJs; // toolbar dataset passed from vue.js for plain JS
9 | window.PingModule = null; // Ping module passed from vue.js for plain JS
10 |
11 | const loadingInfo = document.getElementById('loadingInfo');
12 | const canvas = document.getElementById('qtcanvas');
13 | const pingTarget = document.getElementById("ping_target");
14 | const btnPing = document.getElementById("btnPing");
15 | const disabledElementsAry = [btnPing];
16 |
17 | // IMPORTANT:
18 | // Set base URL to the soffice.* files.
19 | // Use an empty string if those files are in the same directory.
20 | let wasmPkg;
21 | try {
22 | wasmPkg = 'url:' + config_soffice_base_url; // May fail. config.js is optional.
23 | } catch {}
24 | const zHM = new ZetaHelperMain('office_thread.js', {threadJsType: 'module', wasmPkg});
25 |
26 |
27 | // Functions stored below window.* are usually accessed from vue.js.
28 |
29 | window.jsPassCtrlBar = function(pTbDataJs) {
30 | tbDataJs = pTbDataJs;
31 | disabledElementsAry.push(tbDataJs);
32 | }
33 |
34 | window.toggleFormatting = function(id) {
35 | setToolbarActive(id, !tbDataJs.active[id]);
36 | thrPort.postMessage({cmd: 'toggleFormatting', id});
37 | // Give focus to the LO canvas to avoid issues with
38 | // "Setting Bold is
39 | // undone when clicking into non-empty document" when the user would need to click
40 | // into the canvas to give back focus to it:
41 | canvas.focus();
42 | }
43 |
44 | function setToolbarActive(id, value) {
45 | tbDataJs.active[id] = value;
46 | // Need to set "active" on "tbDataJs" to trigger an UI update.
47 | tbDataJs.active = tbDataJs.active;
48 | }
49 |
50 | let dbgPingData;
51 | function pingResult(url, err, data) {
52 | dbgPingData = {data, err};
53 | const hostname = (new URL(url)).hostname;
54 | let output = data;
55 | // If /favicon.ico can't be loaded the result still represents the response time.
56 | if (err) output = hostname + ": " + output + " " + err;
57 | thrPort.postMessage({cmd: 'ping_result', id:{url, data} });
58 | }
59 |
60 | let pingInst;
61 | const urls_ary = ["https://documentfoundation.org/", "https://ip4.me/", "https://allotropia.de/"];
62 | let urls_ary_i = 0;
63 | function pingExamples(err, data) {
64 | let url = urls_ary[urls_ary_i];
65 | pingResult(url, err, data);
66 | url = urls_ary[++urls_ary_i];
67 | if (typeof url !== 'undefined') {
68 | setTimeout(function() { // make the demo look more interesting ;-)
69 | pingInst.ping(url, function(err_rec, data_rec) {
70 | pingExamples(err_rec, data_rec);
71 | });
72 | }, 1000); // milliseconds
73 | }
74 | }
75 |
76 | function btnPingFunc() {
77 | // Using Ping callback interface.
78 | let url = pingTarget.value;
79 | if (!url.startsWith('http')) {
80 | url = 'http://' + url;
81 | }
82 | pingInst.ping(url, function(err, data) {
83 | pingResult(url, err, data);
84 | });
85 | }
86 | btnPing.onclick = btnPingFunc;
87 |
88 |
89 | async function get_calc_ping_example_ods() {
90 | const response = await fetch("./calc_ping_example.ods");
91 | return response.arrayBuffer();
92 | }
93 |
94 |
95 | zHM.start(function() {
96 | // Should run after App.vue has set PingModule but before demo().
97 | // 'Cross-Origin-Embedder-Policy': Ping seems to work with 'require-corp' without
98 | // acutally having CORP on foreign origins.
99 | // Also 'credentialless' isn't supported by Safari-18 as of 2024-09.
100 | pingInst = new window.PingModule();
101 |
102 | thrPort = zHM.thrPort;
103 | thrPort.onmessage = function(e) {
104 | switch (e.data.cmd) {
105 | case 'setFormat':
106 | setToolbarActive(e.data.id, e.data.state);
107 | break;
108 | case 'ui_ready':
109 | // Trigger resize of the embedded window to match the canvas size.
110 | // May somewhen be obsoleted by:
111 | // https://gerrit.libreoffice.org/c/core/+/174040
112 | window.dispatchEvent(new Event('resize'));
113 | setTimeout(function() { // display Office UI properly
114 | loadingInfo.style.display = 'none';
115 | canvas.style.visibility = null;
116 | for (const elem of disabledElementsAry) elem.disabled = false;
117 | pingTarget.addEventListener ("keyup", (evt) => {
118 | if(evt.key === 'Enter') btnPingFunc();
119 | });
120 | // Using Ping callback interface.
121 | pingInst.ping(urls_ary[urls_ary_i], function() {
122 | // Continue after first ping, which is often exceptionally slow.
123 | setTimeout(function() { // small delay to make the demo more interesting
124 | pingInst.ping(urls_ary[urls_ary_i], function(err, data) {
125 | pingExamples(err, data);
126 | });
127 | }, 1000); // milliseconds
128 | });
129 | }, 1000); // milliseconds
130 | break;
131 | default:
132 | throw Error('Unknown message command: ' + e.data.cmd);
133 | }
134 | };
135 |
136 | get_calc_ping_example_ods().then(function(aryBuf) {
137 | zHM.FS.writeFile('/tmp/calc_ping_example.ods', new Uint8Array(aryBuf));
138 | });
139 | });
140 |
141 | /* vim:set shiftwidth=2 softtabstop=2 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
142 |
--------------------------------------------------------------------------------
/examples/letter-address-vuejs3/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
22 |
23 |
24 |
79 |
157 |
158 |
159 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # zetajs: Access ZetaOffice in the Browser from JavaScript via UNO
2 |
3 | The zetajs library provides the facilities to run an instance of ZetaOffice integrated in your
4 | web site, allowing you to control it with JavaScript code via the LibreOffice
5 | [UNO](https://wiki.documentfoundation.org/Documentation/DevGuide) technology.
6 |
7 | Use cases range from an in-browser office suite that looks and feels just like its desktop
8 | counterpart, to fine-tuned custom text editing and spreadsheet capabilities embedded in your web
9 | site, to a headless zetajs instance that does document conversion in the background.
10 |
11 | For a detailed description of zetajs, see the [Starting Points](docs/start.md) documentation.
12 |
13 | (Technically, zetajs provides a wrapper on top of the
14 | [Embind-based](https://blog.allotropia.de/2024/04/30/libreoffice-javascripted/) JavaScript scripting
15 | capabilities for LibreOffice. But it aims to provide a nicer, more idiomatic JavaScript experience,
16 | and completely hides the underlying machinery. In the future, it may even move away from that
17 | underlying Embind layer, in a backward-compatible way.)
18 |
19 | ## Using ZetaOffice
20 |
21 | Visit [zetaoffice.net](https://zetaoffice.net) to learn more about ZetaOffice, its CDN and how to host ZetaOffice yourself.
22 |
23 | ### Demo
24 | To see a demo of zetajs in use, visit [zetaoffice.net/#tryit](https://zetaoffice.net/#tryit).
25 |
26 | 
27 |
28 | ### Examples and test code
29 |
30 | Check out our examples. Each example has instructions how to run it in its respective folder.
31 |
32 | | Example | Description | Toolkits/Libraries | Online Demo |
33 | | --- | --- | --- | --- |
34 | | [standalone](https://github.com/allotropia/zetajs/tree/main/examples/standalone) | Standalone Writer document canvas with simple formatting options. *Simple code, easy to start with* | Bootstrap |https://zetaoffice.net/demos/standalone/ |
35 | | [letter-address-vuejs3](https://github.com/allotropia/zetajs/tree/main/examples/letter-address-vuejs3) | Web form letter demo | Vue, w3.css | https://zetaoffice.net/demos/letter-address-vuejs3/
36 | | [web-office](https://github.com/allotropia/zetajs/tree/main/examples/web-office) | Full office suite in the browser | Bootstrap | https://zetaoffice.net/demos/web-office/
37 | | [ping-monitor](https://github.com/allotropia/zetajs/tree/main/examples/ping-monitor) | Chart with values being added on the fly | Bootstrap, ping.js | https://zetaoffice.net/demos/ping-monitor/
38 | | [vuejs3-ping-tool](https://github.com/allotropia/zetajs/tree/main/examples/vuejs3-ping-tool) | Chart with values being added on the fly | Vue, Bootstrap, ping.js | https://zetaoffice.net/demos/vuejs3-ping-tool/
39 | | [convertpdf](https://github.com/allotropia/zetajs/tree/main/examples/convertpdf) | local file to PDF conversion service | Plain javascript | https://zetaoffice.net/demos/convertpdf/
40 | | [simple-examples](https://github.com/allotropia/zetajs/tree/main/examples/simple-examples) | small examples displaying various API features | Plain javascript
41 |
42 | These examples use the ZetaOffice CDN to get you started quickly.
43 |
44 | ## Why zetajs
45 |
46 | See how zetajs makes scripting ZetaOffice easy, building on the foundation of the LibreOffice UNO API:
47 |
48 | ### 1. Load a document
49 |
50 | ```javascript
51 | const css = zetajs.uno.com.sun.star;
52 | const desktop = css.frame.Desktop.create(zetajs.getUnoComponentContext());
53 | let xModel = desktop.getCurrentFrame().getController().getModel();
54 | if (!xModel?.queryInterface(zetajs.type.interface(css.text.XTextDocument))) {
55 | xModel = desktop.loadComponentFromURL(
56 | 'file:///android/default-document/example.odt', '_default', 0, []);
57 | }
58 | ```
59 |
60 | ### 2. Change each paragraph in Writer into a random color
61 |
62 | ```javascript
63 | const xText = xModel.getText();
64 | const xParaEnumeration = xText.createEnumeration();
65 | for (const xParagraph of xParaEnumeration) {
66 | const color = Math.floor(Math.random() * 0xFFFFFF);
67 | xParagraph.setPropertyValue("CharColor", color);
68 | }
69 | ```
70 |
71 | ## Using with an own build
72 |
73 | Please have a look into the respective config.sample.js file of each demo to use another ZetaOffice build.
74 |
75 | You may also compile a custom [LOWA build](https://git.libreoffice.org/core/+/refs/heads/master/static/README.wasm.md). There the folder `workdir/installation/LibreOffice/emscripten/` will contain the files for the web root. If you host the WASM binary on another origin then the example code you will need to set a [CORS header](https://developer.mozilla.org/docs/Web/HTTP/CORS).
76 |
77 | For the sources of the WASM binaries served by cdn.zetaoffice.net see:
78 | * https://git.libreoffice.org/core/+/refs/heads/distro/allotropia/zeta-24-2
79 | * https://github.com/allotropia/emscripten/commits/fixed-3.1.65
80 | * https://github.com/allotropia/qt5/tree/5.15.2%2Bwasm
81 | * https://github.com/allotropia/qtbase/tree/5.15.2%2Bwasm
82 |
83 | ## Contributions
84 |
85 | ### Submitting issues
86 |
87 | First off, please search existing issues first, before filing a new
88 | one (see [search on github](https://help.github.com/articles/searching-issues)).
89 |
90 | If you think you've found a security problem, feel free to contact one
91 | of the project maintainers in private, for responsible disclosure.
92 |
93 | ### Development and Code
94 |
95 | For any LibreOffice questions (code, API, features), you'll find us on
96 | the [LibreOffice IRC channel](https://web.libera.chat/?channels=libreoffice-dev) - changes
97 | to LibreOffice core then go through
98 | [TDF's development process and gerrit code review system](https://wiki.documentfoundation.org/Development/GetInvolved).
99 |
100 | For changes to the zetajs library, just raise a pull request here, and
101 | make sure you've got permission to contribute your changes under the
102 | MIT license. For that, we use the [Developer Certificate of Origin (DCO)](https://developercertificate.org/):
103 |
104 | ~~~
105 | Developer Certificate of Origin
106 | Version 1.1
107 |
108 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
109 |
110 | Everyone is permitted to copy and distribute verbatim copies of this
111 | license document, but changing it is not allowed.
112 |
113 |
114 | Developer's Certificate of Origin 1.1
115 |
116 | By making a contribution to this project, I certify that:
117 |
118 | (a) The contribution was created in whole or in part by me and I
119 | have the right to submit it under the open source license
120 | indicated in the file; or
121 |
122 | (b) The contribution is based upon previous work that, to the best
123 | of my knowledge, is covered under an appropriate open source
124 | license and I have the right under that license to submit that
125 | work with modifications, whether created in whole or in part
126 | by me, under the same open source license (unless I am
127 | permitted to submit under a different license), as indicated
128 | in the file; or
129 |
130 | (c) The contribution was provided directly to me by some other
131 | person who certified (a), (b) or (c) and I have not modified
132 | it.
133 |
134 | (d) I understand and agree that this project and the contribution
135 | are public and that a record of the contribution (including all
136 | personal information I submit with it, including my sign-off) is
137 | maintained indefinitely and may be redistributed consistent with
138 | this project or the open source license(s) involved.
139 | ~~~
140 |
141 | When submitting a pull request, to make this certification please
142 | therefore add a sign-off line to your commits:
143 |
144 | ~~~
145 | Signed-off-by: Random J Developer
146 | ~~~
147 |
148 | Use your real name (sorry, no pseudonyms or anonymous
149 | contributions).
150 |
151 | This project is tested with BrowserStack.
152 |
--------------------------------------------------------------------------------
/examples/standalone/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
13 |
14 |
15 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
Writer Document canvas
38 |
An example of a stripped-down, standalone Writer document canvas without any surrounding menubars, toolbars, side panels, etc.