const VT_CONTAINER = 1
const VT_LABEL = 2
const VT_BUTTON = 3
const VT_ENTRY = 4
const VT_GROUP = 5
const VT_PROGRESS_BAR = 6
const VT_SCROLL = 7
const VT_TEXT_EDIT = 8
const VT_IMAGE = 9;
const VT_BODY = 10;
const VT_PARAGRAPH = 11;
class Clipboard {
async readText() {
return clipboard_read_text();
}
async writeText(text) {
clipboard_write_text(text);
}
}
class StylesheetItem {
id;
constructor(id) {
this.id = id;
}
update(code) {
stylesheet_update(this.id, code);
}
}
class Stylesheet {
append(code) {
const id = stylesheet_add(code);
return new StylesheetItem(id);
}
remove(stylesheet) {
stylesheet_remove(stylesheet.id);
}
}
export class Navigator {
clipboard;
stylesheet;
constructor() {
this.clipboard = new Clipboard();
this.stylesheet = new Stylesheet();
}
}
export class Process {
exit(code) {
process_exit(code);
}
get argv() {
return process_argv();
}
get isMobilePlatform() {
return process_is_mobile_platform();
}
setPromiseRejectionTracker(handler) {
process_set_promise_rejection_tracker(handler);
}
}
export class FileDialog {
show(options) {
return new Promise((resolve, reject) => {
dialog_show_file_dialog({
dialogType: options.dialogType,
}, options.window?.handle, (result, data) => {
if (result) {
resolve(data);
} else {
reject(data);
}
})
})
}
}
export class Window {
#eventRegistry;
#eventBinder;
#windowHandle;
#body;
constructor(attrs = null) {
this.#windowHandle = Window_create(attrs || {});
this.#eventBinder = new EventBinder(this.#windowHandle, Window_bind_js_event_listener, Window_unbind_js_event_listener, this);
Window_set_js_context(this.#windowHandle, this);
this.#body = new BodyElement();
Window_set_body(this.#windowHandle, this.#body.handle);
}
static fromHandle(windowHandle) {
return Window_get_js_context(windowHandle);
}
get handle() {
return this.#windowHandle
}
get body() {
return this.#body;
}
set title(title) {
Window_set_title(this.#windowHandle, title);
}
get title() {
return Window_get_title(this.#windowHandle);
}
resize(size) {
Window_resize(this.#windowHandle, size);
}
drag() {
Window_drag(this.#windowHandle);
}
get minimized() {
return Window_is_minimized(this.#windowHandle);
}
set minimized(minimized) {
Window_set_minimized(this.#windowHandle, minimized);
}
get maximized() {
return Window_is_maximized(this.#windowHandle);
}
set maximized(maximized) {
Window_set_maximized(this.#windowHandle, maximized);
}
setModal(owner) {
Window_set_modal(this.#windowHandle, owner.#windowHandle)
}
close() {
Window_close(this.#windowHandle);
}
set visible(visible) {
Window_set_visible(this.#windowHandle, visible);
}
get visible() {
return Window_is_visible(this.#windowHandle);
}
requestFullscreen() {
Window_request_fullscreen(this.#windowHandle);
}
exitFullscreen() {
Window_exit_fullscreen(this.#windowHandle);
}
bindResize(callback) {
this.#eventBinder.bindEvent("resize", callback);
}
bindClose(callback) {
this.bindEvent("close", callback);
}
bindFocus(callback) {
this.bindEvent("focus", callback);
}
bindBlur(callback) {
this.bindEvent("blur", callback);
}
bindEvent(type, callback) {
this.#eventBinder.bindEvent(type, callback);
}
addEventListener(type, callback) {
this.#eventBinder.addEventListener(type, callback);
}
removeEventListener(type, callback) {
this.#eventBinder.removeEventListener(type, callback);
}
}
export class EventObject {
_propagationCancelled = false
_preventDefault = false
type;
detail;
target;
currentTarget;
constructor(type, detail, target, currentTarget) {
this.type = type;
this.detail = detail;
this.target = target;
this.currentTarget = currentTarget;
}
stopPropagation() {
this._propagationCancelled = true;
}
preventDefault() {
this._preventDefault = true;
}
result() {
return {
propagationCancelled: this._propagationCancelled,
preventDefault: this._preventDefault,
}
}
}
export class EventRegistry {
eventListeners = Object.create(null);
_id;
_remove_api;
_add_api;
#contextGetter;
#self;
constructor(id, addApi, removeApi, self, contextGetter) {
this._id = id;
this._add_api = addApi;
this._remove_api = removeApi;
this.#contextGetter = contextGetter;
this.#self = self;
}
bindEvent(type, callback) {
type = type.toLowerCase();
if (typeof callback !== "function") {
throw new Error("invalid callback");
}
let oldListenerId = this.eventListeners[type];
if (oldListenerId) {
this._remove_api(this._id, type, oldListenerId);
}
const getJsContext = (target) => {
if (target && this.#contextGetter) {
return this.#contextGetter(target);
}
return target;
}
const self = this.#self;
function eventCallback(type, detail, target) {
const event = new EventObject(type, detail, getJsContext(target), self);
try {
callback && callback(event);
} catch (error) {
console.error(`${type} event handling error, detail=`, detail ,error.message || error);
}
return event.result();
}
this.eventListeners[type] = this._add_api(this._id, type, eventCallback);
}
}
export class EventBinder {
#eventListeners = Object.create(null);
#target;
#removeEventListenerApi;
#addEventListenerApi;
#contextGetter;
#self;
#allEventListeners = Object.create(null);
constructor(target, addApi, removeApi, self, contextGetter) {
this.#target = target;
this.#addEventListenerApi = addApi;
this.#removeEventListenerApi = removeApi;
this.#contextGetter = contextGetter;
this.#self = self;
}
bindEvent(type, callback) {
type = type.toLowerCase();
if (typeof callback !== "function") {
throw new Error("invalid callback");
}
let oldListener = this.#eventListeners[type];
if (oldListener) {
this.removeEventListener(type, oldListener);
}
this.addEventListener(type, callback);
this.#eventListeners[type] = callback;
}
addEventListener(type, callback) {
const getJsContext = (target) => {
try {
if (target && this.#contextGetter) {
return this.#contextGetter(target);
}
return target;
} catch (error) {
return target;
}
}
const self = this.#self;
function eventCallback(detail, target) {
const event = new EventObject(type, detail, getJsContext(target), self);
try {
callback && callback(event);
} catch (error) {
console.error(`${type} event handling error, detail=`, detail ,error.message || error);
}
return event.result();
}
if (!this.#allEventListeners[type]) {
this.#allEventListeners[type] = new Map();
}
const id = this.#addEventListenerApi(this.#target, type, eventCallback);
this.#allEventListeners[type].set(callback, id);
return id;
}
removeEventListener(type, callback) {
const map = this.#allEventListeners[type];
const id = map.get(callback);
if (id) {
map.delete(callback);
this.#removeEventListenerApi(this.#target, id);
}
}
}
export class SystemTray {
#eventRegistry;
#menuUserCallback;
tray;
constructor() {
this.tray = SystemTray_create("Test");
this.#eventRegistry = new EventRegistry(this.tray, SystemTray_bind_event, SystemTray_remove_event_listener, this);
}
set title(title) {
SystemTray_set_title(this.tray, title);
}
set icon(icon) {
SystemTray_set_icon(this.tray, icon);
}
setMenus(menus) {
const list = [];
const menuHandlers = new Map();
for (const m of menus) {
const {id, label, checked, enabled} = m;
const kind = m.kind || "standard";
if (m.handler) {
menuHandlers.set(m.id, m.handler);
}
list.push({id, label, kind, checked, enabled});
}
const menuHandler = (e) => {
const id = e.detail;
const handler = menuHandlers.get(id);
if (handler) {
handler();
}
if (this.#menuUserCallback) {
this.#menuUserCallback(e);
}
}
SystemTray_set_menus(this.tray, list);
this.#eventRegistry.bindEvent("menuclick", menuHandler)
}
bindActivate(callback) {
this.#eventRegistry.bindEvent("activate", callback);
}
bindMenuClick(callback) {
this.#menuUserCallback = callback;
}
}
export class Element {
_parent
handle
#style
#hoverStyle
#eventBinder;
constructor(el, context) {
const myContext = this;
if (typeof el === "number") {
this.handle = Element_create_by_type(el, myContext);
} else {
Element_set_js_context(el, myContext);
this.handle = el;
}
if (!this.handle) {
throw new Error("Failed to create element:" + el)
}
this.#eventBinder = new EventBinder(this.handle, Element_add_js_event_listener, Element_remove_js_event_listener, this, (target) => {
return Element.fromHandle(target);
});
}
static fromHandle(elementHandle) {
if (elementHandle) {
return Element_get_js_context(elementHandle) || null;
}
return null;
}
createEventBinder(target, addEventListenerApi, removeEventListenerApi) {
if (!removeEventListenerApi) {
removeEventListenerApi = (_t, listenerId) => {
Element_remove_js_event_listener(this.handle, listenerId);
}
}
return new EventBinder(target, addEventListenerApi, removeEventListenerApi, this, (target) => {
return Element.fromHandle(target);
});
}
get eid() {
return Element_get_eid(this.handle)
}
set class(clazz) {
Element_set_class(this.handle, clazz);
}
get class() {
return Element_get_class(this.handle);
}
set className(clazz) {
Element_set_class(this.handle, clazz);
}
get className() {
return Element_get_class(this.handle);
}
get parent() {
const eh = Element_get_parent(this.handle);
return Element.fromHandle(eh);
}
set focusable(focusable) {
Element_set_focusable(this.handle, focusable);
}
get focusable() {
return Element_is_focusable(this.handle);
}
get rootElement() {
let p = this.getParent();
if (p == null) {
return this;
} else {
return p.getRootElement();
}
}
focus() {
Element_focus(this.handle);
}
get window() {
const windowHandle = Element_get_window(this.handle);
return Window.fromHandle(windowHandle);
}
set style(style) {
this.#style = style;
Element_set_style(this.handle, style);
}
get style() {
return Element_get_style(this.handle);
}
set hoverStyle(style) {
this.#hoverStyle = style;
Element_set_hover_style(this.handle, style);
}
get hoverStyle() {
return this.#hoverStyle;
}
set scrollTop(value) {
Element_set_scroll_top(this.handle, value);
}
get scrollTop() {
return Element_get_scroll_top(this.handle);
}
set scrollLeft(value) {
Element_set_scroll_left(this.handle, value);
}
get scrollLeft() {
return Element_get_scroll_left(this.handle);
}
set draggable(value) {
Element_set_draggable(this.handle, value);
}
get draggable() {
return Element_get_draggable(this.handle);
}
set cursor(value) {
Element_set_cursor(this.handle, value);
}
get size() {
return Element_get_size(this.handle);
}
get contentSize() {
return Element_get_real_content_size(this.handle);
}
getBoundingClientRect() {
return Element_get_bounding_client_rect(this.handle);
}
get scrollHeight() {
return Element_get_scroll_height(this.handle);
}
get scrollWidth() {
return Element_scroll_width(this.handle);
}
setAttribute(key, value) {
Element_set_attribute(this.handle, key, value);
}
getAttribute(key) {
return Element_get_attribute(this.handle, key);
}
bindBoundsChange(callback) {
this.bindEvent("boundschange", callback);
}
bindFocus(callback) {
this.bindEvent("focus", callback);
}
bindBlur(callback) {
this.bindEvent("blur", callback);
}
bindClick(callback) {
this.#eventBinder.bindEvent("click", callback);
}
bindContextMenu(callback) {
this.#eventBinder.bindEvent("contextmenu", callback);
}
bindMouseDown(callback) {
this.#eventBinder.bindEvent("mousedown", callback);
}
bindMouseUp(callback) {
this.#eventBinder.bindEvent("mouseup", callback);
}
bindMouseMove(callback) {
this.#eventBinder.bindEvent("mousemove", callback);
}
bindMouseEnter(callback) {
this.#eventBinder.bindEvent("mouseenter", callback);
}
bindMouseLeave(callback) {
this.#eventBinder.bindEvent("mouseleave", callback);
}
bindKeyDown(callback) {
this.#eventBinder.bindEvent("keydown", callback);
}
bindKeyUp(callback) {
this.#eventBinder.bindEvent("keyup", callback);
}
bindSizeChanged(callback) {
this.#eventBinder.bindEvent("sizechange", callback);
}
bindScroll(callback) {
this.#eventBinder.bindEvent("scroll", callback);
}
bindMouseWheel(callback) {
this.#eventBinder.bindEvent("mousewheel", callback);
}
bindDragStart(callback) {
this.#eventBinder.bindEvent("dragstart", callback);
}
bindDragOver(callback) {
this.#eventBinder.bindEvent("dragover", callback);
}
bindDrop(callback) {
this.#eventBinder.bindEvent("drop", callback);
}
bindTouchStart(callback) {
this.#eventBinder.bindEvent("touchstart", callback);
}
bindTouchMove(callback) {
this.#eventBinder.bindEvent("touchmove", callback);
}
bindTouchEnd(callback) {
this.#eventBinder.bindEvent("touchend", callback);
}
bindTouchCancel(callback) {
this.#eventBinder.bindEvent("touchcancel", callback);
}
bindDroppedFile(callback) {
this.#eventBinder.bindEvent("droppedfile", callback);
}
bindHoveredFile(callback) {
this.#eventBinder.bindEvent("hoveredfile", callback);
}
bindEvent(type, callback) {
this.#eventBinder.bindEvent(type, callback);
}
set autoFocus(value) {
Element_set_auto_focus(this.handle, value);
}
get autoFocus() {
return Element_get_auto_focus(this.handle);
}
toString() {
return this.handle + "@" + this.constructor.name
}
}
export class Audio {
context;
#eventRegistry;
id;
constructor(config) {
this.id = Audio_create(config || {})
this.#eventRegistry = new EventRegistry(this.id, Audio_add_event_listener, Audio_remove_event_listener, this);
}
play() {
Audio_play(this.id);
}
pause() {
Audio_pause(this.id);
}
stop() {
Audio_stop(this.id);
}
bindLoad(callback) {
this.#eventRegistry.bindEvent('load', callback);
}
bindTimeUpdate(callback) {
this.#eventRegistry.bindEvent("timeupdate", callback);
}
bindEnd(callback) {
this.#eventRegistry.bindEvent("end", callback);
}
bindPause(callback) {
this.#eventRegistry.bindEvent("pause", callback);
}
bindStop(callback) {
this.#eventRegistry.bindEvent("stop", callback);
}
bindCurrentChange(callback) {
this.#eventRegistry.bindEvent("currentchange", callback);
}
bindEvent(type, callback) {
this.#eventRegistry.bindEvent(type, callback);
}
}
export class LabelElement extends Element {
constructor() {
super(VT_LABEL);
}
set textWrap(wrap) {
Text_set_text_wrap(this.handle, wrap);
}
set text(text) {
Text_set_text(this.handle, text);
}
set align(align) {
Element_set_property(this.handle, "align", align);
}
set selection(selection) {
Text_set_selection(this.handle, selection);
}
selectByCaretOffset(startCaretOffset, endCaretOffset) {
this.setSelection([startCaretOffset, endCaretOffset])
}
getLineBeginOffset(line) {
return Text_get_line_begin_offset(this.handle, line);
}
insertLine(line, text) {
Text_insert_line(this.handle, line, text);
}
updateLine(line, newText) {
Text_update_line(this.handle, line, newText);
}
deleteLine(line) {
Text_delete_line(this.handle, line);
}
getCaretOffsetByCursor(row, col) {
return Text_get_atom_offset_by_location(this.handle, [row, col]);
}
}
export class ParagraphElement extends Element {
constructor() {
super(VT_PARAGRAPH);
}
addLine(units) {
Paragraph_add_line(this.handle, units);
}
insertLine(index, units) {
Paragraph_insert_line(this.handle, index, units);
}
deleteLine(index) {
Paragraph_delete_line(this.handle, index);
}
updateLine(index, units) {
Paragraph_update_line(this.handle, index, units);
}
clear() {
Paragraph_clear(this.handle);
}
measureLine(units) {
return Paragraph_measure_line(this.handle, units);
}
get selectionText() {
return Paragraph_get_selection_text(this.handle);
}
}
export class ImageElement extends Element {
constructor() {
super(VT_IMAGE);
}
set src(src) {
Image_set_src(this.handle, src);
}
}
export class EntryElement extends Element {
#placeholderStyle;
constructor() {
super(VT_ENTRY);
}
set align(align) {
Element_set_property(this.handle, "align", align);
}
set text(text) {
Entry_set_text(this.handle, text);
}
set placeholder(placeholder) {
Entry_set_placeholder(this.handle, placeholder);
}
get placeholder() {
return Entry_get_placeholder(this.handle);
}
set placeholderStyle(style) {
this.#placeholderStyle = style;
Entry_set_placeholder_style(this.handle, style);
}
set type(type) {
Entry_set_type(this.handle, type);
}
get type() {
return Entry_get_type(this.handle);
}
get placeholderStyle() {
return this.#placeholderStyle;
}
setSelectionByCharOffset(start, end) {
Entry_set_selection_by_char_offset(this.handle, start, end)
}
setCaretByCharOffset(charOffset) {
Entry_set_caret_by_char_offset(this.handle, charOffset);
}
set multipleLine(multipleLine) {
Entry_set_multiple_line(this.handle, multipleLine)
}
set autoHeight(value) {
Entry_set_auto_height(this.handle, value);
}
get text() {
return Entry_get_text(this.handle);
}
set rows(rows) {
Entry_set_rows(this.handle, rows);
}
bindTextChange(callback) {
this.bindEvent("textchange", callback);
}
}
export class TextEditElement extends Element {
constructor() {
super(VT_TEXT_EDIT);
}
set align(align) {
Element_set_property(this.handle, "align", align);
}
set text(text) {
Element_set_property(this.handle, "text", text);
}
get text() {
return Element_get_property(this.handle, "text");
}
set selection(selection) {
Element_set_property(this.handle, "selection", selection);
}
set caret(caret) {
Element_set_property(this.handle, "caret", caret);
}
scrollToTop(top) {
Element_set_property(this.handle, "scroll_to_top", top);
}
bindTextChange(callback) {
this.bindEvent("textchange", callback);
}
bindCaretChange(callback) {
this.bindEvent("caretchange", callback);
}
}
class ContainerBasedElement extends Element {
#children = [];
addChild(child, index= -1) {
if (child._parent === this) {
const oldIndex = this.#children.indexOf(child);
if (oldIndex === index) {
return;
}
index -= oldIndex < index ? 1 : 0;
this.removeChild(child);
this.addChild(child, index);
return;
}
if (child._parent) {
child._parent.removeChild(child);
}
child._parent = this;
if (typeof index === "number" && index >= 0 && index < this.#children.length) {
Element_add_child(this.handle, child.handle, index);
this.#children.splice(index, 0, child);
} else {
Element_add_child(this.handle, child.handle, -1);
this.#children.push(child);
}
}
addChildBefore(newNode, referenceNode) {
const index = this.#children.indexOf(referenceNode);
this.addChild(newNode, index);
}
addChildAfter(newNode, referenceNode) {
const index = this.#children.indexOf(referenceNode);
if (index >= 0) {
this.addChild(newNode, index + 1);
} else {
this.addChild(newNode);
}
}
removeChild(child) {
const index = this.#children.indexOf(child);
if (index >= 0) {
child._parent = null;
Element_remove_child(this.handle, index);
this.#children.splice(index, 1);
} else {
console.log("remove child failed")
}
}
get children() {
return this.#children.slice();
}
}
export class ButtonElement extends ContainerBasedElement {
constructor() {
super(VT_BUTTON);
}
}
export class ContainerElement extends ContainerBasedElement {
constructor() {
super(VT_CONTAINER);
}
}
export class BodyElement extends ContainerBasedElement {
constructor() {
super(VT_BODY);
}
}
export class ScrollElement extends ContainerBasedElement {
constructor() {
super(VT_SCROLL);
}
set scrollX(value) {
Scroll_set_scroll_x(this.handle, value);
}
set scrollY(value) {
Scroll_set_scroll_y(this.handle, value);
}
scrollBy(value) {
value.x = value.x || 0;
value.y = value.y || 0;
Element_scroll_by(this.handle, value);
}
}
export class WebSocket {
client;
listeners;
onopen;
onclose;
onmessage;
onping;
onpong;
onerror;
#closed = false;
constructor(url) {
this.listeners = Object.create(null);
this.#connect(url);
}
addEventListener(name, callback) {
if (!this.listeners[name]) {
this.listeners[name] = [];
}
const listeners = this.listeners[name]
listeners.push(callback);
}
async send(data) {
try {
await WsConnection_send_str(this.client, data + "");
} catch (error) {
this.#emit('error', error);
}
}
close() {
if (!this.#closed) {
this.#closed = true;
this.#emit("close");
WsConnection_close(this.client);
}
}
async #connect(url) {
try {
this.client = await WsConnection_connect(url);
this.#emit("open");
this.#doRead();
} catch (error) {
this.#emit("error", error);
}
}
async #doRead() {
try {
loop:
for (;;) {
let [type, data] = await WsConnection_read(this.client);
switch (type) {
case "text":
this.#emit("message", data);
break;
case "binary":
this.#emit("message", ArrayBuffer.from(data));
break;
case "ping":
this.#emit("ping", data);
break;
case "pong":
this.#emit("pong", data);
break;
case "close":
break loop;
case "frame":
this.#emit("frame", data);
break;
}
}
this.close();
} catch (error) {
console.error(error);
this.#emit("error");
this.close();
}
}
#emit(name, data) {
let event = {
bubbles: false,
cancelBubble: false,
cancelable: false,
composed: false,
currentTarget: null,
eventPhase: 0,
isTrusted: true,
returnValue: false,
srcElement: null,
target: null,
timeStamp: new Date().getTime(),
type: name,
data,
};
const key = `on${name}`;
if (this[key]) {
try {
this[key](event)
} catch (error) {
console.error(error);
}
}
for (const listener of this.listeners[name] || []) {
try {
listener(event);
} catch (error) {
console.error(error);
}
}
}
}
export class Worker {
#worker
#eventBinder;
constructor(source) {
this.#worker = typeof source === "string" ? Worker_create(source) : Worker_bind(source);
this.#eventBinder = new EventBinder(
this.#worker,
Worker_bind_js_event_listener,
Worker_remove_js_event_listener,
this
);
}
postMessage(data) {
Worker_post_message(this.#worker, JSON.stringify(data));
}
bindMessage(callback) {
this.#eventBinder.bindEvent('message', e => {
e.data = JSON.parse(e.detail.data);
callback(e);
});
}
}
export class WorkerContext {
#workerContext;
#eventBinder;
constructor() {
this.#workerContext = WorkerContext_get();
this.#eventBinder = new EventBinder(
this.#workerContext,
WorkerContext_bind_js_event_listener,
WorkerContext_remove_js_event_listener,
this
)
}
postMessage(data) {
WorkerContext_post_message(this.#workerContext, JSON.stringify(data));
}
bindMessage(callback) {
this.#eventBinder.bindEvent('message', e => {
e.data = JSON.parse(e.detail.data);
callback(e);
});
}
static create() {
if (globalThis.WorkerContext_get) {
return new WorkerContext();
}
return null;
}
}
export class SqliteConn {
#conn;
constructor(conn) {
this.#conn = conn;
}
async execute(sql, params= []) {
return await SqliteConn_execute(this.#conn, sql, params);
}
async query(sql, params = []) {
const [columnNames, rows] = await SqliteConn_query(this.#conn, sql, params);
return rows.map(it => {
const map = {};
for (let i = 0; i < columnNames.length; i++) {
map[columnNames[i]] = it[i];
}
return map;
});
}
}
export class Sqlite {
static async open(path) {
const conn = SqliteConn_create();
await SqliteConn_open(conn, path);
return new SqliteConn(conn);
}
}
function collectCircleRefInfo(value, visited, circleRefList, level) {
if (level >= 3) {
return;
}
if (value && typeof value === "object") {
if (visited.includes(value)) {
circleRefList.push(value);
return;
} else {
visited.push(value);
}
Object.entries(value).forEach(([k, v]) => {
collectCircleRefInfo(v, visited, circleRefList, level + 1);
})
}
}
function log(...values) {
values.forEach((value, index) => {
const visited = [];
const circleRefList = [];
collectCircleRefInfo(value, visited, circleRefList, 0);
printObj(value, "", circleRefList, [], 0);
if (index < values.length - 1) {
printObj(",")
}
})
Console_print("\n");
}
function printObj(value, padding, circleRefList, printedList, level) {
let type = typeof value;
if (value instanceof Error) {
console.log(`[Error(${value.name})]` + value.message);
if (value.stack) {
console.log(value.stack);
}
} else if (type === "object" && value != null) {
const refIdx = circleRefList.indexOf(value);
if (refIdx >= 0 && printedList.includes(value)) {
Console_print("[Circular *" + refIdx + "]");
} else {
const entries = Object.entries(value);
if (level >= 2) {
return "{...}"
}
if (!entries.length) {
Console_print("{}");
} else {
const prefix = refIdx >= 0 ? ("<ref *" + refIdx + ">") : "";
Console_print(prefix + "{\n");
printedList.push(value);
entries.forEach(([k, v], index) => {
Console_print(padding + " " + k + ":");
printObj(v, padding + " ", circleRefList, printedList, level + 1);
if (index < entries.length - 1) {
Console_print(",\n");
}
});
Console_print("\n" + padding + "}");
}
}
} else if (type === "symbol") {
console.log("[Symbol]")
} else if (type === "function") {
console.log("[Function]")
} else {
Console_print(value + "");
}
}
globalThis.console = {
trace: log,
debug: log,
log,
info: log,
warn: log,
error: log,
}
const localStorage = {
getItem(key) {
return localstorage_get(key)
},
setItem(key, value) {
localstorage_set(key, value);
}
}
globalThis.localStorage = localStorage;
export const workerContext = WorkerContext.create();
if (workerContext) {
globalThis.workerContext = workerContext;
}
export class FetchResponse {
_resp;
constructor(resp, status) {
this._resp = resp;
this.status = status;
this.ok = this.status >= 200 && this.status < 300;
}
async json() {
const body = await fetch_response_body_string(this._resp);
return JSON.parse(body);
}
}
async function fetch(url, options) {
const resp = await fetch_create(url, options);
let status = await fetch_response_status(resp);
return new FetchResponse(resp, status);
}
globalThis.fetch = fetch;
globalThis.navigator = new Navigator();
globalThis.process = new Process();
globalThis.process.setPromiseRejectionTracker(error => {
console.error('uncaught promise error', error);
});
globalThis.fileDialog = new FileDialog();
globalThis.Worker = Worker;
globalThis.WorkerContext = WorkerContext;
globalThis.Window = Window;
if (globalThis.SystemTray_create) {
globalThis.SystemTray = SystemTray;
}
globalThis.Element = Element;
globalThis.ContainerElement = ContainerElement;
globalThis.ScrollElement = ScrollElement;
globalThis.LabelElement = LabelElement;
globalThis.EntryElement = EntryElement;
globalThis.TextEditElement = TextEditElement;
globalThis.ButtonElement = ButtonElement;
globalThis.ImageElement = ImageElement;
globalThis.ParagraphElement = ParagraphElement;
globalThis.Audio = Audio;
globalThis.WebSocket = WebSocket;
globalThis.Sqlite = Sqlite;
globalThis.setTimeout = globalThis.timer_set_timeout;
globalThis.clearTimeout = globalThis.timer_clear_timeout;
globalThis.setInterval = globalThis.timer_set_interval;
globalThis.clearInterval = globalThis.timer_clear_interval;
globalThis.KEY_MOD_CTRL = 0x1;
globalThis.KEY_MOD_ALT = 0x1 << 1;
globalThis.KEY_MOD_META = 0x1 << 2;
globalThis.KEY_MOD_SHIFT = 0x1 << 3;