@keymanapp/kmc-package 17.0.85-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nyc_output/coverage-4892-1681239251006-0.json +1 -0
- package/Makefile +38 -0
- package/build/src/kmp-compiler.d.ts +18 -0
- package/build/src/kmp-compiler.d.ts.map +1 -0
- package/build/src/kmp-compiler.js +232 -0
- package/build/src/kmp-compiler.js.map +1 -0
- package/build/src/kmp-json-file.d.ts +68 -0
- package/build/src/kmp-json-file.d.ts.map +1 -0
- package/build/src/kmp-json-file.js +2 -0
- package/build/src/kmp-json-file.js.map +1 -0
- package/build/src/kps-file.d.ts +98 -0
- package/build/src/kps-file.d.ts.map +1 -0
- package/build/src/kps-file.js +16 -0
- package/build/src/kps-file.js.map +1 -0
- package/build/test/helpers/index.d.ts +10 -0
- package/build/test/helpers/index.d.ts.map +1 -0
- package/build/test/helpers/index.js +17 -0
- package/build/test/helpers/index.js.map +1 -0
- package/build/test/test-package-compiler.d.ts +2 -0
- package/build/test/test-package-compiler.d.ts.map +1 -0
- package/build/test/test-package-compiler.js +97 -0
- package/build/test/test-package-compiler.js.map +1 -0
- package/build/test/tsconfig.tsbuildinfo +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/build.sh +66 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +146 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/kmp-compiler.ts.html +904 -0
- package/coverage/lcov-report/test/helpers/index.html +116 -0
- package/coverage/lcov-report/test/helpers/index.ts.html +136 -0
- package/coverage/lcov-report/test/index.html +116 -0
- package/coverage/lcov-report/test/test-package-compiler.ts.html +445 -0
- package/coverage/lcov.info +518 -0
- package/package.json +54 -0
- package/src/kmp-compiler.ts +273 -0
- package/src/kmp-json-file.ts +77 -0
- package/src/kps-file.ts +126 -0
- package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.js +55 -0
- package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.intermediate.json +48 -0
- package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.json +48 -0
- package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kmp.zipped.json +45 -0
- package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.kps +35 -0
- package/test/fixtures/example.qaa.sencoten/example.qaa.sencoten.model.ts +6 -0
- package/test/fixtures/example.qaa.sencoten/wordlist.tsv +10 -0
- package/test/fixtures/khmer_angkor/build/khmer_angkor.js +1 -0
- package/test/fixtures/khmer_angkor/build/khmer_angkor.kmx +0 -0
- package/test/fixtures/khmer_angkor/build/khmer_angkor.kvk +0 -0
- package/test/fixtures/khmer_angkor/ref/kmp.json +110 -0
- package/test/fixtures/khmer_angkor/shared/fonts/khmer/busrakbd/khmer_busra_kbd.ttf +0 -0
- package/test/fixtures/khmer_angkor/shared/fonts/khmer/mondulkiri/FONTLOG.txt +123 -0
- package/test/fixtures/khmer_angkor/shared/fonts/khmer/mondulkiri/Mondulkiri-R.ttf +0 -0
- package/test/fixtures/khmer_angkor/shared/fonts/khmer/mondulkiri/OFL-FAQ.txt +425 -0
- package/test/fixtures/khmer_angkor/shared/fonts/khmer/mondulkiri/OFL.txt +95 -0
- package/test/fixtures/khmer_angkor/source/khmer_angkor.kps +131 -0
- package/test/fixtures/khmer_angkor/source/readme.htm +23 -0
- package/test/fixtures/khmer_angkor/source/splash.gif +0 -0
- package/test/fixtures/khmer_angkor/source/welcome/KAK_Documentation_EN.pdf +0 -0
- package/test/fixtures/khmer_angkor/source/welcome/KAK_Documentation_KH.pdf +0 -0
- package/test/fixtures/khmer_angkor/source/welcome/image002.png +0 -0
- package/test/fixtures/khmer_angkor/source/welcome/keyboard_layout.png +0 -0
- package/test/fixtures/khmer_angkor/source/welcome/welcome.htm +54 -0
- package/test/fixtures/withfolders.qaa.sencoten/build/withfolders.qaa.sencoten.model.js +55 -0
- package/test/fixtures/withfolders.qaa.sencoten/source/welcome.htm +1 -0
- package/test/fixtures/withfolders.qaa.sencoten/source/withfolders.qaa.sencoten.model.kps +41 -0
- package/test/fixtures/withfolders.qaa.sencoten/source/withfolders.qaa.sencoten.model.ts +6 -0
- package/test/fixtures/withfolders.qaa.sencoten/source/wordlist.tsv +10 -0
- package/test/fixtures/withfolders.qaa.sencoten/withfolders.qaa.sencoten.model.kmp.intermediate.json +52 -0
- package/test/fixtures/withfolders.qaa.sencoten/withfolders.qaa.sencoten.model.kmp.zipped.json +50 -0
- package/test/helpers/index.ts +17 -0
- package/test/test-package-compiler.ts +120 -0
- package/test/tsconfig.json +19 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as xml2js from 'xml2js';
|
|
4
|
+
import JSZip from 'jszip';
|
|
5
|
+
import KEYMAN_VERSION from "@keymanapp/keyman-version";
|
|
6
|
+
|
|
7
|
+
import type { KpsFile, KpsFileContentFile, KpsFileInfo, KpsFileKeyboard, KpsFileLanguage, KpsFileLexicalModel, KpsFileOptions, KpsPackage } from './kps-file.js';
|
|
8
|
+
import type { KmpJsonFile, KmpJsonFileInfo, KmpJsonFileLanguage, KmpJsonFileOptions } from './kmp-json-file.js';
|
|
9
|
+
|
|
10
|
+
export { type KmpJsonFile } from './kmp-json-file.js';
|
|
11
|
+
|
|
12
|
+
const FILEVERSION_KMP_JSON = '12.0';
|
|
13
|
+
|
|
14
|
+
export default class KmpCompiler {
|
|
15
|
+
|
|
16
|
+
public transformKpsToKmpObject(kpsString: string, kpsPath: string): KmpJsonFile {
|
|
17
|
+
|
|
18
|
+
// Load the KPS data from XML as JS structured data.
|
|
19
|
+
|
|
20
|
+
let kpsPackage = (() => {
|
|
21
|
+
let a: KpsPackage;
|
|
22
|
+
let parser = new xml2js.Parser({
|
|
23
|
+
tagNameProcessors: [xml2js.processors.firstCharLowerCase],
|
|
24
|
+
explicitArray: false
|
|
25
|
+
});
|
|
26
|
+
parser.parseString(kpsString, (e: unknown, r: unknown) => { a = r as KpsPackage });
|
|
27
|
+
return a;
|
|
28
|
+
})();
|
|
29
|
+
|
|
30
|
+
let kps: KpsFile = kpsPackage.package;
|
|
31
|
+
|
|
32
|
+
//
|
|
33
|
+
// To convert to kmp.json, we need to:
|
|
34
|
+
//
|
|
35
|
+
// 1. Unwrap arrays (and convert to array where single object)
|
|
36
|
+
// 2. Fix casing on `iD`
|
|
37
|
+
// 3. Rewrap info, keyboard.languages, lexicalModel.languages, startMenu.items elements
|
|
38
|
+
// 4. Remove options.followKeyboardVersion, file.fileType
|
|
39
|
+
// 5. Convert file.copyLocation to a Number
|
|
40
|
+
// 6. Filenames need to be basenames (but this comes after processing)
|
|
41
|
+
//
|
|
42
|
+
|
|
43
|
+
// Start to construct the kmp.json file from the .kps file
|
|
44
|
+
|
|
45
|
+
let kmp: KmpJsonFile = {
|
|
46
|
+
system: {
|
|
47
|
+
fileVersion: FILEVERSION_KMP_JSON,
|
|
48
|
+
keymanDeveloperVersion: KEYMAN_VERSION.VERSION
|
|
49
|
+
},
|
|
50
|
+
options: {}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
//
|
|
54
|
+
// Fill in additional fields
|
|
55
|
+
//
|
|
56
|
+
|
|
57
|
+
let keys: [keyof KpsFileOptions, keyof KmpJsonFileOptions][] = [
|
|
58
|
+
['executeProgram','executeProgram'],
|
|
59
|
+
['graphicFile', 'graphicFile'],
|
|
60
|
+
['msiFileName','msiFilename'],
|
|
61
|
+
['msiOptions', 'msiOptions'],
|
|
62
|
+
['readMeFile', 'readmeFile']
|
|
63
|
+
];
|
|
64
|
+
for (let [src,dst] of keys) {
|
|
65
|
+
if (kps.options[src]) {
|
|
66
|
+
kmp.options[dst] = kps.options[src];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
//
|
|
71
|
+
// Add basic metadata
|
|
72
|
+
//
|
|
73
|
+
|
|
74
|
+
if(kps.info) {
|
|
75
|
+
kmp.info = this.kpsInfoToKmpInfo(kps.info);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// FollowKeyboardVersion support
|
|
79
|
+
|
|
80
|
+
if(kps.options?.followKeyboardVersion !== undefined) {
|
|
81
|
+
kmp.info.version = {
|
|
82
|
+
description: this.extractKeyboardVersionFromKmx(kpsPath, kps)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//
|
|
87
|
+
// Add file metadata
|
|
88
|
+
//
|
|
89
|
+
|
|
90
|
+
if(kps.files && kps.files.file) {
|
|
91
|
+
kmp.files = this.arrayWrap(kps.files.file).map((file: KpsFileContentFile) => {
|
|
92
|
+
return {
|
|
93
|
+
name: file.name,
|
|
94
|
+
description: file.description,
|
|
95
|
+
copyLocation: parseInt(file.copyLocation, 10) || undefined
|
|
96
|
+
// note: we don't emit fileType as that is not permitted in kmp.json
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
kmp.files = kmp.files ?? [];
|
|
101
|
+
|
|
102
|
+
// Add the standard kmp.json self-referential to match existing implementations
|
|
103
|
+
kmp.files.push({
|
|
104
|
+
name: "kmp.json",
|
|
105
|
+
description: "Package information (JSON)"
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
//
|
|
109
|
+
// Add keyboard metadata
|
|
110
|
+
//
|
|
111
|
+
|
|
112
|
+
if(kps.keyboards && kps.keyboards.keyboard) {
|
|
113
|
+
kmp.keyboards = this.arrayWrap(kps.keyboards.keyboard).map((keyboard: KpsFileKeyboard) => {
|
|
114
|
+
return {
|
|
115
|
+
displayFont: keyboard.displayFont ? path.basename(keyboard.displayFont.replaceAll('\\', '/')) : undefined,
|
|
116
|
+
oskFont: keyboard.oSKFont ? path.basename(keyboard.oSKFont.replaceAll('\\', '/')) : undefined,
|
|
117
|
+
name:keyboard.name,
|
|
118
|
+
id:keyboard.iD,
|
|
119
|
+
version:keyboard.version,
|
|
120
|
+
languages: this.kpsLanguagesToKmpLanguages(this.arrayWrap(keyboard.languages.language) as KpsFileLanguage[])
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
//
|
|
126
|
+
// Add lexical-model metadata
|
|
127
|
+
//
|
|
128
|
+
|
|
129
|
+
if(kps.lexicalModels && kps.lexicalModels.lexicalModel) {
|
|
130
|
+
kmp.lexicalModels = this.arrayWrap(kps.lexicalModels.lexicalModel).map((model: KpsFileLexicalModel) => {
|
|
131
|
+
return { name:model.name, id:model.iD, languages: this.kpsLanguagesToKmpLanguages(this.arrayWrap(model.languages.language) as KpsFileLanguage[]) }
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//
|
|
136
|
+
// Add Windows Start Menu metadata
|
|
137
|
+
//
|
|
138
|
+
|
|
139
|
+
if(kps.startMenu && kps.startMenu.items) {
|
|
140
|
+
kmp.startMenu = {};
|
|
141
|
+
if(kps.startMenu.addUninstallEntry) kmp.startMenu.addUninstallEntry = kps.startMenu.addUninstallEntry === '';
|
|
142
|
+
if(kps.startMenu.folder) kmp.startMenu.folder = kps.startMenu.folder;
|
|
143
|
+
if(kps.startMenu.items && kps.startMenu.items.item) kmp.startMenu.items = this.arrayWrap(kps.startMenu.items.item);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//
|
|
147
|
+
// Add translation strings
|
|
148
|
+
//
|
|
149
|
+
|
|
150
|
+
if(kps.strings && kps.strings.string) {
|
|
151
|
+
kmp.strings = this.arrayWrap(kps.strings.string);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
kmp = this.stripUndefined(kmp) as KmpJsonFile;
|
|
155
|
+
|
|
156
|
+
return kmp;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Helper functions
|
|
160
|
+
|
|
161
|
+
private kpsInfoToKmpInfo(info: KpsFileInfo): KmpJsonFileInfo {
|
|
162
|
+
let ni: KmpJsonFileInfo = {};
|
|
163
|
+
|
|
164
|
+
const keys: [(keyof KpsFileInfo), (keyof KmpJsonFileInfo)][] = [
|
|
165
|
+
['author','author'],
|
|
166
|
+
['copyright','copyright'],
|
|
167
|
+
['name','name'],
|
|
168
|
+
['version','version'],
|
|
169
|
+
['webSite','website']
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
for (let [src,dst] of keys) {
|
|
173
|
+
if (info[src]) {
|
|
174
|
+
ni[dst] = {description: info[src]._ ?? (typeof info[src] == 'string' ? info[src].toString() : '')};
|
|
175
|
+
if(info[src].$ && info[src].$.URL) ni[dst].url = info[src].$.URL;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return ni;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
private arrayWrap(a: unknown) {
|
|
183
|
+
if (Array.isArray(a)) {
|
|
184
|
+
return a;
|
|
185
|
+
}
|
|
186
|
+
return [a];
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
private kpsLanguagesToKmpLanguages(language: KpsFileLanguage[]): KmpJsonFileLanguage[] {
|
|
190
|
+
return language.map((element) => { return { name: element._, id: element.$.ID } });
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
private extractKeyboardVersionFromKmx(kpsPath: string, kps: KpsFile) {
|
|
194
|
+
// TODO-LDML: #7340 this is incomplete; we need to read from first .kmx
|
|
195
|
+
// When we do this, we'll need to update fixtures to have the correct
|
|
196
|
+
// version string also
|
|
197
|
+
if(!kps.keyboards || !kps.keyboards.keyboard) {
|
|
198
|
+
// We don't read from the kps metadata because we want the keyboard
|
|
199
|
+
// version data here;
|
|
200
|
+
// TODO: currently model files don't support follow-version?
|
|
201
|
+
return '0.0.0';
|
|
202
|
+
}
|
|
203
|
+
let k: KpsFileKeyboard[] = this.arrayWrap(kps.keyboards.keyboard);
|
|
204
|
+
return k?.[0]?.version ?? '0.0.0';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private stripUndefined(o: any) {
|
|
208
|
+
for(const key in o) {
|
|
209
|
+
if(o[key] === undefined) {
|
|
210
|
+
delete o[key];
|
|
211
|
+
} else if(typeof o[key] == 'object') {
|
|
212
|
+
o[key] = this.stripUndefined(o[key]);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return o;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Returns a Promise to the serialized data which can then be written to a .kmp file.
|
|
220
|
+
*
|
|
221
|
+
* @param kpsFilename - Filename of the kps, not read, used only for calculating relative paths
|
|
222
|
+
* @param kmpJsonData - The kmp.json Object
|
|
223
|
+
*/
|
|
224
|
+
public buildKmpFile(kpsFilename: string, kmpJsonData: KmpJsonFile): Promise<string> {
|
|
225
|
+
const zip = JSZip();
|
|
226
|
+
|
|
227
|
+
const kmpJsonFileName = 'kmp.json';
|
|
228
|
+
|
|
229
|
+
const basePath = path.dirname(kpsFilename);
|
|
230
|
+
|
|
231
|
+
// Make a copy of kmpJsonData, as we mutate paths for writing
|
|
232
|
+
const data: KmpJsonFile = JSON.parse(JSON.stringify(kmpJsonData));
|
|
233
|
+
if(!data.files) {
|
|
234
|
+
data.files = [];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
data.files.forEach(function(value) {
|
|
238
|
+
// Get the path of the file
|
|
239
|
+
let filename = value.name;
|
|
240
|
+
|
|
241
|
+
// We add this separately after zipping all other files
|
|
242
|
+
if(filename == 'kmp.json') {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if(path.isAbsolute(value.name)) {
|
|
247
|
+
// absolute paths are not very cross-platform compatible -- we are going to have trouble
|
|
248
|
+
// with path separators and roots
|
|
249
|
+
// TODO: emit a warning
|
|
250
|
+
} else {
|
|
251
|
+
// Transform separators to platform separators -- kps files may use
|
|
252
|
+
// either / or \, although older kps files were always \.
|
|
253
|
+
if(path.sep == '/') {
|
|
254
|
+
filename = filename.replace(/\\/g, '/');
|
|
255
|
+
} else {
|
|
256
|
+
filename = filename.replace(/\//g, '\\');
|
|
257
|
+
}
|
|
258
|
+
filename = path.resolve(basePath, filename);
|
|
259
|
+
}
|
|
260
|
+
const basename = path.basename(filename);
|
|
261
|
+
let data = fs.readFileSync(filename);
|
|
262
|
+
zip.file(basename, data);
|
|
263
|
+
|
|
264
|
+
// Remove path data from files before JSON save
|
|
265
|
+
value.name = basename;
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
zip.file(kmpJsonFileName, JSON.stringify(data, null, 2));
|
|
269
|
+
|
|
270
|
+
// Generate kmp file
|
|
271
|
+
return zip.generateAsync({type: 'binarystring', compression:'DEFLATE'});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export interface KmpJsonFile {
|
|
2
|
+
system: KmpJsonFileSystem;
|
|
3
|
+
options: KmpJsonFileOptions;
|
|
4
|
+
info?: KmpJsonFileInfo;
|
|
5
|
+
files?: KmpJsonFileContentFile[];
|
|
6
|
+
lexicalModels?: KmpJsonFileLexicalModel[];
|
|
7
|
+
startMenu?: KmpJsonFileStartMenu;
|
|
8
|
+
keyboards?: KmpJsonFileKeyboard[];
|
|
9
|
+
strings?: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface KmpJsonFileSystem {
|
|
13
|
+
keymanDeveloperVersion: string;
|
|
14
|
+
fileVersion: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface KmpJsonFileOptions {
|
|
18
|
+
readmeFile?: string;
|
|
19
|
+
graphicFile?: string;
|
|
20
|
+
executeProgram?: string;
|
|
21
|
+
msiFilename?: string;
|
|
22
|
+
msiOptions?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface KmpJsonFileInfo {
|
|
26
|
+
website?: KmpJsonFileInfoItem;
|
|
27
|
+
version?: KmpJsonFileInfoItem;
|
|
28
|
+
name?: KmpJsonFileInfoItem;
|
|
29
|
+
copyright?: KmpJsonFileInfoItem;
|
|
30
|
+
author?: KmpJsonFileInfoItem;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface KmpJsonFileInfoItem {
|
|
34
|
+
description: string;
|
|
35
|
+
url?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface KmpJsonFileContentFile {
|
|
39
|
+
name: string;
|
|
40
|
+
description: string;
|
|
41
|
+
copyLocation?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface KmpJsonFileLexicalModel {
|
|
45
|
+
name: string;
|
|
46
|
+
id: string;
|
|
47
|
+
languages: KmpJsonFileLanguage[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface KmpJsonFileLanguage {
|
|
51
|
+
name: string;
|
|
52
|
+
id: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface KmpJsonFileKeyboard {
|
|
56
|
+
name: string;
|
|
57
|
+
id: string;
|
|
58
|
+
version: string;
|
|
59
|
+
oskFont?: string;
|
|
60
|
+
displayFont?: string;
|
|
61
|
+
rtl?: boolean;
|
|
62
|
+
languages?: KmpJsonFileLanguage[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface KmpJsonFileStartMenu {
|
|
66
|
+
folder?: string;
|
|
67
|
+
addUninstallEntry?: boolean;
|
|
68
|
+
items?: KmpJsonFileStartMenuItem[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface KmpJsonFileStartMenuItem {
|
|
72
|
+
name: string;
|
|
73
|
+
filename: string;
|
|
74
|
+
arguments?: string;
|
|
75
|
+
icon?: string;
|
|
76
|
+
location?: string;
|
|
77
|
+
}
|
package/src/kps-file.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
//
|
|
2
|
+
// The interfaces in this file are designed with reference to the
|
|
3
|
+
// mapped structures produced by xml2js when passed a .kps file.
|
|
4
|
+
//
|
|
5
|
+
// A few notes:
|
|
6
|
+
//
|
|
7
|
+
// * Casing is updated to camelCase during load (leaving `iD` as a
|
|
8
|
+
// mixed up beastie).
|
|
9
|
+
// * Arrays are buried a layer too deep (e.g. <Files><File/><File/></Files>
|
|
10
|
+
// leads to KpsFiles.KpsFile[]
|
|
11
|
+
// * Properties such as used in Info Items use `_` and `$` and must be
|
|
12
|
+
// extracted.
|
|
13
|
+
// * Strings element is not yet checked to be correct
|
|
14
|
+
//
|
|
15
|
+
|
|
16
|
+
export interface KpsPackage {
|
|
17
|
+
/**
|
|
18
|
+
* <Package> -- the root element.
|
|
19
|
+
*/
|
|
20
|
+
package: KpsFile;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface KpsFile {
|
|
24
|
+
system: KpsFileSystem;
|
|
25
|
+
options: KpsFileOptions;
|
|
26
|
+
info?: KpsFileInfo;
|
|
27
|
+
files?: KpsFileContentFiles;
|
|
28
|
+
keyboards?: KpsFileKeyboards;
|
|
29
|
+
lexicalModels?: KpsFileLexicalModels;
|
|
30
|
+
startMenu?: KpsFileStartMenu;
|
|
31
|
+
strings?: KpsFileStrings;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface KpsFileSystem {
|
|
35
|
+
keymanDeveloperVersion: string;
|
|
36
|
+
fileVersion: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface KpsFileOptions {
|
|
40
|
+
followKeyboardVersion?: string;
|
|
41
|
+
readMeFile?: string;
|
|
42
|
+
graphicFile?: string;
|
|
43
|
+
executeProgram?: string;
|
|
44
|
+
msiFileName?: string;
|
|
45
|
+
msiOptions?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface KpsFileInfo {
|
|
49
|
+
name?: KpsFileInfoItem;
|
|
50
|
+
copyright?: KpsFileInfoItem;
|
|
51
|
+
author?: KpsFileInfoItem;
|
|
52
|
+
webSite?: KpsFileInfoItem;
|
|
53
|
+
version?: KpsFileInfoItem;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface KpsFileInfoItem {
|
|
57
|
+
_: string;
|
|
58
|
+
$: { URL: string };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface KpsFileContentFiles {
|
|
62
|
+
file: KpsFileContentFile[] | KpsFileContentFile;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface KpsFileContentFile {
|
|
66
|
+
name: string;
|
|
67
|
+
description: string;
|
|
68
|
+
copyLocation: string;
|
|
69
|
+
fileType: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface KpsFileLexicalModel {
|
|
73
|
+
name: string;
|
|
74
|
+
iD: string;
|
|
75
|
+
languages: KpsFileLanguages;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface KpsFileLexicalModels {
|
|
79
|
+
lexicalModel: KpsFileLexicalModel[] | KpsFileLexicalModel;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface KpsFileLanguages {
|
|
83
|
+
language: KpsFileLanguage[] | KpsFileLanguage;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface KpsFileLanguage {
|
|
87
|
+
_: string;
|
|
88
|
+
$: { ID: string }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface KpsFileKeyboard {
|
|
92
|
+
name: string;
|
|
93
|
+
iD: string;
|
|
94
|
+
version: string;
|
|
95
|
+
oSKFont?: string;
|
|
96
|
+
displayFont?: string;
|
|
97
|
+
rtl?: boolean;
|
|
98
|
+
languages?: KpsFileLanguages;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface KpsFileKeyboards {
|
|
102
|
+
keyboard: KpsFileKeyboard[] | KpsFileKeyboard;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface KpsFileStartMenu {
|
|
106
|
+
folder?: string;
|
|
107
|
+
addUninstallEntry?: string;
|
|
108
|
+
items?: KpsFileStartMenuItems;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface KpsFileStartMenuItem {
|
|
112
|
+
name: string;
|
|
113
|
+
filename: string;
|
|
114
|
+
arguments?: string;
|
|
115
|
+
icon?: string;
|
|
116
|
+
location?: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface KpsFileStartMenuItems {
|
|
120
|
+
item: KpsFileStartMenuItem[] | KpsFileStartMenuItem;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface KpsFileStrings {
|
|
124
|
+
//TODO: validate this structure
|
|
125
|
+
string: string[] | string;
|
|
126
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
'use strict';
|
|
3
|
+
var definitions = {
|
|
4
|
+
defaults: {
|
|
5
|
+
version: "16.0.60-alpha",
|
|
6
|
+
applyCasing: function defaultApplyCasing(casing, text) {
|
|
7
|
+
switch (casing) {
|
|
8
|
+
case 'lower':
|
|
9
|
+
return text.toLowerCase();
|
|
10
|
+
case 'upper':
|
|
11
|
+
return text.toUpperCase();
|
|
12
|
+
case 'initial':
|
|
13
|
+
var headCode = text.charCodeAt(0);
|
|
14
|
+
// The length of the first code unit, as measured in code points.
|
|
15
|
+
var headUnitLength = 1;
|
|
16
|
+
// Is the first character a high surrogate, indicating possible use of UTF-16
|
|
17
|
+
// surrogate pairs? Also, is the string long enough for there to BE a pair?
|
|
18
|
+
if (text.length > 1 && headCode >= 0xD800 && headCode <= 0xDBFF) {
|
|
19
|
+
// It's possible, so now we check for low surrogates.
|
|
20
|
+
var lowSurrogateCode = text.charCodeAt(1);
|
|
21
|
+
if (lowSurrogateCode >= 0xDC00 && lowSurrogateCode <= 0xDFFF) {
|
|
22
|
+
// We have a surrogate pair; this pair is the 'first' character.
|
|
23
|
+
headUnitLength++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Capitalizes the first code unit of the string, leaving the rest intact.
|
|
27
|
+
return text.substring(0, headUnitLength).toUpperCase() // head - uppercased
|
|
28
|
+
.concat(text.substring(headUnitLength)); // tail - lowercased
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}, model: {
|
|
32
|
+
searchTermToKey: function defaultCasedSearchTermToKey(wordform, applyCasing) {
|
|
33
|
+
// While this is a bit WET, as the basic `defaultSearchTermToKey` exists and performs some of
|
|
34
|
+
// the same functions, repetition is the easiest way to allow the function to be safely compiled
|
|
35
|
+
// with ease by use of `.toString()`.
|
|
36
|
+
return Array.from(wordform
|
|
37
|
+
.normalize('NFKD')
|
|
38
|
+
// Remove any combining diacritics (if input is in NFKD)
|
|
39
|
+
.replace(/[\u0300-\u036F]/g, '')) // end of `Array.from`
|
|
40
|
+
.map(function (c) { return applyCasing('lower', c); })
|
|
41
|
+
.join('');
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
applyCasing: function(caseToApply, text) {
|
|
45
|
+
return definitions.defaults.applyCasing(caseToApply, text);
|
|
46
|
+
},
|
|
47
|
+
searchTermToKey: function(text) {
|
|
48
|
+
return definitions.model.searchTermToKey(text, definitions.applyCasing);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
LMLayerWorker.loadModel(new models.TrieModel({"totalWeight":44103,"root":{"type":"internal","weight":13644,"values":["t","e","s","ȼ","n","u","i"],"children":{"t":{"type":"leaf","weight":13644,"entries":[{"key":"tŧe","weight":13644,"content":"TŦE"}]},"e":{"type":"leaf","weight":9134,"entries":[{"key":"e","weight":9134,"content":"E"}]},"s":{"type":"internal","weight":4816,"values":["e","w"],"children":{"e":{"type":"leaf","weight":4816,"entries":[{"key":"sen","weight":4816,"content":"SEN"}]},"w":{"type":"leaf","weight":2621,"entries":[{"key":"sw","weight":2621,"content":"SW̱"}]}}},"ȼ":{"type":"internal","weight":3479,"values":["","s"],"children":{"":{"type":"leaf","weight":3479,"entries":[{"key":"ȼ","weight":3479,"content":"Ȼ"}]},"s":{"type":"leaf","weight":1925,"entries":[{"key":"ȼse","weight":1925,"content":"ȻSE"}]}}},"n":{"type":"leaf","weight":2314,"entries":[{"key":"niƚ","weight":2314,"content":"NIȽ"}]},"u":{"type":"leaf","weight":2298,"entries":[{"key":"u ","weight":2298,"content":"U¸"}]},"i":{"type":"leaf","weight":1988,"entries":[{"key":"i ","weight":1988,"content":"I¸"},{"key":"i","weight":1884,"content":"I"}]}}}}, {
|
|
52
|
+
wordBreaker: wordBreakers['default'],
|
|
53
|
+
searchTermToKey: definitions.searchTermToKey,
|
|
54
|
+
}));
|
|
55
|
+
})();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"system": {
|
|
3
|
+
"keymanDeveloperVersion": "12.0.1500.0",
|
|
4
|
+
"fileVersion": "12.0"
|
|
5
|
+
},
|
|
6
|
+
"options": {},
|
|
7
|
+
"info": {
|
|
8
|
+
"author": {
|
|
9
|
+
"description": "Eddie Antonio Santos",
|
|
10
|
+
"url": "mailto:Eddie.Santos@nrc-cnrc.gc.ca"
|
|
11
|
+
},
|
|
12
|
+
"copyright": {
|
|
13
|
+
"description": "© 2019 National Research Council Canada"
|
|
14
|
+
},
|
|
15
|
+
"name": {
|
|
16
|
+
"description": "SENĆOŦEN (Saanich Dialect) Lexical Model"
|
|
17
|
+
},
|
|
18
|
+
"version": {
|
|
19
|
+
"description": "0.0.0"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
{
|
|
24
|
+
"name": "example.qaa.sencoten.model.js",
|
|
25
|
+
"description": "Lexical model example.qaa.sencoten.model.js"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"description": "Package information (JSON)",
|
|
29
|
+
"name": "kmp.json"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"lexicalModels": [
|
|
33
|
+
{
|
|
34
|
+
"name": "SENĆOŦEN dictionary",
|
|
35
|
+
"id": "example.qaa.sencoten",
|
|
36
|
+
"languages": [
|
|
37
|
+
{
|
|
38
|
+
"name": "North Straits Salish",
|
|
39
|
+
"id": "qaa"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "SENĆOŦEN",
|
|
43
|
+
"id": "qaa-Latn"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"system": {
|
|
3
|
+
"keymanDeveloperVersion": "12.0.1500.0",
|
|
4
|
+
"fileVersion": "12.0"
|
|
5
|
+
},
|
|
6
|
+
"options": {},
|
|
7
|
+
"info": {
|
|
8
|
+
"author": {
|
|
9
|
+
"description": "Eddie Antonio Santos",
|
|
10
|
+
"url": "mailto:Eddie.Santos@nrc-cnrc.gc.ca"
|
|
11
|
+
},
|
|
12
|
+
"copyright": {
|
|
13
|
+
"description": "© 2019 National Research Council Canada"
|
|
14
|
+
},
|
|
15
|
+
"name": {
|
|
16
|
+
"description": "SENĆOŦEN (Saanich Dialect) Lexical Model"
|
|
17
|
+
},
|
|
18
|
+
"version": {
|
|
19
|
+
"description": "0.0.0"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
{
|
|
24
|
+
"name": "..\\build\\example.qaa.sencoten.model.js",
|
|
25
|
+
"description": "Lexical model example.qaa.sencoten.model.js"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "kmp.json",
|
|
29
|
+
"description": "Package information (JSON)"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"lexicalModels": [
|
|
33
|
+
{
|
|
34
|
+
"name": "SENĆOŦEN dictionary",
|
|
35
|
+
"id": "example.qaa.sencoten",
|
|
36
|
+
"languages": [
|
|
37
|
+
{
|
|
38
|
+
"name": "North Straits Salish",
|
|
39
|
+
"id": "qaa"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "SENĆOŦEN",
|
|
43
|
+
"id": "qaa-Latn"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"system": {
|
|
3
|
+
"keymanDeveloperVersion": "12.0.1500.0",
|
|
4
|
+
"fileVersion": "12.0"
|
|
5
|
+
},
|
|
6
|
+
"options": {},
|
|
7
|
+
"info": {
|
|
8
|
+
"author": {
|
|
9
|
+
"description": "Eddie Antonio Santos",
|
|
10
|
+
"url": "mailto:Eddie.Santos@nrc-cnrc.gc.ca"
|
|
11
|
+
},
|
|
12
|
+
"copyright": {
|
|
13
|
+
"description": "© 2019 National Research Council Canada"
|
|
14
|
+
},
|
|
15
|
+
"name": {
|
|
16
|
+
"description": "SENĆOŦEN (Saanich Dialect) Lexical Model"
|
|
17
|
+
},
|
|
18
|
+
"version": {
|
|
19
|
+
"description": "1.0.3"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
{
|
|
24
|
+
"name": "example.qaa.sencoten.model.js",
|
|
25
|
+
"description": "Lexical model example.qaa.sencoten.model.js",
|
|
26
|
+
"copyLocation": 0
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"lexicalModels": [
|
|
30
|
+
{
|
|
31
|
+
"name": "SENĆOŦEN dictionary",
|
|
32
|
+
"id": "example.qaa.sencoten",
|
|
33
|
+
"languages": [
|
|
34
|
+
{
|
|
35
|
+
"name": "North Straits Salish",
|
|
36
|
+
"id": "qaa"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "SENĆOŦEN",
|
|
40
|
+
"id": "qaa-Latn"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|