mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-11 11:25:34 +00:00
feat(developer): replace all any-datatypes
add checkForCorrespondingElements() to validate() min/maxOccurs added in keylayout.xsd edited keylayout.dtd (possible remaining squiggle lines will be addressed in PR#15860
This commit is contained in:
parent
9a9d33bb50
commit
461fdd86c4
7 changed files with 194 additions and 134 deletions
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 <when state="none" next="1"/> 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 <when state="1" output="â"/> ) .........................................
|
||||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
* -->
|
||||
|
||||
<!ELEMENT keyboard (layouts, keyMapSet) >
|
||||
<!ELEMENT keyboard (modifierMap)* >
|
||||
<!ATTLIST keyboard (group, id, name, maxout) #REQUIRED >
|
||||
|
||||
<!ELEMENT layouts ( layout)* >
|
||||
<!ATTLIST layout (first, last, mapSet, modifiers) #REQUIRED >
|
||||
<!-- Overall structure -->
|
||||
<!-- from TN 2056:
|
||||
<!ELEMENT keyboard (layouts+, modifierMap+, keyMapSet+, actions*, terminators*)>
|
||||
but kmc-convert uses instead: -->
|
||||
<!ELEMENT keyboard layouts>
|
||||
<!ELEMENT keyboard modifierMap+>
|
||||
<!ELEMENT keyboard keyMapSet+>
|
||||
<!ELEMENT keyboard actions*>
|
||||
<!ELEMENT keyboard terminators*>
|
||||
|
||||
<!ELEMENT modifierMap (keyMapSelect)*>
|
||||
<!ATTLIST modifierMap (id, defaultIndex) #REQUIRED >
|
||||
<!ATTLIST keyboard group NMTOKEN #REQUIRED >
|
||||
<!ATTLIST keyboard id NMTOKEN #REQUIRED >
|
||||
<!ATTLIST keyboard name CDATA #REQUIRED >
|
||||
<!ATTLIST keyboard maxout NMTOKEN #IMPLIED >
|
||||
|
||||
<!ELEMENT keyMapSelect (modifier)*>
|
||||
<!ATTLIST keyMapSelect (mapIndex) #REQUIRED >
|
||||
<!-- Hardware layout elements -->
|
||||
<!ELEMENT layouts (layout+) >
|
||||
<!ELEMENT layout EMPTY >
|
||||
<!ATTLIST layout first NMTOKEN #REQUIRED >
|
||||
<!ATTLIST layout last NMTOKEN #REQUIRED >
|
||||
<!ATTLIST layout mapSet IDREF #REQUIRED >
|
||||
<!ATTLIST layout modifiers IDREF #REQUIRED >
|
||||
|
||||
<!ATTLIST modifier (keys) #REQUIRED >
|
||||
<!-- Modifier descriptions -->
|
||||
<!ELEMENT modifierMap (keyMapSelect+) >
|
||||
<!ATTLIST modifierMap id ID #REQUIRED >
|
||||
<!ATTLIST modifierMap defaultIndex NMTOKEN #REQUIRED >
|
||||
|
||||
<!ELEMENT keyMapSet ( keyMap)* >
|
||||
<!ATTLIST keyMapSet (id) #REQUIRED >
|
||||
<!ELEMENT keyMapSelect (modifier+) >
|
||||
<!ATTLIST keyMapSelect mapIndex NMTOKEN #REQUIRED >
|
||||
|
||||
<!ELEMENT keyMap (key)* >
|
||||
<!ATTLIST keyMap (index) #REQUIRED >
|
||||
<!ELEMENT modifier EMPTY >
|
||||
<!ATTLIST modifier keys CDATA #REQUIRED >
|
||||
|
||||
<!ATTLIST key (code) #REQUIRED >
|
||||
<!-- Keyboard mapping most keyboards use ANSI only but some use JIS also -->
|
||||
<!ELEMENT keyMapSet (keyMap+) >
|
||||
<!ATTLIST keyMapSet id ID #REQUIRED >
|
||||
|
||||
<!ELEMENT keyMap (key+) >
|
||||
<!ATTLIST keyMap index NMTOKEN #REQUIRED >
|
||||
<!ATTLIST keyMap baseMapSet IDREF #IMPLIED >
|
||||
<!ATTLIST keyMap baseIndex NMTOKEN #IMPLIED >
|
||||
|
||||
<!ELEMENT key (action*) >
|
||||
<!ATTLIST key code NMTOKEN #REQUIRED >
|
||||
<!-- from TN 2056:
|
||||
<!ATTLIST key output CDATA #IMPLIED >
|
||||
<!ATTLIST key action IDREF #IMPLIED >
|
||||
since output or action needs to be present kmc-convert uses instead: -->
|
||||
<!ATTLIST key (action #REQUIRED output #IMPLIED | output #REQUIRED action #IMPLIED)>
|
||||
|
||||
<!-- Actions (state records) -->
|
||||
<!ELEMENT actions (action+) >
|
||||
|
||||
<!ELEMENT action (when+) >
|
||||
<!ATTLIST action id ID #IMPLIED >
|
||||
|
||||
<!ELEMENT when EMPTY >
|
||||
<!ATTLIST when state NMTOKEN #REQUIRED >
|
||||
<!ATTLIST when through NMTOKEN #IMPLIED >
|
||||
<!ATTLIST when output CDATA #IMPLIED >
|
||||
<!ATTLIST when multiplier NMTOKEN #IMPLIED >
|
||||
<!ATTLIST when next NMTOKEN #IMPLIED >
|
||||
|
||||
<!-- Terminators -->
|
||||
<!-- kmc-convert does not use terminators -->
|
||||
<!ELEMENT terminators (when+)>
|
||||
|
|
|
|||
|
|
@ -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]));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@
|
|||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
|
||||
<xs:element name="layouts">
|
||||
<!-- in TN 2056, the 'layouts' element is defined as maxOccurs="unbounded",
|
||||
but in practice, only one 'layouts' element is used per .keylayout file.
|
||||
<xs:element name="layouts" minOccurs="1" maxOccurs="unbounded"> -->
|
||||
<xs:element name="layouts" minOccurs="1" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="layout" minOccurs="1" maxOccurs="unbounded">
|
||||
|
|
@ -63,7 +66,7 @@
|
|||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="actions">
|
||||
<xs:element name="actions" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="action" minOccurs="1" maxOccurs="unbounded">
|
||||
|
|
@ -83,7 +86,7 @@
|
|||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<xs:element name="terminators">
|
||||
<xs:element name="terminators" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="when" minOccurs="1" maxOccurs="unbounded">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue