-
-
Notifications
You must be signed in to change notification settings - Fork 103
/
keymanEngine.ts
721 lines (635 loc) · 26.6 KB
/
keymanEngine.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
import { KeymanEngine as KeymanEngineBase } from 'keyman/engine/main';
import { Device as DeviceDetector } from 'keyman/engine/device-detect';
import { getAbsoluteY } from 'keyman/engine/dom-utils';
import { OutputTarget } from 'keyman/engine/element-wrappers';
import {
OSKView,
TwoStateActivator,
VisualKeyboard
} from 'keyman/engine/osk';
import { ErrorStub, KeyboardStub, CloudQueryResult, toPrefixedKeyboardId as prefixed } from 'keyman/engine/package-cache';
import { DeviceSpec, Keyboard, extendString } from "@keymanapp/keyboard-processor";
import * as views from './viewsAnchorpoint.js';
import { BrowserConfiguration, BrowserInitOptionDefaults, BrowserInitOptionSpec } from './configuration.js';
import { default as ContextManager } from './contextManager.js';
import DefaultBrowserRules from './defaultBrowserRules.js';
import HardwareEventKeyboard from './hardwareEventKeyboard.js';
import { FocusStateAPIObject } from './context/focusAssistant.js';
import { PageIntegrationHandlers } from './context/pageIntegrationHandlers.js';
import { LanguageMenu } from './languageMenu.js';
import { setupOskListeners } from './oskConfiguration.js';
import { whenDocumentReady } from './utils/documentReady.js';
import { outputTargetForElement } from 'keyman/engine/attachment';
import { UtilApiEndpoint} from './utilApiEndpoint.js';
import { UIModule } from './uiModuleInterface.js';
import { HotkeyManager } from './hotkeyManager.js';
import { BeepHandler } from './beepHandler.js';
import KeyboardInterface from './keyboardInterface.js';
export default class KeymanEngine extends KeymanEngineBase<BrowserConfiguration, ContextManager, HardwareEventKeyboard> {
touchLanguageMenu?: LanguageMenu;
private pageIntegration: PageIntegrationHandlers;
private _initialized: number = 0;
readonly _util: UtilApiEndpoint;
private _ui: UIModule;
hotkeyManager: HotkeyManager = new HotkeyManager();
private readonly beepHandler: BeepHandler;
/**
* Provides a quick link to the base help page for Keyman keyboards.
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/helpURL
*/
public readonly helpURL = 'https://help.keyman.com/go';
keyEventRefocus = () => {
this.contextManager.restoreLastActiveTarget();
}
constructor(worker: Worker, sourceUri: string) {
const config = new BrowserConfiguration(sourceUri); // currently set to perform device auto-detect.
super(worker, config, new ContextManager(config, () => this.legacyAPIEvents), (engine: KeymanEngine) => {
return {
// The `engine` parameter cannot be supplied with the constructing instance before calling
// `super`, hence the 'fun' rigging to supply it _from_ `super` via this closure.
keyboardInterface: new KeyboardInterface(window, engine),
defaultOutputRules: new DefaultBrowserRules(engine.contextManager)
};
});
this._util = new UtilApiEndpoint(config);
this.beepHandler = new BeepHandler(this.core.keyboardInterface);
this.core.keyboardProcessor.beepHandler = () => this.beepHandler.beep(this.contextManager.activeTarget);
this.hardKeyboard = new HardwareEventKeyboard(config.hardDevice, this.core.keyboardProcessor, this.contextManager);
// Scrolls the document-body to ensure that a focused element remains visible after the OSK appears.
this.contextManager.on('targetchange', (target: OutputTarget<any>) => {
const e = target?.getElement();
if(this.osk) {
(this.osk.activationModel as TwoStateActivator<HTMLElement>).activationTrigger = e;
}
if(this.config.hostDevice.touchable && target) {
this.ensureElementVisibility(e);
}
});
}
public ensureElementVisibility(e: HTMLElement) {
if(!e || !this.osk) {
return;
}
// Get the absolute position of the caret
const y = getAbsoluteY(e);
const t = window.pageYOffset;
let dy = y-t;
if(y >= t) {
dy -= (window.innerHeight - this.osk._Box.offsetHeight - e.offsetHeight - 2);
if(dy < 0) {
dy=0;
}
}
// Hide OSK, then scroll, then re-anchor OSK with absolute position (on end of scroll event)
if(dy != 0) {
window.scrollTo(0, dy + t);
}
}
public get util() {
return this._util;
}
public get views() {
// NOT this.views. Just... `views`, the import of viewsAnchorpoint.ts
return views;
}
/**
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/initialized
*/
public get initialized() {
return this._initialized;
}
public get ui() {
return this._ui;
}
public set ui(module: UIModule) {
if(this._ui) {
this._ui.shutdown();
}
this._ui = module;
if(this.config.deferForInitialization.isFulfilled) {
module.initialize();
}
}
/**
* Function Initialization
* Scope Public
* @param {com.keyman.OptionType} arg object specifying configuration properties for KeymanEngine + its resources
*
* Performs initialization of the KeymanEngine for Web, including:
* - device-detection
* - option configuration
* - integration with the active page
* - OSK-selection
* - finalization for pre-loaded keyboard + stub registrations.
*
* It also self-defers if the page is not yet fully loaded; it will automatically await page
* load and resume once page-load is complete. (Certain initialization behaviors will only
* proceed properly with a fully-loaded page.)
*
* @returns A Promise that only resolves once the engine is fully initialized.
*/
public async init(options: Required<BrowserInitOptionSpec>) {
let deviceDetector = new DeviceDetector();
let device = deviceDetector.detect();
const totalOptions = {...BrowserInitOptionDefaults, ...options};
// // Possible condition we can add: no change of init options after a ***
// // prior finalized init.
this.config.hostDevice = device;
// Set any incoming options, overriding previous entries.
this.config.initialize(totalOptions);
this._initialized = 1;
// Must wait for document load for further initialization.
await whenDocumentReady();
// Deferred keyboard loading + shortcutting if a different init call on the engine has
// already fully resolved.
if(this.config.deferForInitialization.isResolved) {
// abort! Maybe throw an error, too.
return Promise.resolve();
}
// There may be some valid mutations possible even on repeated calls?
// The original seems to allow it.
await super.init(totalOptions);
// Used by keymanweb.com; if no keyboard-cookie exists, we need this to trigger
// default-stub selection on mobile devices so that a keyboard - and thus, the
// globe key - are accessible.
//
// The `super` call above initializes `keyboardRequisitioner`, as needed here.
this.keyboardRequisitioner.cloudQueryEngine.once('unboundregister', () => {
if(!this.contextManager.activeKeyboard?.keyboard) {
// Autoselects this.keyboardRequisitioner.cache.defaultStub, which will be
// set to an actual keyboard on mobile devices.
this.setActiveKeyboard('', '');
}
});
this.contextManager.initialize(); // will seek to attach to the page, which requires document.body
// Capture the saved-keyboard string now, before we load any keyboards/stubs
// or do anything that would mutate the value.
const savedKeyboardStr = this.contextManager.getSavedKeyboardRaw();
// Automatically performs related handler setup & maintains references
// needed for related cleanup / shutdown.
this.pageIntegration = new PageIntegrationHandlers(window, this);
this.config.finalizeInit();
if(this.ui) {
this.ui.initialize();
this.legacyAPIEvents.callEvent('loaduserinterface', {});
}
this._initialized = 2;
// Let any deferred, pre-init stubs complete registration
await this.config.deferForInitialization;
/*
Attempt to restore the user's last-used keyboard from their previous session.
The method auto-loads the default stub if one is available and the last-used keyboard
has no registered stub.
Note: any cloud stubs will probably not be available yet.
If we tracked cloud requests and awaited a Promise.all on pending queries,
we could handle that too.
*/
const loadingKbd: Promise<any> = this.contextManager.restoreSavedKeyboard(savedKeyboardStr);
// Wait for the initial keyboard to load before setting the OSK; this will avoid building an
// empty OSK that we'll instantly discard after.
try {
await loadingKbd;
} catch { /* in case of failed fetch due to network error or bad URI; we must still let the OSK init. */ };
const firstKbdConfig = {
keyboardToActivate: this.contextManager.activeKeyboard
};
const osk = device.touchable ? new views.AnchoredOSKView(this, firstKbdConfig) : new views.FloatingOSKView(this, firstKbdConfig);
setupOskListeners(this, osk, this.contextManager);
// And, now that we have our loaded active keyboard - or failed, thus must use that default...
// Now we set the OSK in place, an act which triggers VisualKeyboard construction.
this.osk = osk;
}
get register(): (x: CloudQueryResult) => void {
return this.keyboardRequisitioner.cloudQueryEngine.registerFromCloud;
}
/**
* Function getUIState
* Scope Public
* @return {Object.<string,(boolean|number)>}
* Description Return object with activation state of UI:
* activationPending (bool): KMW being activated
* activated (bool): KMW active
*
* See https://help.keyman.com/DEVELOPER/ENGINE/WEB/16.0/reference/core/getUIState
*/
public getUIState(): FocusStateAPIObject {
return this.contextManager.focusAssistant.getUIState();
}
/**
* Set or clear the IsActivatingKeymanWebUI flag (exposed function)
*
* See https://help.keyman.com/DEVELOPER/ENGINE/WEB/16.0/reference/core/activatingUI
*
* @param {(boolean|number)} state Activate (true,false)
*/
public activatingUI(state: boolean | number) {
this.contextManager.focusAssistant.setMaintainingFocus(!!state);
}
/**
* Function setKeyboardForControl
* Scope Public
* @param {Element} Pelem Control element
* @param {string|null=} Pkbd Keyboard (Clears the set keyboard if set to null.)
* @param {string|null=} Plc Language Code
* Description Set default keyboard for the control
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/setKeyboardForControl
*/
public setKeyboardForControl(Pelem: HTMLElement, Pkbd?: string, Plc?: string) {
if(Pelem instanceof Pelem.ownerDocument.defaultView.HTMLIFrameElement) {
console.warn("'keymanweb.setKeyboardForControl' cannot set keyboard on iframes.");
return;
}
if(!this.isAttached(Pelem)) {
console.error("KeymanWeb is not attached to element " + Pelem);
return;
}
let stub = null;
if(Pkbd) {
stub = this.keyboardRequisitioner.cache.getStub(Pkbd, Plc);
if(!stub) {
throw new Error(`No keyboard has been registered with id ${Pkbd} and language code ${Plc}.`);
}
}
this.contextManager.setKeyboardForTarget(Pelem._kmwAttachment.interface, Pkbd, Plc);
}
/**
* Function getKeyboardForControl
* Scope Public
* @param {Element} Pelem Control element
* @return {string|null} The independently-managed keyboard for the control.
* Description Returns the keyboard ID of the current independently-managed keyboard for this control.
* If it is currently following the global keyboard setting, returns null instead.
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/getKeyboardForControl
*/
public getKeyboardForControl(Pelem) {
const target = outputTargetForElement(Pelem);
return this.contextManager.getKeyboardStubForTarget(target).id;
}
// Is not currently published API... but it exists.
/**
* Function getLanguageForControl
* Scope Public
* @param {Element} Pelem Control element
* @return {string|null} The independently-managed keyboard for the control.
* Description Returns the language code used with the current independently-managed keyboard for this control.
* If it is currently following the global keyboard setting, returns null instead.
*/
getLanguageForControl(Pelem) {
const target = outputTargetForElement(Pelem);
return this.contextManager.getKeyboardStubForTarget(target).langId;
}
isAttached(x: HTMLElement): boolean {
return this.contextManager.page.isAttached(x);
}
/**
* Exposed function to load keyboards by name. One or more arguments may be used
*
* @param {any[]} args keyboard name string or keyboard metadata JSON object
* @returns {Promise<(KeyboardStub|ErrorStub)[]>} Promise of added keyboard/error stubs
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/addKeyboards
*/
public addKeyboards(...args: any[]): Promise<(KeyboardStub|ErrorStub)[]> {
return this.config.deferForInitialization.then(() => {
if (!args || !args[0] || args[0].length == 0) {
// Get the cloud keyboard catalog
return this.keyboardRequisitioner.fetchCloudCatalog().catch((errVal) => {
console.error(errVal[0].error);
return errVal;
});
} else {
let x: (string|KeyboardStub)[] = [];
if (Array.isArray(args[0])) {
x = x.concat(args[0]);
} else if (Array.isArray(args)) {
x = x.concat(args);
}
return this.keyboardRequisitioner.addKeyboardArray(x);
}
})
}
/**
* Add default keyboards for given language(s)
*
* @param {string|string[]} arg Language name (multiple arguments allowed)
* @returns {Promise<(KeyboardStub|ErrorStub)[]>} Promise of added keyboard/error stubs
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/addKeyboardsForLanguage
**/
public addKeyboardsForLanguage(arg: string[]|string) : Promise<(KeyboardStub|ErrorStub)[]> {
return this.config.deferForInitialization.then(() => {
if (typeof arg === 'string') {
return this.keyboardRequisitioner.addLanguageKeyboards(arg.split(',').map(item => item.trim()));
} else {
return this.keyboardRequisitioner.addLanguageKeyboards(arg);
}
});
}
/**
* Get an associative array of keyboard identification strings
* This was defined as an array, so is kept that way, but
* Javascript treats it as an object anyway
*
* This is a public API function documented at
* https://help.keyman.com/developer/engine/web/current-version/reference/core/getKeyboard.
*
* @param {Object} Lstub Keyboard stub object
* @param {Object} Lkbd Keyboard script object
* @return {Object} Copy of keyboard identification strings
*
*/
private _GetKeyboardDetail = function(Lstub: KeyboardStub, Lkbd: Keyboard) { // I2078 - Full keyboard detail
let Lr = {
Name: Lstub.KN,
InternalName: Lstub.KI,
LanguageName: Lstub.KL, // I1300 - Add support for language names
LanguageCode: Lstub.KLC, // I1702 - Add support for language codes, region names, region codes, country names and country codes
RegionName: Lstub.KR,
RegionCode: Lstub.KRC,
CountryName: Lstub['KC'] as string,
CountryCode: Lstub['KCC'] as string,
KeyboardID: Lstub['KD'] as string,
Font: Lstub.KFont,
OskFont: Lstub.KOskFont,
HasLoaded: !!Lkbd,
IsRTL: Lkbd ? Lkbd.isRTL : null
};
return Lr;
}
/**
* Function isCJK
* Scope Public
* @param {Object=} k0
* @return {boolean}
* Description Tests if active keyboard (or specified keyboard script object, as optional argument)
* uses a pick list (Chinese, Japanese, Korean, etc.)
* (This function accepts either keyboard structure.)
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/isCJK
*/
public isCJK(k0? /* keyboard script object | return-type of _GetKeyboardDetail [b/c Toolbar UI]*/) {
let kbd: Keyboard;
if(k0) {
let kbdDetail = k0 as ReturnType<KeymanEngine['_GetKeyboardDetail']>;
if(kbdDetail.KeyboardID){
kbd = this.keyboardRequisitioner.cache.getKeyboard(k0.KeyboardID);
} else {
kbd = new Keyboard(k0);
}
} else {
kbd = this.core.activeKeyboard;
}
return kbd && kbd.isCJK;
}
/**
* Get keyboard meta data for the selected keyboard and language
*
* @param {string} PInternalName Internal name of keyboard
* @param {string=} PlgCode language code
* @return {Object} Details of named keyboard
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/getKeyboard
**/
public getKeyboard(PInternalName: string, PlgCode?: string) {
const stub = this.keyboardRequisitioner.cache.getStub(PInternalName, PlgCode);
const keyboard = this.keyboardRequisitioner.cache.getKeyboardForStub(stub);
return stub && this._GetKeyboardDetail(stub, keyboard);
}
/**
* Get API-friendly array of available keyboard stubs
*
* Refer to https://help.keyman.com/developer/engine/web/current-version/reference/core/getKeyboards.
*
* The type of each entry of the array corresponds to that of `getKeyboard`.
*
* @return {Array} Array of available keyboards
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/getKeyboards
*/
public getKeyboards(): ReturnType<KeymanEngine['_GetKeyboardDetail']>[] {
const Lr: ReturnType<KeymanEngine['_GetKeyboardDetail']>[] = [];
const cache = this.keyboardRequisitioner.cache;
const keyboardStubs = cache.getStubList()
for(let Ln=0; Ln < keyboardStubs.length; Ln++) { // I1511 - array prototype extended
const Lstub = keyboardStubs[Ln];
// In Chrome, (including on Android), Array.prototype.find() requires Chrome 45.
// This is a later version than the default on our oldest-supported Android devices.
const Lkbd = cache.getKeyboardForStub(Lstub);
const Lrn = this._GetKeyboardDetail(Lstub, Lkbd); // I2078 - Full keyboard detail
Lr.push(Lrn);
}
return Lr;
}
/**
* Build 362: removeKeyboards() remove keyboard from list of available keyboards
*
* @param {string} x keyboard name string
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/removeKeyboards
*/
public removeKeyboards(...x: string[]) {
for(let i=0; i < x.length; i++) {
// This will completely forget the keyboard, requiring an async load operation to restore it again.
// `true` is responsible for this & is required to pass a variable-store unit test.
this.keyboardRequisitioner.cache.forgetKeyboard(x[i], true);
if(this.contextManager.activeKeyboard?.metadata.id == prefixed(x[i])) {
this.contextManager.activateKeyboard('', '');
}
}
return true;
}
/**
* Gets the cookie for the name and language code of the most recently active keyboard
*
* Defaults to US English, but this needs to be user-set in later revision (TODO)
*
* @return {string} InternalName:LanguageCode
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/getSavedKeyboard
**/
public getSavedKeyboard(): string {
return this.contextManager.getSavedKeyboard();
}
/**
* Set focus to last active target element (browser-dependent)
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/focusLastActiveElement
*/
public focusLastActiveElement() {
this.contextManager.lastActiveTarget?.focus();
}
/**
* Get the last active target element *before* KMW activated (I1297)
*
* @return {Object}
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/getLastActiveElement
*/
public getLastActiveElement() {
return this.contextManager.lastActiveTarget?.getElement();
}
/**
* Set the active input element directly optionally setting focus
*
* @param {Object|string} e element id or element
* @param {boolean=} setFocus optionally set focus (KMEW-123)
**/
setActiveElement(e: string|HTMLElement, setFocus: boolean) {
if(typeof e == 'string') {
const id = e;
e = document.getElementById(e);
if(!e) {
throw new Error(`Could not find the specified element (id: ${id}`);
}
}
const target = outputTargetForElement(e);
if(!target) {
throw new Error(`KMW is not attached to the specified element (id: ${e.id}).`);
}
this.contextManager.setActiveTarget(target, setFocus);
}
/**
* Move focus to user-specified element
*
* @param {string|Object} e element or element id
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/moveToElement
**/
public moveToElement(e: string|HTMLElement) {
if(typeof(e) == "string") { // Can't instanceof string, and String is a different type.
e=document.getElementById(e);
}
e.focus();
}
/**
* Function addHotkey
* Scope Public
* @param {number} keyCode
* @param {number} shiftState
* @param {function(Object)} handler
* Description Add hot key handler to array of document-level hotkeys triggered by key up event
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/addHotKey
*/
public addHotKey(keyCode: number, shiftState: number, handler: () => void) {
this.hotkeyManager.addHotKey(keyCode, shiftState, handler);
}
/**
* Function removeHotkey
* Scope Public
* @param {number} keyCode
* @param {number} shiftState
* Description Remove a hot key handler from array of document-level hotkeys triggered by key up event
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/removeHotKey
*/
public removeHotKey(keyCode: number, shiftState: number) {
this.hotkeyManager.removeHotkey(keyCode, shiftState);
}
/**
* Function attachToControl
* Scope Public
* @param {Element} Pelem Element to which KMW will be attached
* Description Attaches KMW to control (or IFrame)
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/attachToControl
*/
public attachToControl(Pelem: HTMLElement) {
this.contextManager.page.attachToControl(Pelem);
}
/**
* Function detachFromControl
* Scope Public
* @param {Element} Pelem Element from which KMW will detach
* Description Detaches KMW from a control (or IFrame)
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/detachFromControl
*/
public detachFromControl(Pelem: HTMLElement) {
this.contextManager.page.detachFromControl(Pelem);
}
/**
* Function disableControl
* Scope Public
* @param {Element} Pelem Element to be disabled
* Description Disables a KMW control element
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/disableControl
*/
public disableControl(Pelem: HTMLElement) {
this.contextManager.page.disableControl(Pelem);
}
/**
* Function enableControl
* Scope Public
* @param {Element} Pelem Element to be disabled
* Description Disables a KMW control element
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/enableControl
*/
public enableControl(Pelem: HTMLMapElement) {
this.contextManager.page.enableControl(Pelem);
}
/**
* Create copy of the OSK that can be used for embedding in documentation or help
* The currently active keyboard will be returned if PInternalName is null
*
* @param {string} PInternalName internal name of keyboard, with or without Keyboard_ prefix
* @param {number} Pstatic static keyboard flag (unselectable elements)
* @param {string=} argFormFactor layout form factor, defaulting to 'desktop'
* @param {(string)=} argLayerId name or index of layer to show, defaulting to 'default'
* @return {Object} DIV object with filled keyboard layer content
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/osk/BuildVisualKeyboard
*/
public BuildVisualKeyboard(
PInternalName: string,
Pstatic: number,
argFormFactor?: DeviceSpec.FormFactor,
argLayerId?: string
): HTMLElement {
let PKbd: Keyboard = null;
if(PInternalName != null) {
PKbd = this.keyboardRequisitioner.cache.getKeyboard(PInternalName);
}
PKbd = PKbd || this.core.activeKeyboard;
let Pstub = this.keyboardRequisitioner.cache.getStub(PKbd);
// help.keyman.com will set this function in place to specify the desired
// dimensions for the documentation-keyboards, so we'll give it priority. One of those
// "temporary" (but actually permanent) solutions from yesteryear.
//
// Note that the main intended use of that function is for embedded KMW on the mobile apps...
// but they never call `BuildVisualKeyboard`, so it's all good.
const getOskHeight = this['getOskHeight'];
let targetHeight = (typeof getOskHeight == 'function' ? getOskHeight() : null) || this.osk.computedHeight || 200;
return VisualKeyboard.buildDocumentationKeyboard(
PKbd,
Pstub,
this.config.paths,
argFormFactor,
argLayerId,
targetHeight
);
}
/**
* Detaches all KMW event handlers attached by this instance of the engine and releases
* other related resources as appropriate.
*
* The primary use of this method is to facilitate a clean transition between engine
* instances during integration testing. The goal is to prevent interactions intended
* for the 'current' instance from being accidentally intercepted by a discarded one.
*/
shutdown() {
this.pageIntegration.shutdown();
this.contextManager.shutdown();
this.osk?.shutdown();
this.core.languageProcessor.shutdown();
this.hardKeyboard.shutdown();
this.util.shutdown(); // For tracked dom events, stylesheets.
this.legacyAPIEvents.callEvent('unloaduserinterface', {});
this.ui?.shutdown();
}
}