feat(common/web): basic classes for DOM-based keyboard loading

This commit is contained in:
Joshua A. Horton 2023-02-08 09:26:13 +07:00
parent 8b57e28a34
commit cb91a89736
4 changed files with 96 additions and 2 deletions

View file

@ -1,5 +1,7 @@
export * from "./keyboards/activeLayout.js";
export * from "./keyboards/defaultLayouts.js";
export { default as DOMKeyboardLoader } from "./keyboards/domKeyboardLoader.js";
export { default as DOMKeyboardSandbox } from "./keyboards/domKeyboardSandbox.js";
export { default as Keyboard } from "./keyboards/keyboard.js";
export * from "./keyboards/keyboard.js";
export { KeyboardHarness, KeyboardKeymanGlobal, MinimalCodesInterface, MinimalKeymanGlobal } from "./keyboards/keyboardHarness.js";

View file

@ -0,0 +1,59 @@
// Enables DOM types, but just for this one module.
///<reference lib="dom" />
import { KeyboardHarness, MinimalKeymanGlobal } from './keyboardHarness.js';
import Keyboard from './keyboard.js';
import KeyboardLoaderBase from './keyboardLoaderBase.js';
import { ManagedPromise } from '@keymanapp/web-utils';
export default class DOMKeyboardLoader extends KeyboardLoaderBase {
public readonly element: HTMLIFrameElement;
constructor()
constructor(harness: KeyboardHarness);
constructor(harness?: KeyboardHarness) {
if(harness && harness._jsGlobal != window) {
// Copy the String typing over; preserve string extensions!
harness._jsGlobal['String'] = window['String'];
}
if(!harness) {
super(new KeyboardHarness(window, MinimalKeymanGlobal));
} else {
super(harness);
}
}
protected loadKeyboardInternal(uri: string): Promise<Keyboard> {
const promise = new ManagedPromise<Keyboard>();
try {
const document = this.harness._jsGlobal.document;
const script = document.createElement('script');
document.head.appendChild(script);
script.onerror = promise.reject;
script.onload = () => {
if(this.harness.activeKeyboard) {
promise.resolve(this.harness.activeKeyboard);
} else {
promise.reject();
}
}
promise.finally(() => {
// https://stackoverflow.com/a/37393041 - totally safe.
script.remove();
});
// Now that EVERYTHING ELSE is ready, establish the link to the keyboard's script.
script.src = uri;
} catch (err) {
return Promise.reject(err);
}
return Promise.resolve(this.harness.activeKeyboard);
}
}

View file

@ -0,0 +1,35 @@
// Enables DOM types, but just for this one module.
///<reference lib="dom" />
import { ManagedPromise } from "@keymanapp/web-utils";
export default class DOMKeyboardSandbox {
public readonly sandboxHost: HTMLIFrameElement;
public sandbox(): Window {
return this.sandboxHost.contentWindow;
}
private constructor() {
const host = this.sandboxHost = document.createElement('iframe');
host.style.display = 'none';
host.style.width = '0px';
host.style.height = '0px';
host.tabIndex = -1;
host.title = "Sandboxed keyboard loader";
host.hidden = true;
}
public static buildSandbox(): Promise<DOMKeyboardSandbox> {
// Promise: because the iframe's document isn't instantly ready.
const promise = new ManagedPromise<DOMKeyboardSandbox>();
const instance = new DOMKeyboardSandbox();
instance.sandboxHost.onload = () => promise.resolve(instance);
instance.sandboxHost.onerror = () => promise.reject();
// Need to insert the sandbox into the DOM before it can load or error!
window.document.appendChild(instance.sandboxHost);
return promise.corePromise;
}
}

View file

@ -1,5 +1,3 @@
// TODO: export from ../index.ts as well!
import { KeyboardHarness, MinimalKeymanGlobal } from './keyboardHarness.js';
import Keyboard from './keyboard.js';