diff --git a/common/web/keyboard-processor/src/keyboards/keyboardProperties.ts b/common/web/keyboard-processor/src/keyboards/keyboardProperties.ts index e3b48685ea..7f4ede2de0 100644 --- a/common/web/keyboard-processor/src/keyboards/keyboardProperties.ts +++ b/common/web/keyboard-processor/src/keyboards/keyboardProperties.ts @@ -6,8 +6,15 @@ export interface KeyboardFont { 'path': string; } +// Filename properties are deliberately omitted here; we can add that at higher-levels where it matters +// via 'mix-in'. +// +// For example, the OSK module doesn't care about the filename of a loaded keyboard. It doesn't do +// keyboard loading on its own whatsoever. + // Corresponds to Keyman Engine for Web's internal "keyboard stub" format. export type KeyboardInternalPropertySpec = { + KI: string, 'KFont': KeyboardFont, 'KOskFont': KeyboardFont, displayName?: string, @@ -16,17 +23,37 @@ export type KeyboardInternalPropertySpec = { 'KLC'?: string }; -// Corresponds to the documented API for the Web engine's `addKeyboards` function -// when a single language object is specified - not an array. +export type LanguageAPIPropertySpec = { + id: string, + name: string, + font: KeyboardFont, + oskFont: KeyboardFont +} + +/** + * Corresponds to the documented API for the Web engine's `addKeyboards` function + * when a single language object is specified - not an array. + * + * See https://help.keyman.com/DEVELOPER/ENGINE/WEB/15.0/reference/core/addKeyboards, + * "Using an `object`". + */ export type KeyboardAPIPropertySpec = { id: string, name: string, - languages: { - id: string, - name: string, - font: KeyboardFont, - oskFont: KeyboardFont - } + languages: LanguageAPIPropertySpec; +} + +/** + * Corresponds to the documented API for the Web engine's `addKeyboards` function + * when a language array is specified for the object. + * + * See https://help.keyman.com/DEVELOPER/ENGINE/WEB/15.0/reference/core/addKeyboards, + * "Using an `object`". + */ +export type KeyboardAPIPropertyMultilangSpec = { + id: string, + name: string, + languages: LanguageAPIPropertySpec[]; } type MetadataObj = KeyboardInternalPropertySpec | KeyboardAPIPropertySpec; @@ -50,6 +77,7 @@ export default class KeyboardProperties { } else { let apiStub = arg1 as KeyboardAPIPropertySpec; this.wrappedStub = { + KI: apiStub.id, KN: apiStub.name, KL: apiStub.languages.name, KLC: apiStub.languages.id, @@ -60,6 +88,7 @@ export default class KeyboardProperties { } } else { this.wrappedStub = { + KI: null, displayName: arg1, 'KLC': arg2, 'KFont': arg3, @@ -68,6 +97,38 @@ export default class KeyboardProperties { } } + public static fromMultilanguageAPIStub(apiStub: KeyboardAPIPropertyMultilangSpec, spacebarTextMode?: SpacebarText): KeyboardProperties[] { + let stubs: KeyboardProperties[] = []; + + for(let langSpec of apiStub.languages) { + let stub: KeyboardAPIPropertySpec = { + id: apiStub.id, + name: apiStub.name, + languages: langSpec + }; + + stubs.push(new KeyboardProperties(stub, spacebarTextMode)); + } + + return stubs; + } + + public get id(): string { + return this.wrappedStub.KI; + } + + public get name(): string { + return this.wrappedStub.KN; + } + + public get langId(): string { + return this.wrappedStub.KLC; + } + + public get langName(): string { + return this.wrappedStub.KL; + } + public get displayName(): string { if(this.wrappedStub.displayName) { return this.wrappedStub.displayName; diff --git a/common/web/keyboard-processor/src/keyboards/spacebarText.ts b/common/web/keyboard-processor/src/keyboards/spacebarText.ts index 34b6952202..d98e7b6462 100644 --- a/common/web/keyboard-processor/src/keyboards/spacebarText.ts +++ b/common/web/keyboard-processor/src/keyboards/spacebarText.ts @@ -1,4 +1,5 @@ -const enum SpacebarText { +// Compiles completely out if `const enum`, making it unavailable in JS-based unit tests. +enum SpacebarText { KEYBOARD = 'keyboard', LANGUAGE = 'language', LANGUAGE_KEYBOARD = 'languageKeyboard', diff --git a/common/web/keyboard-processor/tests/cases/keyboard-properties.js b/common/web/keyboard-processor/tests/cases/keyboard-properties.js new file mode 100644 index 0000000000..bb7d373baf --- /dev/null +++ b/common/web/keyboard-processor/tests/cases/keyboard-properties.js @@ -0,0 +1,90 @@ +import { assert } from 'chai'; +import fs from 'fs'; +import vm from 'vm'; + +import KeyboardProperties from '@keymanapp/keyboard-processor/build/obj/keyboards/keyboardProperties.js'; +import SpacebarText from '@keymanapp/keyboard-processor/build/obj/keyboards/spacebarText.js'; + +describe('Keyboard Properties', function() { + let rootCommonStubPath = '../../test/resources/json/keyboards/'; + + it('initialization from KMW\'s addKeyboards() API spec', () => { + let files = fs.readdirSync(rootCommonStubPath); + + for(let file of files) { + let stub = JSON.parse(fs.readFileSync(`${rootCommonStubPath}/${file}`)); + + let dataset = []; + if(stub.languages instanceof Array) { + dataset = KeyboardProperties.fromMultilanguageAPIStub(stub); + } else { + dataset = [new KeyboardProperties(stub)]; + } + + // do verification + for(let data of dataset) { + assert.isOk(data.id); + assert.isOk(data.name); + assert.isOk(data.langId); + assert.isOk(data.langName); + // Do not make assertions on font, oskFont - those may be undefined for some keyboards. + + // Generated dynamically from the backing source data. + assert.isOk(data.displayName); + } + } + }); + + it('generates display-name text if not directly-specified', () => { + // Could convert to run on all stubs, but... this should be fine as-is. + let stub = JSON.parse(fs.readFileSync(`${rootCommonStubPath}/khmer_angkor.json`)); + let propObject = new KeyboardProperties(stub); + + // Without a configured SpacebarText value, will display the keyboard name. + assert.equal(propObject.displayName, propObject.name); + + propObject.spacebarTextMode = SpacebarText.BLANK; + assert.equal(propObject.displayName, ''); + + propObject.spacebarTextMode = SpacebarText.KEYBOARD; + assert.equal(propObject.displayName, propObject.name); + + propObject.spacebarTextMode = SpacebarText.LANGUAGE; + assert.equal(propObject.displayName, propObject.langName); + + propObject.spacebarTextMode = SpacebarText.LANGUAGE_KEYBOARD; + assert.isTrue(propObject.displayName.includes(propObject.langName) && propObject.displayName.includes(propObject.name)); + }); + + it('does not override directly-specified display-name text', () => { + // Could convert to run on all stubs, but... this should be fine as-is. + let stub = JSON.parse(fs.readFileSync(`${rootCommonStubPath}/khmer_angkor.json`)); + + const customDisplayName = "(custom)"; + let propObject = new KeyboardProperties(stub); + + propObject.displayName = customDisplayName; + + // Without a configured SpacebarText value, will display the keyboard name. + assert.equal(propObject.displayName, customDisplayName); + + propObject.spacebarTextMode = SpacebarText.BLANK; + assert.equal(propObject.displayName, customDisplayName); + + propObject.spacebarTextMode = SpacebarText.KEYBOARD; + assert.equal(propObject.displayName, customDisplayName); + + propObject.spacebarTextMode = SpacebarText.LANGUAGE; + assert.equal(propObject.displayName, customDisplayName); + + propObject.spacebarTextMode = SpacebarText.LANGUAGE_KEYBOARD; + assert.equal(propObject.displayName, customDisplayName); + + propObject.displayName = null; // clear the value + assert.notEqual(propObject.displayName, customDisplayName); + assert.isTrue(propObject.displayName.includes(propObject.langName) && propObject.displayName.includes(propObject.name)); + + propObject.displayName = customDisplayName; + assert.equal(propObject.displayName, customDisplayName); + }); +}); \ No newline at end of file