From cb91a897367d02d2cd28da9cf59705aa8a2622fd Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Wed, 8 Feb 2023 09:26:13 +0700 Subject: [PATCH] feat(common/web): basic classes for DOM-based keyboard loading --- common/web/keyboard-processor/src/index.ts | 2 + .../src/keyboards/domKeyboardLoader.ts | 59 +++++++++++++++++++ .../src/keyboards/domKeyboardSandbox.ts | 35 +++++++++++ .../src/keyboards/nodeKeyboardLoader.ts | 2 - 4 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 common/web/keyboard-processor/src/keyboards/domKeyboardLoader.ts create mode 100644 common/web/keyboard-processor/src/keyboards/domKeyboardSandbox.ts diff --git a/common/web/keyboard-processor/src/index.ts b/common/web/keyboard-processor/src/index.ts index 4187d7f366..b420a1a930 100644 --- a/common/web/keyboard-processor/src/index.ts +++ b/common/web/keyboard-processor/src/index.ts @@ -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"; diff --git a/common/web/keyboard-processor/src/keyboards/domKeyboardLoader.ts b/common/web/keyboard-processor/src/keyboards/domKeyboardLoader.ts new file mode 100644 index 0000000000..98d2ed193d --- /dev/null +++ b/common/web/keyboard-processor/src/keyboards/domKeyboardLoader.ts @@ -0,0 +1,59 @@ +// Enables DOM types, but just for this one module. + +/// + +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 { + const promise = new ManagedPromise(); + + 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); + } +} \ No newline at end of file diff --git a/common/web/keyboard-processor/src/keyboards/domKeyboardSandbox.ts b/common/web/keyboard-processor/src/keyboards/domKeyboardSandbox.ts new file mode 100644 index 0000000000..7e098621b1 --- /dev/null +++ b/common/web/keyboard-processor/src/keyboards/domKeyboardSandbox.ts @@ -0,0 +1,35 @@ +// Enables DOM types, but just for this one module. +/// + +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 { + // Promise: because the iframe's document isn't instantly ready. + const promise = new ManagedPromise(); + 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; + } +} \ No newline at end of file diff --git a/common/web/keyboard-processor/src/keyboards/nodeKeyboardLoader.ts b/common/web/keyboard-processor/src/keyboards/nodeKeyboardLoader.ts index f84eb4be63..e90636cc99 100644 --- a/common/web/keyboard-processor/src/keyboards/nodeKeyboardLoader.ts +++ b/common/web/keyboard-processor/src/keyboards/nodeKeyboardLoader.ts @@ -1,5 +1,3 @@ -// TODO: export from ../index.ts as well! - import { KeyboardHarness, MinimalKeymanGlobal } from './keyboardHarness.js'; import Keyboard from './keyboard.js';