import { libwebview } from 'libwebview-nodejs';
/** Class representing a Webview. */
export class Webview {
/** Create a webview.
* @constructor
* @param {boolean} debug enable DevTools and other debug features.
* @param target the destination window handle. set it to null if you want to create a new window
*/
constructor(debug = false, target = null) {
this.webview = libwebview.webview_create(debug ? 1 : 0, target);
this.isDebug = debug;
if (!this.webview) {
throw new Error("Failed to create webview");
}
}
/** Updates the title of the native window.
* Must be called from the UI thread.
* @param v the new title
*/
title(v) {
libwebview.webview_set_title(this.webview, v);
}
/** Navigates webview to the given URL
* URL may be a data URI, i.e. "data:text/text,...". It is often ok not to url-encode it properly, webview will re-encode it for you. Same as [navigate]
* @param url the URL or URI
* */
navigate(url) {
libwebview.webview_navigate(this.webview, url);
}
/** Set webview HTML directly.
* @param v the HTML content
*/
html(v) {
libwebview.webview_set_html(this.webview, v);
}
/**
* Updates the size of the native window.
* Accepts a WEBVIEW_HINT
* @param hints can be one of `NONE(=0)`, `MIN(=1)`, `MAX(=2)` or `FIXED(=3)`
*/
size(width, height, hints = SizeHint.None) {
libwebview.webview_set_size(this.webview, width, height, hints);
}
/**
* Injects JS code at the initialization of the new page.
* Every time the webview will open a new page - this initialization code will be executed. It is guaranteed that code is executed before window.onload.
* @param js the JS code
*/
init(js) {
libwebview.webview_init(this.webview, js);
}
/** Evaluates arbitrary JS code in browser.
* Evaluation happens asynchronously, also the result of the expression is ignored. Use the `bind` function if you want to receive notifications about the results of the evaluation.
* @param js the JS code
*/
eval(js) {
libwebview.webview_eval(this.webview, js);
}
/** Binds a native NodeJS callback so that it will appear under the given name as a global webview's JS function.
* Callback receives an Array from browser's JS. Request string is a JSON array of all the arguments passed to the JS function.
* @param name the name of the global browser's JS function
* @param fn the callback function receives the request parameter in webview browser and return the response(=[isSuccess,result]), both in JSON string. If isSuccess=false, it wll reject the Promise.
*/
bindRaw(name, fn) {
let callback = (seq, req, _arg) => {
const [isError, result] = fn(this, req);
libwebview.webview_return(this.webview, seq, isError, result);
};
libwebview.webview_bind(this.webview, name, null, callback);
process.on('exit', function () { callback; }); // Avoid GC
}
/** Binds a Node.js callback so that it will appear under the given name as a global JS function in webview .
* Example: `bind("sumInNodeJS",(webview, a,b) => { return a+b; });` in webview browser, you should call `await sumInNodeJS(1,2)` and get `3`
* @param name the name of the global browser's JS function
* @param fn the callback function which receives the parameter and return the result to Webview. Any exception happened in Node.js here will reject the `Promise` instead of crash the program.
*/
bind(name, fn) {
this.bindRaw(name, (w, req) => {
var _a;
let args = JSON.parse(req);
try {
const resultValue = (_a = fn(w, ...args)) !== null && _a !== void 0 ? _a : null; // convert undefined to null
const result = JSON.stringify(resultValue);
if (result === undefined) {
// need JSON.stringify to wrap string in quotes
return [1, JSON.stringify(`JSON.stringify failed for return value ${resultValue}`)];
}
return [0, result];
}
catch (error) {
// JSON.stringify(error) returns "[object Object]", call String to get message, need JSON.stringify to wrap string in quotes
return [1, JSON.stringify(String(error))];
}
});
}
/**
* Posts a function to be executed on the main thread.
* It safely schedules the callback to be run on the main thread on the next main loop iteration.
* @param fn the function to be executed on the main thread.
*/
dispatch(fn) {
throw "Not Implemeted";
// let callback = Callback('void',['pointer','pointer'], (_,arg) => {
// fn(this);
// });
// libwebview.webview_dispatch(this.webview ,callback);
// process.on('exit', function() { callback; });
}
/** Removes a callback that was previously set by `webview_bind`.
* @param name the name of JS function used in `webview_bind`
*/
unbind(name) {
libwebview.webview_unbind(this.webview, name);
}
/** Runs the main loop and destroy it when terminated.
* This will block the thread. Functions like `setInterval` won't work.
*/
show() {
this.start();
this.terminate();
this.destroy();
}
/** Runs the main loop until it's terminated. **After this function exits - you must destroy the webview**.
* This will block the thread.
*/
start() {
libwebview.webview_run(this.webview);
}
/** Destroy the webview and close the native window.
* You must destroy the webview after [run]
*/
destroy() {
libwebview.webview_destroy(this.webview);
this.webview = null;
}
/** Stops the main loop.
* It is safe to call this function from other background thread.
*/
terminate() {
libwebview.webview_terminate(this.webview);
}
/** An **unsafe** pointer to the webview
*/
get unsafeHandle() {
return this.webview;
}
/** An **unsafe** pointer to the webviews platform specific native window handle.
* An unsafe pointer to the webviews platform specific native window handle.
* When using GTK backend the pointer is `GtkWindow` pointer, when using Cocoa
* backend the pointer is `NSWindow` pointer, when using Win32 backend the
* pointer is `HWND` pointer.
*/
get unsafeWindowHandle() {
return libwebview.webview_get_window(this.webview);
}
}
/** Window size hints */
const SizeHint = new Proxy(
{
/** Width and height are default size */
None: 0,
/** Width and height are minimum bounds */
Min: 1,
/** Width and height are maximum bounds */
Max: 2,
/** Window size can not be changed by a user */
Fixed:3
},
{
get(obj, prop) {
if (!obj.hasOwnProperty(prop)) {
throw new Error(`"${prop}" does not exist in the SizeHint enum`)
}
return obj[prop]
},
set(obj, prop, value) {
throw new Error('Cannot add a new value to an enum')
}
}
)