diff --git a/developer/src/common/web/utils/src/types/keylayout/keylayout-xml.ts b/developer/src/common/web/utils/src/types/keylayout/keylayout-xml.ts index 5da06e96ba..57760aae63 100644 --- a/developer/src/common/web/utils/src/types/keylayout/keylayout-xml.ts +++ b/developer/src/common/web/utils/src/types/keylayout/keylayout-xml.ts @@ -158,7 +158,9 @@ export interface KL_When { * to define which output or next is followed by a state */ state?: string; + through?: string; output?: string; + multiplier?: string; next?: string; }; diff --git a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-file-reader.ts b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-file-reader.ts index 3d6fb711a9..26ac23a3a1 100644 --- a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-file-reader.ts +++ b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-file-reader.ts @@ -11,6 +11,7 @@ import { CompilerCallbacks, DeveloperUtilsMessages, Keylayout, KeymanXMLReader } import { util, SchemaValidators } from '@keymanapp/common-types'; import { ConverterMessages } from '../converter-messages.js'; import boxXmlArray = util.boxXmlArray; +import { KL_KeyMapSelect,KL_KeyMap } from "../../../common/web/utils/src/types/keylayout/keylayout-xml.js"; export class KeylayoutFileReader { @@ -23,10 +24,10 @@ export class KeylayoutFileReader { * @param keyMapSelect the keyMapSelect element to find in keyMapSet * @return true if the keyMapSet element is found, false if not */ - public findMapIndexinKeymap(jsonObj: any, keyMapSelect: any): boolean { + public findMapIndexinKeymap(jsonObj: Keylayout.KeylayoutXMLSourceFile, keyMapSelect: KL_KeyMapSelect): boolean { for (const keyMapSet of jsonObj.keyboard.keyMapSet) { for (const keyMap of keyMapSet.keyMap) { - if (keyMap['index'] === keyMapSelect) { + if (keyMap['index'] === keyMapSelect.mapIndex) { return true; } } @@ -40,10 +41,10 @@ export class KeylayoutFileReader { * @param keyMap the keyMap element to find in modifierMap * @return true if the keyMap element is found, false if not */ - public findIndexinKeymapSelect(jsonObj: any, keyMap: any): boolean { + public findIndexinKeymapSelect(jsonObj: Keylayout.KeylayoutXMLSourceFile, keyMap: KL_KeyMap): boolean { for (const modifierMap of jsonObj.keyboard.modifierMap) { for (const keyMapSelect of modifierMap.keyMapSelect) { - if (keyMapSelect['mapIndex'] === keyMap) { + if (keyMapSelect['mapIndex'] === keyMap.index) { return true; } } @@ -58,19 +59,19 @@ export class KeylayoutFileReader { * @param jsonObj the read keylayout data to be checked * @return true if all keyMapSelect elements have a corresponding keyMap element, false if not */ - public checkForCorrespondingElements(jsonObj: any): boolean { + public checkForCorrespondingElements(jsonObj: Keylayout.KeylayoutXMLSourceFile): boolean { let available = true; // check if all keyMapSelect elements have a corresponding keyMap element in the .keylayout file for (const modifierMap of jsonObj.keyboard.modifierMap) { for (const keyMapSelect of modifierMap.keyMapSelect) { - available = available && this.findMapIndexinKeymap(jsonObj, keyMapSelect['mapIndex']); + available = available && this.findMapIndexinKeymap(jsonObj, keyMapSelect); } } // check if all keyMap elements have a corresponding keyMapSelect element in the .keylayout file for (const keyMapSet of jsonObj.keyboard.keyMapSet) { for (const keyMap of keyMapSet.keyMap) { - available = available && this.findIndexinKeymapSelect(jsonObj, keyMap['index']); + available = available && this.findIndexinKeymapSelect(jsonObj, keyMap); } } return available; diff --git a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts index 074c76be37..9db2deb84e 100644 --- a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts +++ b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts @@ -48,7 +48,7 @@ export interface KeylayoutFileData { actionId?: string; keyCode?: string; key?: string; - behavior?: string; + behavior: string; modifier?: string; outchar?: string; }; @@ -124,7 +124,7 @@ export class KeylayoutToKmnConverter { return null; } try { - if (!KeylayoutReader.validate(jsonO,inputFilename)) { + if (!KeylayoutReader.validate(jsonO, inputFilename)) { return null; } } catch (e) { @@ -150,7 +150,7 @@ export class KeylayoutToKmnConverter { * @param jsonObj containing filename, behaviorand rules of a json object * @return an ProcessedData containing all data ready to print out */ - private convert(jsonObj: any, inputfilename: string, outputFilename?: string): ProcessedData { + private convert(jsonObj: Keylayout.KeylayoutXMLSourceFile, inputfilename: string, outputFilename?: string): ProcessedData { // modifiers for each behavior const modifierBehavior: string[][] = []; @@ -196,7 +196,7 @@ export class KeylayoutToKmnConverter { * @param jsonObj: json Object containing all data read from a keylayout file * @return an object containing the name of the input file, an array of behaviors and a populated array of Rules[] */ - public createRuleData(dataUkelele: ProcessedData, jsonObj: any): ProcessedData { + public createRuleData(dataUkelele: ProcessedData, jsonObj: Keylayout.KeylayoutXMLSourceFile): ProcessedData { const rules: Rule[] = []; let dkCounterC3: number = 0; @@ -260,7 +260,7 @@ export class KeylayoutToKmnConverter { } else if (jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['action'] !== undefined) { - actionId = jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['action']; + actionId = jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['action'] ?? ""; // ............................................................................................................................... // case C1: action + state none + output ......................................................................................... // C1 see: https://docs.google.com/document/d/12J3NGO6RxIthCpZDTR8FYSRjiMgXJDLwPY2z9xqKzJ0/edit?tab=t.0#heading=h.g7jwx3lx0ydd ... @@ -317,7 +317,7 @@ export class KeylayoutToKmnConverter { this.callbacks.reportMessage(ConverterMessages.Error_UndefinedActionDetected({ inputFilename: jsonObj.keyboard['name'] + ".keylayout", action: actionId, - KeyName: this.mapUkeleleKeycodeToVK(jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['code']), + KeyName: this.mapUkeleleKeycodeToVK(Number(jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['code'])), keymapIndex: jsonObj.keyboard.keyMapSet[0].keyMap[i]['index'] })); return null; @@ -325,13 +325,14 @@ export class KeylayoutToKmnConverter { // with actionId from above loop all 'action' and search for a state(none)-next-pair ............................................................................................................ // e.g. in Block 5: find for action id a18 ...................................................................................................................... - for (let l = 0; l < jsonObj.keyboard.actions.action[b1ActionIndex].when.length; l++) { - if ((jsonObj.keyboard.actions.action[b1ActionIndex].when[l]['state'] === "none") // find "none" - && (jsonObj.keyboard.actions.action[b1ActionIndex].when[l]['next'] !== undefined)) { // find "next" + if (jsonObj.keyboard.actions?.action?.[b1ActionIndex]?.when) { + for (const when of jsonObj.keyboard.actions.action[b1ActionIndex].when) { + if ((when['state'] === "none") // find "none" + && (when['next'] !== undefined)) { // find "next" // Data of Block Nr 5 ..................................................................................................................................................................... // of this state(none)-next-pair get value of next (next="1") ............................................................................................................................. - /* eg: 1 */ const b5ValueNext: string = jsonObj.keyboard.actions.action[b1ActionIndex].when[l]['next']; + /* eg: 1 */ const b5ValueNext: string = when['next']; // ........................................................................................................................................................................................ @@ -339,7 +340,7 @@ export class KeylayoutToKmnConverter { // with present actionId (a18) find all keycode-behavior-pairs that use this action (a18) => (keymapIndex 0/keycode 24 and keymapIndex 3/keycode 24) .................................... // from these create an array of modifier combinations e.g. [['','caps?'], ['Caps']] ..................................................................................................... /* eg: [['24', 0], ['24', 3]] */ const b4DeadkeyObj: KeylayoutFileData[] = this.getKeyModifierArrayFromActionID(jsonObj, actionId); - /* e.g. [['','caps?'], ['Caps']]*/ const b4DeadkeyModifierObj: string[] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b4DeadkeyObj); + /* e.g. [['','caps?'], ['Caps']]*/ const b4DeadkeyModifierObj: string[][] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b4DeadkeyObj); // ........................................................................................................................................................................................ @@ -353,14 +354,14 @@ export class KeylayoutToKmnConverter { // create array[Keycode,Keyname,action id,actionIndex,output] and array[Keyname,action id,behavior,modifier,output] ...................................................................... /* eg: ['0','K_A','a9','0','â'] */ const b1KeycodeObj: KeylayoutFileData[] = this.getKeyActionOutputArrayFromActionStateOutputArray(jsonObj, b6ActionIdObj); /* eg: ['K_A','a9','0','NCAPS','â']*/ const b1ModifierKeyObj: KeylayoutFileData[] = this.getKeyBehaviorModOutputArrayFromKeyActionBehaviorOutputArray(jsonObj, b1KeycodeObj, isCapsused); - // ....................................................................................................................................................................................... + // ....................................................................................................................................................................................... - for (let n1 = 0; n1 < b4DeadkeyModifierObj.length; n1++) { - for (let n2 = 0; n2 < b4DeadkeyModifierObj[n1].length; n2++) { - for (let n3 = 0; n3 < b4DeadkeyObj.length; n3++) { - for (let n4 = 0; n4 < b1ModifierKeyObj.length; n4++) { + for (let n1 = 0; n1 < b4DeadkeyModifierObj.length; n1++) { + for (let n2 = 0; n2 < b4DeadkeyModifierObj[n1].length; n2++) { + for (let n3 = 0; n3 < b4DeadkeyObj.length; n3++) { + for (let n4 = 0; n4 < b1ModifierKeyObj.length; n4++) { - ruleObj = new Rule( + ruleObj = new Rule( /* ruleType */ "C2", /* modifierPrevDeadkey*/ "", @@ -376,11 +377,12 @@ export class KeylayoutToKmnConverter { /* modifierKey*/ b1ModifierKeyObj[n4].modifier, /* key */ b1ModifierKeyObj[n4].key, /* output */ new TextEncoder().encode(b1ModifierKeyObj[n4].outchar), - ); - if ((b1ModifierKeyObj[n4].outchar !== undefined) - && (b1ModifierKeyObj[n4].outchar !== "undefined") - && (b1ModifierKeyObj[n4].outchar !== "")) { - rules.push(ruleObj); + ); + if ((b1ModifierKeyObj[n4].outchar !== undefined) + && (b1ModifierKeyObj[n4].outchar !== "undefined") + && (b1ModifierKeyObj[n4].outchar !== "")) { + rules.push(ruleObj); + } } } } @@ -416,7 +418,7 @@ export class KeylayoutToKmnConverter { // with present actionId (a16) find all keycode-behavior-pairs that use this action (a16) => (keymapIndex 3/keycode 32) .................................................................... // from these create an array of modifier combinations e.g. [ [ 'anyOption', 'Caps' ] ] ..................................................................................................... /* e.g. [['32', 3]] */ const b4DeadkeyObj: KeylayoutFileData[] = this.getKeyModifierArrayFromActionID(jsonObj, actionId); - /* e.g. [ [ 'anyOption', 'Caps' ] ]*/ const b4DeadkeyModifierObj: string[] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b4DeadkeyObj); + /* e.g. [ [ 'anyOption', 'Caps' ] ]*/ const b4DeadkeyModifierObj: string[][] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b4DeadkeyObj); // ........................................................................................................................................................................................... // Data of Block Nr 3 ........................................................................................................................................................................ @@ -428,7 +430,7 @@ export class KeylayoutToKmnConverter { // with present actionId (a17) find all key names and behaviors that use this action (a17) => (keymapIndex 3/keycode 28) .................................................................... // from these create an array of modifier combinations e.g. [ [ 'anyOption', 'Caps' ] ] ..................................................................................................... /* eg: index=3 */ const b2PrevDeadkeyObj: KeylayoutFileData[] = this.getKeyModifierArrayFromActionID(jsonObj, b3ActionId); - /* e.g. [ [ 'anyOption', 'Caps' ] ] */ const b2PrevDeadkeyModifierObj: string[] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b2PrevDeadkeyObj); + /* e.g. [ [ 'anyOption', 'Caps' ] ] */ const b2PrevDeadkeyModifierObj: string[][] = this.getModifierArrayFromKeyModifierArray(dataUkelele.modifiers, b2PrevDeadkeyObj); // ........................................................................................................................................................................................... // Data of Block Nr 6 ........................................................................................................................................................................ // create an array[action id,state,output] from all state-output-pairs that use state = b5ValueNext (e.g. use 1 in ) ......................................... @@ -486,7 +488,7 @@ export class KeylayoutToKmnConverter { keymapIndex: jsonObj.keyboard.keyMapSet[0].keyMap[i]['index'], output: jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['output'], key: jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['code'], - KeyName: this.mapUkeleleKeycodeToVK(jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['code']) + KeyName: this.mapUkeleleKeycodeToVK(Number(jsonObj.keyboard.keyMapSet[0].keyMap[i].key[j]['code'])) })); return null; } @@ -773,7 +775,7 @@ export class KeylayoutToKmnConverter { ]; if (!(pos >= 0 && pos <= 0x31) || (pos === null) || (pos === undefined)) { - return ""; + return "" as string; } else { return vk[pos]; } @@ -781,11 +783,14 @@ export class KeylayoutToKmnConverter { /** * @brief member function to return an index for a given actionID - * @param data :any - an object containing all data read from a .keylayout file + * @param data an object containing all data read from a .keylayout file * @param search :string - value 'id' to be found * @return a number specifying the index of an actionId */ - public getActionIndexFromActionId(data: any, search: string): number { + public getActionIndexFromActionId(data: Keylayout.KeylayoutXMLSourceFile, search: string): number { + if (!data.keyboard?.actions?.action) { + return -1; + } for (let i = 0; i < data.keyboard.actions.action.length; i++) { if (data.keyboard.actions.action[i]['id'] === search) { return i; @@ -796,53 +801,60 @@ export class KeylayoutToKmnConverter { /** * @brief member function to find the actionID of a certain state-next pair - * @param data :any an object containing all data read from a .keylayout file + * @param data an object containing all data read from a .keylayout file * @param search :string value 'next' to be found * @return a string containing the actionId of a certain state(none)-next pair */ - public getActionIdFromActionNext(data: any, search: string): string { - if (search !== "none") { - for (let i = 0; i < data.keyboard.actions.action.length; i++) { - for (let j = 0; j < data.keyboard.actions.action[i].when.length; j++) { - if (data.keyboard.actions.action[i].when[j]['next'] === search) { - return data.keyboard.actions.action[i]['id']; + public getActionIdFromActionNext(data: Keylayout.KeylayoutXMLSourceFile, search: string): string { + if (search !== "none" && data.keyboard?.actions?.action) { + for (const action of data.keyboard.actions.action) { + if (action.when) { + for (const when of action.when) { + if (when['next'] === search) { + return action['id'] as string; + } } } } } - return ""; + return "" as string; } /** * @brief member function to create an array of (modifier) behaviors for a given keycode in [{keycode,modifier}] - * @param data : any - an object containing all data read from a .keylayout file + * @param data an object containing all data read from a .keylayout file * @param search : KeylayoutFileData[] - an array[{keycode,modifier}] to be found * @return a string[] containing modifiers */ - public getModifierArrayFromKeyModifierArray(data: any, search: KeylayoutFileData[]): string[] { - const returnString1D: string[] = []; + public getModifierArrayFromKeyModifierArray(data: ProcessedData["modifiers"], search: KeylayoutFileData[]): string[][] | [null] { + const returnString1D: string[][] = []; for (let i = 0; i < search.length; i++) { - returnString1D.push(data[search[i].behavior]); + if (search[i].behavior === undefined || search[i].behavior === null) { + return [null]; + } + returnString1D.push(data[Number(search[i].behavior)]); } return returnString1D; } - /** * @brief member function to find the output for a certain actionID for state 'none' - * @param data :any an object containing all data read from a .keylayout file + * @param data an object containing all data read from a .keylayout file * @param search :string an actionId to be found * @return a string containing the output character */ - public getOutputFromActionIdNone(data: any, search: string): string { + public getOutputFromActionIdNone(data: Keylayout.KeylayoutXMLSourceFile, search: string): string { let OutputValue: string = ""; - for (let i = 0; i < data.keyboard.actions.action.length; i++) { - if (data.keyboard.actions.action[i]['id'] === search) { - for (let j = 0; j < data.keyboard.actions.action[i].when.length; j++) { - if (data.keyboard.actions.action[i].when[j]['state'] === "none") { - if (data.keyboard.actions.action[i].when[j]['output'] !== undefined) { - OutputValue = data.keyboard.actions.action[i].when[j]['output']; - } + + if (!data.keyboard?.actions?.action) { + return OutputValue; + } + + for (const action of data.keyboard.actions.action as Keylayout.KL_Action[]) { + if (action['id'] === search) { + for (const when of action.when as Keylayout.KL_When[]) { + if ((when['state'] === "none") && (when['output'] !== undefined)) { + OutputValue = when['output']; } } } @@ -852,11 +864,11 @@ export class KeylayoutToKmnConverter { /** * @brief member function to return array of [Keycode,Keyname,actionId,actionIDIndex, output] for a given actionID in of [ actionID,state,output] - * @param data :any - an object containing all data read from a .keylayout file + * @param data an object containing all data read from a .keylayout file * @param search :idStateOutputObject[] - array of [{ actionID,state,output }] * @return a KeylayoutFileData[] containing [{Keycode,Keyname,actionId,actionID, output}] */ - public getKeyActionOutputArrayFromActionStateOutputArray(data: any, search: ActionStateOutput[]): KeylayoutFileData[] { + public getKeyActionOutputArrayFromActionStateOutputArray(data: Keylayout.KeylayoutXMLSourceFile, search: ActionStateOutput[]): KeylayoutFileData[] { if ((search === undefined) || (search === null)) return []; @@ -867,7 +879,7 @@ export class KeylayoutToKmnConverter { for (let i = 0; i < data.keyboard.keyMapSet[0].keyMap.length; i++) { for (let j = 0; j < data.keyboard.keyMapSet[0].keyMap[i].key.length; j++) { if (data.keyboard.keyMapSet[0].keyMap[i].key[j]['action'] === search[k].id && - data.keyboard.keyMapSet[0].keyMap[i].key[j]['code'] <= KeylayoutToKmnConverter.MAX_KEY_IDENTIFIER) { + Number(data.keyboard.keyMapSet[0].keyMap[i].key[j]['code']) <= KeylayoutToKmnConverter.MAX_KEY_IDENTIFIER) { const singleDataSet = { keyCode: data.keyboard.keyMapSet[0].keyMap[i].key[j]['code'], key: this.mapUkeleleKeycodeToVK(Number(data.keyboard.keyMapSet[0].keyMap[i].key[j]['code'])), @@ -885,24 +897,26 @@ export class KeylayoutToKmnConverter { /** * @brief member function to get an array of all actionId-output pairs for a certain state - * @param data : any an object containing all data read from a .keylayout file + * @param data an object containing all data read from a .keylayout file * @param search : string a 'state' to be found * @return an array: idStateOutputObject[] containing all [{actionId, state, output}] for a certain state */ - public getActionStateOutputArrayFromActionState(data: any, search: string): ActionStateOutput[] { + public getActionStateOutputArrayFromActionState(data: Keylayout.KeylayoutXMLSourceFile, search: string): ActionStateOutput[] { const actionStateOutput: ActionStateOutput[] = []; - - for (let i = 0; i < data.keyboard.actions.action.length; i++) { - for (let j = 0; j < data.keyboard.actions.action[i].when.length; j++) { - if (data.keyboard.actions.action[i].when[j]['state'] === search) { - if (data.keyboard.actions.action[i].when[j]['output'] !== undefined) { - const singleDataSet = { - id: data.keyboard.actions.action[i]['id'], - state: data.keyboard.actions.action[i].when[j]['state'], - output: data.keyboard.actions.action[i].when[j]['output'] - }; - actionStateOutput.push(singleDataSet); + if (search !== "none" && data.keyboard?.actions?.action) { + for (const action of data.keyboard.actions.action) { + if (action.when) { + for (const when of action.when) { + if ((when['state'] === search) && (when['output'] !== undefined)) { + const singleDataSet = { + id: action['id'], + state: when['state'], + output: when['output'] + }; + actionStateOutput.push(singleDataSet as ActionStateOutput); + } } + } } } @@ -911,12 +925,12 @@ export class KeylayoutToKmnConverter { /** * @brief member function to create an 2D array of [KeyName,actionId,behavior,modifier,output] - * @param data : any an object containing all data read from a .keylayout file + * @param data an object containing all data read from a .keylayout file * @param search : array of [{keycode,keyname,actionId,behavior,output}] to be found * @param isCAPSused : boolean flag to indicate if CAPS is used in a keylayout file or not * @return an array: KeylayoutFileData[] containing [{KeyName,actionId,behavior,modifier,output}] */ - public getKeyBehaviorModOutputArrayFromKeyActionBehaviorOutputArray(data: any, search: KeylayoutFileData[], isCAPSused: boolean): KeylayoutFileData[] { + public getKeyBehaviorModOutputArrayFromKeyActionBehaviorOutputArray(data: Keylayout.KeylayoutXMLSourceFile, search: KeylayoutFileData[], isCAPSused: boolean): KeylayoutFileData[] { const keyBehaviorModOutput = []; if (!((search === undefined) || (search === null) || (search.length === 0))) { for (let i = 0; i < search.length; i++) { @@ -945,20 +959,20 @@ export class KeylayoutToKmnConverter { unique.push(o); } return unique; - }, []); + }, [] as KeylayoutFileData[]); return uniquekeyBehaviorModOutput; } /** * @brief member function to create an array of [actionID, output, behavior,keyname,modifier] for a given actionId - * @param data : any - an object containing all data read from a .keylayout file - * @param modi : any - an array of modifiers + * @param data an object containing all data read from a .keylayout file + * @param modi an array of modifiers * @param search : string - an actionId to be found * @param outchar : string - the output character * @param isCAPSused : boolean - flag to indicate if CAPS is used in a keylayout file or not * @return an array: KeylayoutFileData[] containing [{actionID,output, behavior,keyname,modifier}] */ - public getActionOutputBehaviorKeyModiFromActionIDStateOutput(data: any, modi: string[][], search: string, outchar: string, isCapsused: boolean): KeylayoutFileData[] { + public getActionOutputBehaviorKeyModiFromActionIDStateOutput(data: Keylayout.KeylayoutXMLSourceFile, modi: string[][], search: string, outchar: string, isCapsused: boolean): KeylayoutFileData[] { const actionOutputBehaviorKeyModi = []; if ((!modi) || (search === "") || (search === undefined)) { return []; @@ -967,8 +981,8 @@ export class KeylayoutToKmnConverter { for (let i = 0; i < data.keyboard.keyMapSet[0].keyMap.length; i++) { for (let j = 0; j < data.keyboard.keyMapSet[0].keyMap[i].key.length; j++) { if (data.keyboard.keyMapSet[0].keyMap[i].key[j]['action'] === search) { - for (let k = 0; k < modi[data.keyboard.keyMapSet[0].keyMap[i]['index']].length; k++) { - const behaviorIdx: number = data.keyboard.keyMapSet[0].keyMap[i]['index']; + for (let k = 0; k < modi[Number(data.keyboard.keyMapSet[0].keyMap[i]['index'])].length; k++) { + const behaviorIdx: number = Number(data.keyboard.keyMapSet[0].keyMap[i]['index']); const singleDataSet = { outchar: outchar, actionId: data.keyboard.keyMapSet[0].keyMap[i].key[j]['action'], @@ -996,18 +1010,18 @@ export class KeylayoutToKmnConverter { unique.push(o); } return unique; - }, []); + }, [] as KeylayoutFileData[]); return uniqueactionOutputBehaviorKey; } /** * @brief member function to create an array of [{keycode,behavior}] for a given actionId - * @param data : any - an object containing all data read from a .keylayout file + * @param data an object containing all data read from a .keylayout file * @param search : string - an actionId to be found * @return an array: KeylayoutFileData[] containing [{keycode,behavior}] */ - public getKeyModifierArrayFromActionID(data: any, search: string): KeylayoutFileData[] { + public getKeyModifierArrayFromActionID(data: Keylayout.KeylayoutXMLSourceFile, search: string): KeylayoutFileData[] { const mapIndexObject1D: KeylayoutFileData[] = []; for (let i = 0; i < data.keyboard.keyMapSet[0].keyMap.length; i++) { for (let j = 0; j < data.keyboard.keyMapSet[0].keyMap[i].key.length; j++) { diff --git a/developer/src/kmc-convert/test/data/keylayout.dtd b/developer/src/kmc-convert/test/data/keylayout.dtd index e60fa8c51c..9a734b11b8 100644 --- a/developer/src/kmc-convert/test/data/keylayout.dtd +++ b/developer/src/kmc-convert/test/data/keylayout.dtd @@ -4,29 +4,86 @@ * Created by S. Schmitt on 2025-07-14 * * This DTD describes a technical preview of Keylayout Data - --> + * + * Unless otherwise specified we used TN 2056: + * https://developer.apple.com/library/archive/technotes/tn2056/_index.html#//apple_ref/doc/uid/DTS10003085-CH1-SUBSECTION7 + * + * kmc-convert does not need and therefore does not process the following attributes: + * group, id, maxout, + * first, last, mapSet, modifiers, + * defaultIndex, + * baseMapSet, baseIndex, + * through, multiplier + * kmc-convert does not need and therefore does not process the following elements: + * terminators + * kmc-convert does not need and therefore does not process 'anonymous actions' + * --> - - - - - + + + + + + + - - + + + + - - + + + + + + + - + + + + - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/developer/src/kmc-convert/test/keylayout-to-kmn-converter.tests.ts b/developer/src/kmc-convert/test/keylayout-to-kmn-converter.tests.ts index c2f9fa5352..a7010ef1e7 100644 --- a/developer/src/kmc-convert/test/keylayout-to-kmn-converter.tests.ts +++ b/developer/src/kmc-convert/test/keylayout-to-kmn-converter.tests.ts @@ -186,24 +186,6 @@ describe('KeylayoutToKmnConverter', function () { assert.isNull(convertedEmpty); }); - it('should return empty on only modifiers as input', async function () { - const convertedMod = sut.convertBound.convert({ - keylayoutFilename: '', - modifiers: [['caps'], ['Shift'], ['command']], - rules: [] - }, ''); - assert.isNull(convertedMod); - }); - - it('should return empty on only rules as input', async function () { - const convertedRule = sut.convertBound.convert({ - keylayoutFilename: '', - modifiers: [], - rules: [['C0', '', '', 0, 0, '', '', 0, 0, 'CAPS', 'K_A', 'A']] - }, ''); - assert.isNull(convertedRule); - }); - it('should return empty array of rules on null input', async function () { const convertedRule = sut.convertBound.convert(null, 'ABC.kmn'); assert.isNull(convertedRule); @@ -350,13 +332,14 @@ describe('KeylayoutToKmnConverter', function () { [[{ key: '999', behavior: null }], [null]], [[{ key: '0', behavior: -999 }], [null]], [[{ key: '0', behavior: null }], [null]], + [[{ key: '0', behavior: undefined }], [null]], [[], []], ].forEach(function (values) { it((values[1] !== null) ? ("getModifierArrayFromKeyModifierArray('" + JSON.stringify(values[0]) + "')").padEnd(68, " ") + " should return '" + JSON.stringify(values[1]) + "'" : ("getModifierArrayFromKeyModifierArray('" + JSON.stringify(values[0]) + "')").padEnd(68, " ") + " should return '" + "null" + "'", async function () { - const result = sut.getModifierArrayFromKeyModifierArray(converted.modifiers, values[0] as KeylayoutFileData[]); + const result = sut.getModifierArrayFromKeyModifierArray(converted.modifiers, values[0] as unknown as KeylayoutFileData[]); assert.deepStrictEqual(JSON.stringify(result), JSON.stringify(values[1])); }); }); diff --git a/developer/src/kmc-convert/test/kmn-file-writer.tests.ts b/developer/src/kmc-convert/test/kmn-file-writer.tests.ts index 0eb23c88b4..ede21c9b9a 100644 --- a/developer/src/kmc-convert/test/kmn-file-writer.tests.ts +++ b/developer/src/kmc-convert/test/kmn-file-writer.tests.ts @@ -74,7 +74,7 @@ describe('KmnFileWriter', function () { ["ሴ", 'ሴ'], ["😎", '😎'], ["", '\u0002'], - ["�",undefined ], + ["�", undefined], ["a", 'a'], ["ሴ", 'ሴ'], ["😆", '😆'], @@ -148,9 +148,9 @@ describe('KmnFileWriter', function () { ['c WARNING: unavailable modifier : here: '], ['c WARNING: unavailable superior rule ( [Y K_Y] > dk(B0) ) : here: ']], - ].forEach(function (values: any, index: number) { - it(('rule " ' + values[0][0].ruleType + ' "') + 'should create "' + values[1] + ' | ' + values[2] + ' | ' + values[3] + '"', async function () { - const result: string[] = sutW.reviewRules(values[0], 0); + ].forEach(function (values: (string[] | Rule[])[], index: number) { + it(('rule " ' + (values[0][0] as Rule).ruleType as string + ' "') + 'should create "' + values[1] + ' | ' + values[2] + ' | ' + values[3] + '"', async function () { + const result: string[] = sutW.reviewRules(values[0] as Rule[], 0); assert.equal(result[0], values[1][0]); assert.equal(result[1], values[2][0]); assert.equal(result[2], values[3][0]); @@ -313,9 +313,9 @@ describe('KmnFileWriter', function () { [''], ["c WARNING: duplicate rule: earlier: [CAPS K_C] > 'X' here: "]], - ].forEach(function (values: any, index: number) { - it('rule ' + values[0][0].ruleType + ' should create " ' + ' "' + values[1] + ' | ' + values[2] + ' | ' + values[3] + '"', async function () { - const result: string[] = sutW.reviewRules(values[0], 1); + ].forEach(function (values: (string[] | Rule[])[], index: number) { + it('rule ' + (values[0][0] as Rule).ruleType as string + ' should create " ' + ' "' + values[1] + ' | ' + values[2] + ' | ' + values[3] + '"', async function () { + const result: string[] = sutW.reviewRules(values[0] as Rule[], 1); assert.equal(result[0], values[1][0]); assert.equal(result[1], values[2][0]); assert.equal(result[2], values[3][0]); @@ -333,9 +333,9 @@ describe('KmnFileWriter', function () { [''], [''], ["c WARNING: ambiguous rule: later: [RALT K_B] > dk(A0) ambiguous rule: earlier: [RALT K_B] > 'X' here: PLEASE CHECK THE FOLLOWING RULE AS IT WILL NOT BE WRITTEN ! "]], - ].forEach(function (values: any, index: number) { - it(('rule ' + values[0][0].ruleType + ' should create " ' + ' "') + values[1] + ' | ' + values[2] + ' | ' + values[3] + '"', async function () { - const result: string[] = sutW.reviewRules(values[0], 2); + ].forEach(function (values: (string[] | Rule[])[], index: number) { + it(('rule ' + (values[0][0]as Rule).ruleType as string + ' should create " ' + ' "') + values[1] + ' | ' + values[2] + ' | ' + values[3] + '"', async function () { + const result: string[] = sutW.reviewRules(values[0]as Rule[], 2); assert.equal(result[0], values[1][0]); assert.equal(result[1], values[2][0]); assert.equal(result[2], values[3][0]); @@ -432,13 +432,13 @@ describe('KmnFileWriter', function () { "dk(B2) + [NCAPS RALT K_A] > 'â'\n\n" ] ], - ].forEach(function (values: any) { + ].forEach(function (values: (string[] | Rule[])[], index: number) { it(('an array of Rules should create a set of kmn rules '), async function () { const data: ProcessedData = { keylayoutFilename: "", kmnFilename: "", modifiers: [[]], - rules: values[0] + rules: values[0] as Rule[] }; const result1 = sutW.writeDataRules(data); assert.isTrue(result1 === values[1][0]); diff --git a/resources/standards-data/keylayout/dtd/keylayout.xsd b/resources/standards-data/keylayout/dtd/keylayout.xsd index 48e84f3f15..554d5bbd27 100644 --- a/resources/standards-data/keylayout/dtd/keylayout.xsd +++ b/resources/standards-data/keylayout/dtd/keylayout.xsd @@ -8,7 +8,10 @@ - + + @@ -63,7 +66,7 @@ - + @@ -83,7 +86,7 @@ - +