Merge branch 'epic/kmc-convert' into feat/developer/kmc-convert
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled

This commit is contained in:
SabineSIL 2026-06-25 17:12:02 +02:00 committed by GitHub
commit c53456ddc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
140 changed files with 1376 additions and 1415 deletions

View file

@ -119,7 +119,7 @@ jobs:
- name: Install meson
shell: bash
run: |
pip3 install --user meson==1.10.1
pip3 install --user meson==1.11.0
- name: Run test
shell: bash

View file

@ -8,12 +8,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Update labels based on changed files
uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Update labels based on PR title
id: labeler
uses: fuxingloh/multi-labeler@f5bd7323b53b0833c1e4ed8d7b797ae995ef75b4 # v2.0.1
uses: fuxingloh/multi-labeler@bcd50af464202999e57f556b4aefcf05a34abf85 # v5.0.0
with:
github-token: ${{secrets.GITHUB_TOKEN}}
config-path: .github/multi-labeler.yml

View file

@ -1,5 +1,48 @@
# Keyman Version History
## 19.0.247 alpha 2026-06-16
* chore: update multi-labeler to 5.0.0 (#16100)
## 19.0.246 alpha 2026-06-15
* feat(developer): run project validation from IDE (#16076)
* fix(core): use meson subsystem and needs_exe_wrapper for cross builds on Windows (#16088)
* docs(windows): add context documentation for additional Advanced options (#16089)
## 19.0.245 alpha 2026-06-10
* chore(web): hide KMX keyboard test page (#16083)
* docs(web): remove unnecessary mentioning of Keyman 17 from docs (#16085)
* chore(web): remove outdated `*_load.js` files (#16086)
* fix(web): fix `KeyboardStub.validateForCustomKeyboard` (#16067)
## 19.0.244 alpha 2026-06-09
* refactor(web): extract function, rename variables (#16063)
* refactor(web): reformatting of files (#16066)
* chore(web): improve the KeymanWeb integration documentation (#16064)
* chore(web): more improvements to documentation (#16070)
## 19.0.243 alpha 2026-06-08
* fix(developer): reduce km_core_keyboard_attrs size to 12 (#16075)
* chore(deps): bump minimatch in /core/tests/unit/wasm (#16045)
* chore(deps): bump glob from 10.4.5 to 10.5.0 in /core/tests/unit/wasm (#16046)
* chore(deps-dev): bump js-yaml from 4.1.0 to 4.2.0 in /core/tests/unit/wasm (#16047)
* chore(deps-dev): bump picomatch from 2.3.1 to 2.3.2 in /core/tests/unit/wasm (#16048)
* chore(deps): bump qs and express (#16009)
## 19.0.242 alpha 2026-06-03
* fix(web): fix displaying of keyboard menu (#16061)
## 19.0.241 alpha 2026-06-02
* chore(web): web-core preflight - strip core references (#16040)
* docs: add note on how to use composer on dockerized websites (#16029)
* fix(web): fix race displaying active keyboard in menu (#16042)
## 19.0.240 alpha 2026-05-28
* chore(web): add filename to link text of manual web tests (#16019)

View file

@ -1 +1 @@
19.0.241
19.0.248

View file

@ -36,22 +36,20 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
(function(kmw) {
kmw.init({
attachType:'auto'
}).then(function() {
kmw.addKeyboards({
id:'test_8568_deadkeys',
name:'Testcase for deadkeys bug (#8568)',
languages:{
id:'en',
name:'English'
},
filename:'./test_8568_deadkeys.js'
});
keyman.init({
attachType:'auto'
}).then(async function() {
await keyman.addKeyboards({
id:'test_8568_deadkeys',
name:'Testcase for deadkeys bug (#8568)',
languages:{
id:'en',
name:'English'
},
filename:'./test_8568_deadkeys.js'
});
})(keyman);
</script>
});
</script>
</head>

View file

@ -1,5 +1,6 @@
[host_machine]
system = 'windows'
subsystem = 'windows'
cpu_family = 'aarch64'
cpu = 'arm64'
endian = 'little'

View file

@ -1,5 +1,6 @@
[host_machine]
system = 'windows'
subsystem = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'
@ -11,3 +12,6 @@ ar = 'lib'
ld = 'link'
strip = 'llvm-strip'
pkgconfig = 'pkg-config'
[properties]
needs_exe_wrapper = false

View file

@ -133,9 +133,9 @@
}
},
"node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
"integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -538,9 +538,10 @@
}
},
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dev": true,
"license": "ISC",
"dependencies": {
@ -572,13 +573,13 @@
}
},
"node_modules/glob/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
"brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
@ -710,10 +711,20 @@
}
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@ -770,9 +781,9 @@
"license": "ISC"
},
"node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"version": "5.1.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"dev": true,
"license": "ISC",
"dependencies": {
@ -932,9 +943,9 @@
}
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {

View file

@ -110,7 +110,7 @@ hamburger menu in the Keyman app.
**Web**: copy khmer_angkor.js to your website, then [load it with KeymanWeb][load-keymanweb-keyboard]:
```js
keyman.addKeyboards({
await keyman.addKeyboards({
id:'khmer_angkor', // The keyboard's unique identification code.
name:'Khmer Angkor', // The keyboard's user-readable name.
language:{

View file

@ -198,6 +198,13 @@ The following parameters are available:
project (which can also be controlled at a project level with the
`skipMetadataFiles` option). This option is only valid for compiling projects.
`--publish-only`
: Skip building component files within a project, and only run the validation
step. Must be combined with `--for-publishing` in order to have an effect.
This option is only valid for compiling projects, and is mostly intended for
use by the IDE. Will fail if build artifacts for components are not present.
### Examples
```shell

View file

@ -109,7 +109,7 @@ hamburger menu in the Keyman app.
**Web**: copy khmer_angkor.js to your website, then [load it with KeymanWeb][load-keymanweb-keyboard]:
```js
keyman.addKeyboards({
await keyman.addKeyboards({
id:'khmer_angkor', // The keyboard's unique identification code.
name:'Khmer Angkor', // The keyboard's user-readable name.
language:{

View file

@ -35,6 +35,7 @@ export function declareBuild(program: Command) {
buildCommand.command('file [infile...]', {isDefault: true})
.description(`Compile one or more source files or projects ('file' subcommand is default).`)
.option('--for-publishing', 'Verify that project meets @keymanapp repository requirements')
.option('--publish-only', 'Only run the for-publishing validation, skip all other build steps')
.addHelpText('after', `
Supported file types:
* folder: Keyman project in folder

View file

@ -49,6 +49,7 @@ export class BuildKeyboardInfo extends BuildActivity {
sourcePath: calculateSourcePath(infile),
lastCommitDate,
forPublishing: !!options.forPublishing,
publishOnly: !!options.publishOnly,
};
// Note: should we always ignore the passed-in output filename for .keyboard_info?
const outputFilename = project.getOutputFilePath(KeymanFileTypes.Binary.KeyboardInfo);

View file

@ -75,6 +75,7 @@ export class BuildModelInfo extends BuildActivity {
kpsFilename: project.resolveInputFilePath(kps),
lastCommitDate,
forPublishing: !!options.forPublishing,
publishOnly: !!options.publishOnly,
};
// Note: should we always ignore the passed-in output filename for .model_info?

View file

@ -51,15 +51,17 @@ class ProjectBuilder {
}
}
// Go through the various file types and build them
for(const builder of buildActivities) {
if(builder.sourceExtension == KeymanFileTypes.Source.Project) {
// We don't support nested projects
continue;
}
if(!this.options.publishOnly) {
// Go through the various file types and build them
for(const builder of buildActivities) {
if(builder.sourceExtension == KeymanFileTypes.Source.Project) {
// We don't support nested projects
continue;
}
if(!await this.buildProjectTargets(builder)) {
return false;
if(!await this.buildProjectTargets(builder)) {
return false;
}
}
}
@ -113,7 +115,11 @@ class ProjectBuilder {
const buildFilename = path.relative(process.cwd(), infile).replace(/\\/g, '/');
const callbacks = new CompilerFileCallbacks(buildFilename, options, this.callbacks);
callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename: infile, relativeFilename:buildFilename}));
if(activity.sourceExtension == KeymanFileTypes.Source.Project) {
callbacks.reportMessage(InfrastructureMessages.Info_ValidatingProject({filename: infile, relativeFilename:buildFilename}));
} else {
callbacks.reportMessage(InfrastructureMessages.Info_BuildingFile({filename: infile, relativeFilename:buildFilename}));
}
fs.mkdirSync(path.dirname(outfile), {recursive:true});
@ -123,12 +129,19 @@ class ProjectBuilder {
// note: command line option here, if set, overrides project setting
result = result && !callbacks.hasFailureMessage(this.options.compilerWarningsAsErrors ?? this.project.options.compilerWarningsAsErrors);
if(result) {
callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename: infile, relativeFilename:buildFilename}));
if(activity.sourceExtension == KeymanFileTypes.Source.Project) {
if(result) {
callbacks.reportMessage(InfrastructureMessages.Info_ProjectValidatedSuccessfully({filename: infile, relativeFilename:buildFilename}));
} else {
callbacks.reportMessage(InfrastructureMessages.Info_ProjectNotValidatedSuccessfully({filename: infile, relativeFilename:buildFilename}));
}
} else {
callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename: infile, relativeFilename: buildFilename}));
if(result) {
callbacks.reportMessage(InfrastructureMessages.Info_FileBuiltSuccessfully({filename: infile, relativeFilename:buildFilename}));
} else {
callbacks.reportMessage(InfrastructureMessages.Info_FileNotBuiltSuccessfully({filename: infile, relativeFilename: buildFilename}));
}
}
return result;
}

View file

@ -197,5 +197,26 @@ export class InfrastructureMessages {
`Failed to generate new project '${def(o.id)}'.`,
)});
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
static INFO_ValidatingProject = SevInfo | 0x0029;
static Info_ValidatingProject = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(
this.INFO_ValidatingProject,
`Validating ${def(o.relativeFilename)}`,
)});
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
static INFO_ProjectValidatedSuccessfully = SevInfo | 0x002A;
static Info_ProjectValidatedSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(
this.INFO_ProjectValidatedSuccessfully,
`${def(o.relativeFilename)} validated successfully.`,
)});
// For this message, we override the filename with the passed-in file. A bit of a hack but does the job
static INFO_ProjectNotValidatedSuccessfully = SevInfo | 0x002B;
static Info_ProjectNotValidatedSuccessfully = (o:{filename:string,relativeFilename:string}) => ({filename:o.filename, ...m(
this.INFO_ProjectNotValidatedSuccessfully,
`${def(o.relativeFilename)} failed to validate.`
)});
}

View file

@ -242,22 +242,26 @@ export class NodeCompilerCallbacks implements CompilerCallbacks {
* We treat a few certain infrastructure messages with special colours
* @param event
* @returns
* Keep in sync with: UfrmMessages.pas, TfrmMessages.Add
*/
messageSpecialColor(event: CompilerEvent) {
switch(event.code) {
case InfrastructureMessages.INFO_BuildingFile:
case InfrastructureMessages.INFO_CopyingProject:
case InfrastructureMessages.INFO_GeneratingProject:
case InfrastructureMessages.INFO_ValidatingProject:
return color.whiteBright;
case InfrastructureMessages.INFO_FileNotBuiltSuccessfully:
case InfrastructureMessages.INFO_ProjectNotBuiltSuccessfully:
case InfrastructureMessages.INFO_ProjectNotCopiedSuccessfully:
case InfrastructureMessages.INFO_ProjectNotGeneratedSuccessfully:
case InfrastructureMessages.INFO_ProjectNotValidatedSuccessfully:
return color.red;
case InfrastructureMessages.INFO_FileBuiltSuccessfully:
case InfrastructureMessages.INFO_ProjectBuiltSuccessfully:
case InfrastructureMessages.INFO_ProjectCopiedSuccessfully:
case InfrastructureMessages.INFO_ProjectGeneratedSuccessfully:
case InfrastructureMessages.INFO_ProjectValidatedSuccessfully:
return color.green;
}
return null;

View file

@ -9,6 +9,12 @@ export interface ExtendedCompilerOptions extends CompilerOptions {
* MIT
*/
forPublishing?: boolean;
/**
* Do not build components, just do the publish phase for the project. This is
* used mostly by Keyman Developer IDE, which will run the .keyboard_info /
* .package_info validation and compilation step if skipMetadataFiles is true.
*/
publishOnly?: boolean;
/**
* Overrides for message reporting
*/
@ -198,6 +204,7 @@ export function commanderOptionsToCompilerOptions(options: any, callbacks: Compi
warnDeprecatedCode: options.warnDeprecatedCode,
// ExtendedOptions
forPublishing: options.forPublishing,
publishOnly: options.publishOnly,
messageOverrides: overrides,
}
}

View file

@ -13,7 +13,7 @@
"@ngrok/ngrok": "^1.7.0",
"@sentry/node": "^7.57.0",
"chalk": "^4.1.2",
"express": "^4.22.1",
"express": "^4.22.2",
"multer": "^2.1.1",
"open": "^8.4.0",
"restructure": "^3.0.1",

View file

@ -17,6 +17,7 @@ type
function Run(const parameters: TArray<string>; const path, logFilename: string; Callback: TUtilExecuteCallbackEvent): Boolean;
public
function Compile(ProjectFile: TProjectFile; const infile, outfile: string; debug: Boolean; Callback: TUtilExecuteCallbackEvent = nil): Boolean;
function CompileForPublishing(ProjectFile: TProjectFile; const projectFilename: string; debug: Boolean; Callback: TUtilExecuteCallbackEvent = nil): Boolean;
function Copy(const source, dest, cwd: string; relocateExternal: Boolean; Callback: TUtilExecuteCallbackEvent = nil): Boolean;
end;
@ -39,6 +40,39 @@ uses
{ TKmcWrapper }
function TKmcWrapper.CompileForPublishing(
ProjectFile: TProjectFile;
const projectFilename: string;
debug: Boolean;
Callback: TUtilExecuteCallbackEvent
): Boolean;
var
cmdline: TArray<string>;
begin
cmdline := [
'build',
'--log-format', 'tsv',
'--log-level', 'info',
'--for-publishing',
'--publish-only',
projectFilename
];
if Assigned(FGlobalProject) and (FGlobalProject.Options.CompilerWarningsAsErrors) then
cmdline := cmdline + ['--compiler-warnings-as-errors']
else
cmdline := cmdline + ['--no-compiler-warnings-as-errors'];
if Assigned(FGlobalProject) and (not FGlobalProject.Options.WarnDeprecatedCode) then
cmdline := cmdline + ['--no-warn-deprecated-code'];
if debug then
cmdline := cmdline + ['--debug'];
Result := Run(cmdline, ExtractFileDir(projectFilename), projectFilename, Callback);
end;
function TKmcWrapper.Compile(
ProjectFile: TProjectFile;
const infile: string;

View file

@ -239,7 +239,7 @@ type
km_core_keyboard_attrs = record
version_string: pkm_core_cu;
id: pkm_core_cu;
default_optons: pkm_core_option_item
default_options: pkm_core_option_item
end;
pkm_core_keyboard_attrs = ^km_core_keyboard_attrs;

View file

@ -186,7 +186,7 @@ initialization
Assert(sizeof(km_core_context_item) = 8);
Assert(sizeof(km_core_action_item) = 12);
Assert(sizeof(km_core_option_item) = 12);
Assert(sizeof(km_core_keyboard_attrs) = 16);
Assert(sizeof(km_core_keyboard_attrs) = 12);
Assert(sizeof(km_core_attr) = 16);
//keyman_core_api_debug.h:

View file

@ -170,6 +170,12 @@ const // ABGR - text on white bg, Colors similar to Light+ VSCode color theme
INFO_FileNotBuiltSuccessfully = NAMESPACE_Infrastructure or $0007;
INFO_ProjectBuiltSuccessfully = NAMESPACE_Infrastructure or $000B;
INFO_ProjectNotBuiltSuccessfully = NAMESPACE_Infrastructure or $000C;
INFO_ProjectCopiedSuccessfully = NAMESPACE_Infrastructure or $0024;
INFO_ProjectNotCopiedSuccessfully = NAMESPACE_Infrastructure or $0025;
INFO_ProjectGeneratedSuccessfully = NAMESPACE_Infrastructure or $0027;
INFO_ProjectNotGeneratedSuccessfully = NAMESPACE_Infrastructure or $0028;
INFO_ProjectValidatedSuccessfully = NAMESPACE_Infrastructure or $002A;
INFO_ProjectNotValidatedSuccessfully = NAMESPACE_Infrastructure or $002B;
Segment_Filename = 0;
Segment_Filename_Separator = 1;
@ -254,12 +260,18 @@ begin
FColor := clBlack;
FTextColor := Color_Text;
// Override formatting for 4 known messages
// Override formatting for known messages -- keep in sync with messageSpecialColor() in NodeCompilerCallbacks.ts
if (MsgCode = INFO_FileBuiltSuccessfully) or
(MsgCode = INFO_ProjectBuiltSuccessfully) then
(MsgCode = INFO_ProjectBuiltSuccessfully) or
(MsgCode = INFO_ProjectCopiedSuccessfully) or
(MsgCode = INFO_ProjectGeneratedSuccessfully) or
(MsgCode = INFO_ProjectValidatedSuccessfully) then
state := plsSuccess
else if (MsgCode = INFO_FileNotBuiltSuccessfully) or
(MsgCode = INFO_ProjectNotBuiltSuccessfully) then
(MsgCode = INFO_ProjectNotBuiltSuccessfully) or
(MsgCode = INFO_ProjectNotCopiedSuccessfully) or
(MsgCode = INFO_ProjectNotGeneratedSuccessfully) or
(MsgCode = INFO_ProjectNotValidatedSuccessfully) then
state := plsFailure;
case state of

View file

@ -29,6 +29,8 @@ begin
w := TKmcWrapper.Create;
try
Result := w.Compile(Self, FileName, TargetFilename, False);
if not OwnerProject.Options.SkipMetadataFiles then
Result := Result and w.CompileForPublishing(Self, OwnerProject.FileName, False);
// TODO(lowpri): FDebug flag
finally
w.Free;

View file

@ -419,11 +419,6 @@
<p>Choose a help file to include in the keyboard</p>
</Control>
<Control Name="cmdCompileKeymanWeb" Title="Compile to Web">
<p>Compiles the keyboard for KeymanWeb. Two output files will be generated: [filename].js and [filename]_load.js. The KeymanWeb Tutorial
contains more information about the difference between the two output files.</p>
</Control>
<Control Name="cmdTestKeymanWeb" Title="Test KeymanWeb Keyboard">
<p>Tests your KeymanWeb keyboard in an Internet Explorer embedded window</p>
</Control>

147
package-lock.json generated
View file

@ -1232,7 +1232,7 @@
"@ngrok/ngrok": "^1.7.0",
"@sentry/node": "^7.57.0",
"chalk": "^4.1.2",
"express": "^4.22.1",
"express": "^4.22.2",
"multer": "^2.1.1",
"open": "^8.4.0",
"restructure": "^3.0.1",
@ -5301,22 +5301,23 @@
"license": "Apache-2.0"
},
"node_modules/body-parser": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"version": "1.20.5",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
"integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"bytes": "~3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.13.0",
"raw-body": "2.5.2",
"destroy": "~1.2.0",
"http-errors": "~2.0.1",
"iconv-lite": "~0.4.24",
"on-finished": "~2.4.1",
"qs": "~6.15.1",
"raw-body": "~2.5.3",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8",
@ -5327,6 +5328,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
@ -5335,14 +5337,60 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/body-parser/node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": {
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/body-parser/node_modules/raw-body": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
"integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
"license": "MIT",
"dependencies": {
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.4.24",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/body-parser/node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/brace-expansion": {
"version": "1.1.12",
@ -5600,6 +5648,7 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dev": true,
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
@ -6372,6 +6421,7 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
@ -7832,14 +7882,14 @@
"dev": true
},
"node_modules/express": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
"integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "~1.20.3",
"body-parser": "~1.20.5",
"content-disposition": "~0.5.4",
"content-type": "~1.0.4",
"cookie": "~0.7.1",
@ -7858,7 +7908,7 @@
"parseurl": "~1.3.3",
"path-to-regexp": "~0.1.12",
"proxy-addr": "~2.0.7",
"qs": "~6.14.0",
"qs": "~6.15.1",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "~0.19.0",
@ -7908,40 +7958,6 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/express/node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/express/node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3",
"side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/express/node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@ -8666,6 +8682,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"dependencies": {
"es-define-property": "^1.0.0"
},
@ -11784,12 +11801,12 @@
"license": "MIT"
},
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"version": "6.15.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
"integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.6"
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
@ -11844,6 +11861,7 @@
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dev": true,
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@ -12272,6 +12290,7 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dev": true,
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
@ -12339,14 +12358,16 @@
}
},
"node_modules/side-channel": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
"object-inspect": "^1.13.3",
"side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
@ -14256,7 +14277,7 @@
"@sentry/cli": "^2.31.0",
"@zip.js/zip.js": "^2.7.32",
"c8": "^7.12.0",
"express": "^4.19.2",
"express": "^4.22.2",
"jsdom": "^23.0.1",
"promise-status-async": "^1.2.10",
"tsx": "^4.19.0"

View file

@ -8,8 +8,8 @@ There are multiple ways to add and install keyboards into your KeymanWeb install
The most efficient way to utilize a keyboard is to obtain a local copy of it and place this copy in a static location on your website. Once this is done, it can be directly linked into KeymanWeb as follows.
```c
keyman.addKeyboards({
```typescript
await keyman.addKeyboards({
id:'us', // The keyboard's unique identification code.
name:'English', // The keyboard's user-readable name.
language:{
@ -22,7 +22,7 @@ keyman.addKeyboards({
Custom fonts may also be utilized via the `language.font` property. For example:
```c
```typescript
font:{
family:'LaoWeb',
source:['../font/saysettha_web.ttf','../font/saysettha_web.woff','../font/saysettha_web.eot']
@ -33,16 +33,16 @@ font:{
To obtain the default Keyman keyboard for a given language, call the following function.
```c
keyman.addKeyboardsForLanguage('Dzongkha');
```typescript
await keyman.addKeyboardsForLanguage('Dzongkha');
```
This example would find the default keyboard for the Dzongkha language. This method will fail if the name doesn't perfectly match any language found in the CDN's repository.
Alternatively, languages may be looked up via their BCP 47 language code as follows:
```c
keyman.addKeyboards('@he')
```typescript
await keyman.addKeyboards('@he');
```
The `@` prefix indicates the use of the BCP 47 language code, which in this case corresponds with Hebrew.
@ -51,8 +51,8 @@ The `@` prefix indicates the use of the BCP 47 language code, which in this case
To obtain a specific keyboard by name or by keyboard name and language code as a pair, see the following:
```c
keyman.addKeyboards('french','sil_euro_latin@sv','sil_euro_latin@no')
```typescript
await keyman.addKeyboards('french', 'sil_euro_latin@sv', 'sil_euro_latin@no');
```
This will install three keyboards - one for French (named, quite simply, "French") and two copies of the EuroLatin keyboard - one for Swedish and one for Norwegian.

View file

@ -1,20 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<script src='https://s.keyman.com/kmw/engine/17.0.331/keymanweb.js'></script>
<script>var kmw = keyman;</script>
<script>
window.addEventListener('load', function () {
keyman.init().then(function() {
keyman.addKeyboards({
id:'laokeys',
name:'Lao (Phonetic)',
languages:{
id:'lo',
name:'Lao'
},
filename:'./js/laokeys.js'
});
keyman.init().then(async function() {
await keyman.addKeyboards({
id:'laokeys',
name:'Lao (Phonetic)',
languages:{
id:'lo',
name:'Lao'
},
filename:'./js/laokeys.js'
});
});
</script>
@ -28,4 +25,4 @@
<a href="automatic-control">Back to Document</a>
</body>
</html>
</html>

View file

@ -1,35 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<script src='https://s.keyman.com/kmw/engine/17.0.331/keymanweb.js'></script>
<script>var kmw = keyman;</script>
<script src='https://s.keyman.com/kmw/engine/17.0.331/kmwuitoggle.js'></script>
<script type='text/javascript' src='js/unified_loader.js'></script>
<script type="text/javascript">
function SetupDocument() {
keyman.init({
root: './', // Note - if drawing the latest version of KeymanWeb from the CDN, this will
// default to the source folder on our servers.
ui: 'toggle',
resources: './'
}).then(function() {
// Load the keyboards of your choice here.
loadKeyboards();
keyman.init({
root: './', // Note - if drawing the latest version of KeymanWeb from the CDN, this will
// default to the source folder on our servers.
ui: 'toggle',
resources: './'
}).then(async function() {
// Load the keyboards of your choice here.
await loadKeyboards();
/* Disable KeymanWeb interaction on the 'Email to' TEXT control */
keyman.disableControl(document.f.address);
/* Set the default keyboard for the 'Subject' TEXT control to 'off' (i.e. default browser keyboard) */
/* As KeymanWeb relies on the on-screen keyboard to change languages for touch-based devices, this will
* not work for them and will default to the first language added to KeymanWeb after initialization. */
keyman.setKeyboardForControl(document.f.subject, '', '');
/* Set the default keyboard for the 'Message body' TEXTAREA to the LaoKeys keyboard */
keyman.setKeyboardForControl(document.f.text, 'Keyboard_laokeys', 'lo');
});
}
window.addEventListener( 'load' , SetupDocument );
// Disable KeymanWeb interaction on the 'Email to' TEXT control
keyman.disableControl(document.f.address);
// Set the default keyboard for the 'Subject' TEXT control to 'off' (i.e.
// default browser keyboard). As KeymanWeb relies on the on-screen keyboard
// to change languages for touch-based devices, this will not work for them
// and will default to the first language added to KeymanWeb after
// initialization.
keyman.setKeyboardForControl(document.f.subject, '', '');
// Set the default keyboard for the 'Message body' TEXTAREA to the LaoKeys keyboard
keyman.setKeyboardForControl(document.f.text, 'Keyboard_laokeys', 'lo');
});
</script>
</head>
<body>
@ -38,7 +36,7 @@
<p>Email to <span class='note'>(KeymanWeb not enabled)</span><br /><input type='text' name='address' size="40" placeholder="id = address"/></p>
<p>Subject <span class='note'>(Defaults to 'English' or 'off' unless on a touch-based device)</span><br /><input type='text' name='subject' size="40" placeholder="id = subject"/></p>
<p>Subject <span class='note'>(Defaults to '(System keyboard)' or 'off' unless on a touch-based device)</span><br /><input type='text' name='subject' size="40" placeholder="id = subject"/></p>
<p>Message body <span class='note'>(Defaults to 'LaoKeys')</span><br /><textarea name='text' cols='40' rows='10' placeholder="id = text"></textarea></p>
@ -46,4 +44,4 @@
<a href="control-by-control">Back to Document</a>
</body>
</html>
</html>

View file

@ -6,15 +6,14 @@
<!-- Start of Code -->
<script src='https://s.keyman.com/kmw/engine/17.0.331/keymanweb.js'></script>
<script>var kmw = keyman;</script>
<script src='https://s.keyman.com/kmw/engine/17.0.331/kmwuitoggle.js'></script>
<script>
(function() {
keyman.init({attachType:'auto'}).then(function() {
keyman.addKeyboards('@en'); // Loads default English keyboard from Keyman Cloud (CDN)
keyman.addKeyboards('@th'); // Loads default Thai keyboard from Keyman Cloud (CDN)
});
})(keyman);
keyman.init({
attachType:'auto'
}).then(async function() {
await keyman.addKeyboards('@en'); // Loads default English keyboard from Keyman Cloud (CDN)
await keyman.addKeyboards('@th'); // Loads default Thai keyboard from Keyman Cloud (CDN)
});
</script>
</head>
<body>
@ -23,4 +22,4 @@
<a href="index">Back to Document</a>
</body>
</html>
</html>

View file

@ -1,45 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<script src='https://s.keyman.com/kmw/engine/17.0.331/keymanweb.js'></script>
<script>var kmw = keyman;</script>
<script type='text/javascript' src='js/unified_loader.js'></script>
<script type="text/javascript">
var KWControl = null;
function SetupDocument()
{
keyman.init().then(function(){
// Load the keyboards of your choice here.
loadKeyboards();
keyman.init().then(async function() {
// Load the keyboards of your choice here.
await loadKeyboards();
KWControl = document.getElementById('KWControl');
var kbds = keyman.getKeyboards();
for(var kbd in kbds)
{
var opt = document.createElement('OPTION');
opt.value = kbds[kbd].InternalName + "$$" + kbds[kbd].LanguageCode;
opt.innerHTML = kbds[kbd].Name;
KWControl.appendChild(opt);
}
document.f.multilingual.focus();
KWControl = document.getElementById('KWControl');
var kbds = keyman.getKeyboards();
for(var kbd in kbds)
{
var opt = document.createElement('OPTION');
opt.value = kbds[kbd].InternalName + "$$" + kbds[kbd].LanguageCode;
opt.innerHTML = kbds[kbd].Name;
KWControl.appendChild(opt);
}
document.f.multilingual.focus();
keyman.setActiveKeyboard('', '');
});
}
keyman.setActiveKeyboard('', '');
});
function KWControlChange()
{
function KWControlChange() {
var name = KWControl.value.substr(0, KWControl.value.indexOf("$$"));
var languageCode = KWControl.value.substr(KWControl.value.indexOf("$$") + 2);
keyman.setActiveKeyboard(name, languageCode);
document.f.multilingual.focus();
}
window.addEventListener( 'load' , SetupDocument );
</script>
</head>
<body>

View file

@ -1,29 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<script src='https://s.keyman.com/kmw/engine/17.0.331/keymanweb.js'></script>
<script>var kmw = keyman;</script>
<script type='text/javascript' src='js/laokeys_load.js'></script>
<script type="text/javascript">
function SetupDocument()
{
keyman.init().then(function() {
keyman.setActiveKeyboard('laokeys');
keyman.osk.hide();
keyman.init().then(async function() {
await keyman.addKeyboards({
id: "laokeys",
name: "Lao (Phonetic)",
languages: { id: 'lo', name: 'Lao' },
filename: "./js/laokeys.js"
});
}
keyman.setActiveKeyboard('laokeys');
keyman.osk.hide();
});
function KWControlClick()
{
function KWControlClick() {
if(keyman.osk.isEnabled()) {
keyman.osk.hide();
} else {
keyman.osk.show(true); // Specifies that the OSK should display whenever a valid control has focus, re-enabling the default behavior.
}
}
window.addEventListener('load', SetupDocument);
</script>
</head>
<body>
@ -38,4 +36,4 @@
<a href="manual-control">Back to Document</a>
</form>
</body>
</html>
</html>

View file

@ -2,28 +2,28 @@
title: Automatic Mode Example
---
This page shows how to include a local keyboard from an arbitrary location in your website's file structure.
This page shows how to include a local keyboard from an arbitrary location
in your website's file structure.
In this example, we use only the LaoKey keyboard. Please click [this link](./__auto-control.html) to open the test page.
In this example, we use only the LaoKey keyboard. Please click
[this link](./__auto-control.html) to open the test page.
## Code Walkthrough
```html
<head>
<!-- Start of Code -->
<script src="js/keymanweb.js" type="text/javascript"></script>
<script src="https://s.keyman.com/kmw/engine/17.0.331/keymanweb.js" type="text/javascript"></script>
<script>
window.addEventListener('load', function () {
keyman.init().then(function() {
keyman.addKeyboards({
id:'laokeys',
name:'Lao (Phonetic)',
languages:{
id:'lo',
name:'Lao'
},
filename:'./js/laokeys.js'
});
keyman.init().then(async function() {
await keyman.addKeyboards({
id:'laokeys',
name:'Lao (Phonetic)',
languages:{
id:'lo',
name:'Lao'
},
filename:'./js/laokeys.js'
});
});
</script>
@ -31,8 +31,6 @@ In this example, we use only the LaoKey keyboard. Please click [this link](./__a
</head>
```
As you can see above, the second line in the code snippet above references the LaoKey keyboard loader JavaScript file. This is a small stub file, typically less than 200 bytes, that defines the name and actual location of the real keyboard file (in this case, **laokeys.js**). When a page may reference many keyboards, this saves downloading potentially hundreds of kilobytes of unused Javascript keyboards - the keyboard is downloaded when it is first selected by the user.
## API References
On initialization: [`keyman.init()`](../../reference/core/init).

View file

@ -2,7 +2,10 @@
title: Control-by-Control Example
---
In this example, a simulated webmail form, the default and permissible keyboard for each control is managed by the web page. We use the automatic mode for simplicity of demonstration. We also link to the CDN for KeymanWeb in this example. Please click [this link](__control-by-control.html) to open the test page.
In this example, a simulated webmail form, the default and permissible keyboard for each control
is managed by the web page. We use the automatic mode for simplicity of demonstration. We also
link to the CDN for KeymanWeb in this example. Please click [this link](__control-by-control.html)
to open the test page.
## Code Walkthrough
@ -10,28 +13,23 @@ Include the following script in the HEAD of your page:
```js
<script type="text/javascript">
function SetupDocument() {
keyman.init({
root: './', // Note - if drawing the latest version of KeymanWeb from the CDN, this will
// default to the source folder on our servers.
ui: 'toggle',
resources: './'
}).then(function() {
// Load the keyboards of your choice here.
loadKeyboards();
keyman.init({
root: './', // Note - if drawing the latest version of KeymanWeb from the CDN, this will
// default to the source folder on our servers.
ui: 'toggle',
resources: './'
}).then(async function() {
// Load the keyboards of your choice here.
await loadKeyboards();
/* Disable KeymanWeb interaction on the 'Email to' TEXT control */
keyman.disableControl(document.f.address);
/* Set the default keyboard for the 'Subject' TEXT control to 'off' (i.e. default browser keyboard) */
/* As KeymanWeb relies on the on-screen keyboard to change languages for touch-based devices, this will
* not work for them and will default to the first language added to KeymanWeb after initialization. */
keyman.setKeyboardForControl(document.f.subject, '', '');
/* Set the default keyboard for the 'Message body' TEXTAREA to the LaoKeys keyboard */
keyman.setKeyboardForControl(document.f.text, 'Keyboard_laokeys', 'lo');
});
}
window.addEventListener( 'load' , SetupDocument );
// Disable KeymanWeb interaction on the 'Email to' TEXT control
keyman.disableControl(document.f.address);
// Set the default keyboard for the 'Subject' TEXT control to 'off' (i.e. default
// browser keyboard)
keyman.setKeyboardForControl(document.f.subject, '', '');
// Set the default keyboard for the 'Message body' TEXTAREA to the LaoKeys keyboard
keyman.setKeyboardForControl(document.f.text, 'Keyboard_laokeys', 'lo');
});
</script>
```
@ -40,18 +38,18 @@ Also include the following HTML code:
```html
<head>
<!-- Load the KeymanWeb engine -->
<script src="js/keymanweb.js" type="text/javascript"></script>
<script src="js/kmwuitoggle.js" type="text/javascript"></script>
<script src="https://s.keyman.com/kmw/engine/17.0.331/keymanweb.js" type="text/javascript"></script>
<script src="https://s.keyman.com/kmw/engine/17.0.331/kmwuitoggle.js" type="text/javascript"></script>
<!-- // Also be sure to load your keyboards. Note that stubs will not work with this
// example without detailing paths precisely in the init() call's parameter.-->
</head>
<!-- When the page has finished loading, advise KeymanWeb of control settings, see above -->
<body onload="SetupDocument()">
```
---
**Note:** In this example we disabled the first element (`document.f.address`) by API call. A later API call can re-enable KeymanWeb for this control should it fit the page's design. Alternatively, this can be done by instead adding the class `'kmw-disabled'` to the control. This will permanently block KeymanWeb from handling its input.
**Note:** In this example we disabled the first element (`document.f.address`) by API call. A
later API call can re-enable KeymanWeb for this control should it fit the page's design.
Alternatively, this can be done by instead adding the class `'kmw-disabled'` to the control. This
will permanently block KeymanWeb from handling its input.
---

View file

@ -2,7 +2,9 @@
title: Manual Control - Custom Interface
---
In this example, the web page designer has opted for their own user interface instead of the KeymanWeb interface. The keyboards in the selector are populated from the KeymanWeb list of keyboards. Please click [this link](__full-manual-control.html) to open the test page.
In this example, the web page designer has opted for their own user interface instead of the
KeymanWeb interface. The keyboards in the selector are populated from the KeymanWeb list of
keyboards. Please click [this link](__full-manual-control.html) to open the test page.
## Code Walkthrough
@ -12,31 +14,25 @@ Include the following script in the HEAD of your page:
<script>
var KWControl = null;
/* SetupDocument: Called when the page finishes loading */
function SetupDocument()
{
keyman.init().then(function(){
// Load the keyboards of your choice here.
loadKeyboards();
keyman.init().then(async function() {
// Load the keyboards of your choice here.
await loadKeyboards();
KWControl = document.getElementById('KWControl');
var kbds = keyman.getKeyboards();
for(var kbd in kbds)
{
var opt = document.createElement('OPTION');
opt.value = kbds[kbd].InternalName + "$$" + kbds[kbd].LanguageCode;
opt.innerHTML = kbds[kbd].Name;
KWControl.appendChild(opt);
}
document.f.multilingual.focus();
KWControl = document.getElementById('KWControl');
var kbds = keyman.getKeyboards();
for(var kbd in kbds) {
var opt = document.createElement('OPTION');
opt.value = kbds[kbd].InternalName + "$$" + kbds[kbd].LanguageCode;
opt.innerHTML = kbds[kbd].Name;
KWControl.appendChild(opt);
}
document.f.multilingual.focus();
keyman.setActiveKeyboard('', '');
});
}
keyman.setActiveKeyboard('', '');
});
/* KWControlChange: Called when user selects an item in the KWControl SELECT */
function KWControlChange()
{
function KWControlChange() {
/* Select the keyboard in KeymanWeb */
var name = KWControl.value.substr(0, KWControl.value.indexOf("$$"));
var languageCode = KWControl.value.substr(KWControl.value.indexOf("$$"+2));
@ -52,14 +48,11 @@ Also include the following HTML code:
```html
<head>
<!-- Load the KeymanWeb engine -->
<script src="keymanweb.js" type="text/javascript"></script>
<script src="https://s.keyman.com/kmw/engine/17.0.331/keymanweb.js" type="text/javascript"></script>
<!-- Load the your keyboard stubs here -->
<script src="unified_loader.js" type="text/javascript"></script>
<!-- ... -->
</head>
<!-- When the page has finished loading, populate the keyboard selector, see above -->
<body onload="SetupDocument()">
```
- File: [unified_loader.js](js/unified_loader.js)

View file

@ -1,2 +0,0 @@
/*(C) Copyright 1994-2006 Tavultesoft Pty Ltd. All Rights Reserved. Details: keymanweb.com*/
KeymanWeb.KRS(new Stub_Keyboard_devanagari_inscript()); function Stub_Keyboard_devanagari_inscript() {this.KF="devanagari_inscript.js";this.KI="Keyboard_devanagari_inscript";this.KN="Devanagari (INSCRIPT)";}

View file

@ -1 +0,0 @@
KeymanWeb.KRS(new Stub_Keyboard_european2()); function Stub_Keyboard_european2() {this.KF="european2-1.6.js";this.KI="Keyboard_european2";this.KN="EuroLatin2 Keyboard";}

View file

@ -1,2 +0,0 @@
/*(C) Copyright 1994-2006 Tavultesoft Pty Ltd. All Rights Reserved. Details: keymanweb.com*/
KeymanWeb.KRS(new Stub_Keyboard_hebrew()); function Stub_Keyboard_hebrew() {this.KF="hebrew.js";this.KI="Keyboard_hebrew";this.KN="Hebrew";}

View file

@ -1,2 +0,0 @@
/*(C) Copyright 1994-2006 Tavultesoft Pty Ltd. All Rights Reserved. Details: keymanweb.com*/
KeymanWeb.KRS(new Stub_Keyboard_keymanwebtest()); function Stub_Keyboard_keymanwebtest() {this.KF="keymanwebtest.js";this.KI="Keyboard_keymanwebtest";this.KN="KeymanWeb Test";}

View file

@ -1,2 +0,0 @@
/*(C) Copyright 1994-2006 Tavultesoft Pty Ltd. All Rights Reserved. Details: keymanweb.com*/
KeymanWeb.KRS(new Stub_Keyboard_korean_korda()); function Stub_Keyboard_korean_korda() {this.KF="korean_korda.js";this.KI="Keyboard_korean_korda";this.KN="Korean (KORDA) - 30 Day Evaluation";}

View file

@ -1,2 +0,0 @@
/*(C) Copyright 1994-2006 Tavultesoft Pty Ltd. All Rights Reserved. Details: keymanweb.com*/
KeymanWeb.KRS(new Stub_Keyboard_korean_morse()); function Stub_Keyboard_korean_morse() {this.KF="korean_morse.js";this.KI="Keyboard_korean_morse";this.KN="Korean (Morse) - 30 Day Evaluation";}

View file

@ -1,7 +0,0 @@
/*(C) Copyright 1994-2021 SIL International. All Rights Reserved. Details: keymanweb.com*/
keyman.addKeyboards({
id: "laokeys",
name: "Lao (Phonetic)",
languages:{id:'lo',name:'Lao'},
filename: "./js/laokeys.js"
});

View file

@ -1,16 +1,18 @@
/*(C) Copyright 2021 SIL International. All Rights Reserved. Details: keymanweb.com*/
function loadKeyboards() {
// Uses an older type of keyboard stub definition.
var KWK={
"devanagari_inscript":{KN:"Devanagari (INSCRIPT)", KLC:"hi", KL:"Hindi"},
"european2":{KN:"EuroLatin2", KLC:"en", KL:"English"},
"hebrew":{KN:"Hebrew", KLC:"he", KL:"Hebrew"},
"korean_korda":{KN:"Korean (KORDA) - 30 Day Evaluation", KLC:"ko", KL:"Korean"},
"korean_morse":{KN:"Korean (Morse) - 30 Day Evaluation", KLC:"ko", KL:"Korean"},
"laokeys":{KN:"Lao (Phonetic)", KLC:"lo", KL:"Lao"}
};
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
async function loadKeyboards() {
const keyboardList = [
{ id: 'devanagari_inscript', name: 'Devanagari (INSCRIPT)', languages: { id: 'hi', name: 'Hindi' }, filename: './js/devanagari_inscript.js' },
{ id: 'european2', name: 'EuroLatin2', languages: { id: 'en', name: 'English' }, filename: './js/european2.js' },
{ id: 'hebrew', name: 'Hebrew', languages: { id: 'he', name: 'Hebrew' }, filename: './js/hebrew.js' },
{ id: 'korean_korda', name: 'Korean (KORDA)', languages: { id: 'ko', name: 'Korean' }, filename: './js/korean_korda.js' },
{ id: 'korean_morse', name: 'Korean (Morse)', languages: { id: 'ko', name: 'Korean' }, filename: './js/korean_morse.js' },
// using a KeyboardStub would allow to customize e.g. which font the keyboard uses
{ KF: "./js/laokeys.js", KI: "Keyboard_laokeys", KN: "Lao (Phonetic)", KL: "Lao", KLC: "lo" }
];
for(var n in KWK) {
KeymanWeb.registerStub({KF:"./js/" + n+".js", KI:"Keyboard_"+n, KN:KWK[n].KN, KL:KWK[n].KL, KLC:KWK[n].KLC});
}
return keyman.addKeyboards(keyboardList).catch(function(err) {
console.error('keyman.addKeyboards failed with '+errToString(err)+' for '+JSON.stringify(keyboardList));
});
}

View file

@ -10,21 +10,19 @@ Include the following script in the HEAD of your page:
```js
<script>
/* SetupDocument: Called when the page finishes loading */
function SetupDocument()
{
/* Make sure that Keyman is initialized (we can't guarantee initialization order) */
keyman.init().then(function () {
/* Prevents automatic display of the onscreen keyboard. (default automatic) */
keyman.osk.hide();
/* Select the LaoKeys keyboard */
keyman.init().then(async function() {
await keyman.addKeyboards({
id: "laokeys",
name: "Lao (Phonetic)",
languages:{id:'lo',name:'Lao'},
filename: "./js/laokeys.js"
});
keyman.setActiveKeyboard('laokeys');
keyman.osk.hide();
});
}
/* KWControlClick: Called when user clicks on the KWControl IMG */
function KWControlClick()
{
function KWControlClick() {
if(keyman.osk.isEnabled()) {
keyman.osk.hide();
} else {
@ -40,17 +38,10 @@ Also include the following HTML code:
```html
<head>
<!-- Load the KeymanWeb engine -->
<script src="keymanweb.js" type="text/javascript"></script>
<!-- Load the LaoKeys keyboard stub -->
<script src="laokeys_load.js" type="text/javascript"></script>
<script src="https://s.keyman.com/kmw/engine/17.0.331/keymanweb.js" type="text/javascript"></script>
</head>
<!-- When the page has finished loading, activate the LaoKeys keyboard, see above -->
<body onload="SetupDocument()">
```
- File: [laokeys_load.js](js/laokeys_load.js)
And finally, include the control img for KeymanWeb:
```html
@ -61,9 +52,11 @@ And finally, include the control img for KeymanWeb:
## API References
On programmatically setting the keyboard:
- [`keyman.setActiveKeyboard()`](../../reference/core/setActiveKeyboard).
On managing the visibility of the OSK:
- [`keyman.osk.hide()`](../../reference/osk/hide)
- [`keyman.osk.show()`](../../reference/osk/show).

View file

@ -22,12 +22,10 @@ The source code for the page may be seen below.
<script src='https://s.keyman.com/kmw/engine/18.0.245/keymanweb.js'></script>
<script src='https://s.keyman.com/kmw/engine/18.0.245/kmwuitoggle.js'></script>
<script>
(function() {
keyman.init({attachType:'auto'}).then(function() {
keyman.addKeyboards('@en'); // Loads default English keyboard from Keyman Cloud (CDN)
keyman.addKeyboards('@th'); // Loads default Thai keyboard from Keyman Cloud (CDN)
});
})(keyman);
keyman.init({attachType:'auto'}).then(async function() {
await keyman.addKeyboards('@en'); // Loads default English keyboard from Keyman Cloud (CDN)
await keyman.addKeyboards('@th'); // Loads default Thai keyboard from Keyman Cloud (CDN)
});
</script>
</head>
<body>

View file

@ -9,7 +9,7 @@ Adds keyboards to KeymanWeb.
## Syntax
```js
keyman.addKeyboards(spec[, spec...])
await keyman.addKeyboards([spec...])
```
### Parameters
@ -179,3 +179,7 @@ The `spec.languages.font` object contains the following members:
: `string` <span class="optional">optional</span>
Font size (in CSS dimensions). If not specified, then `1em` is used.
### Not passing any parameter
Not passing any parameter will obtain all Keyman keyboards from the CDN.

View file

@ -9,7 +9,7 @@ Add default or all keyboards for a given language to KeymanWeb.
## Syntax
```js
keyman.addKeyboardsForLanguage(spec[, spec...])
await keyman.addKeyboardsForLanguage(spec[, spec...])
```
### Parameters

View file

@ -8,7 +8,7 @@ Get keyboard meta data for the selected keyboard and language.
## Syntax
```c
```js
keyman.getKeyboard(keyboardName, languageCode)
```
@ -75,5 +75,5 @@ The `keyboard` object contains the following members:
: `string` *optional*
: The font packaged with the keyboard to properly display specialized OSK characters.
## See also
## See also
- [keyman.addKeyboards()](addKeyboards) and its documentation about keyboard specification objects.

View file

@ -8,7 +8,7 @@ Get details of currently installed keyboards.
## Syntax
```c
```js
keyman.getKeyboards()
```
@ -25,5 +25,5 @@ None.
See [keyman.getKeyboard()](getKeyboard) for detail on the returned keyboard specification objects.
## See also
## See also
- [keyman.addKeyboards()](addKeyboards)

View file

@ -8,13 +8,13 @@ Registers the keyboard stub or returns true if already registered.
## Syntax
```c
```js
keyman.interface.registerStub(Pstub);
```
or
```c
```js
KeymanWeb.KRS(Pstub); // Shorthand
```

View file

@ -2,4 +2,6 @@
title: Layout Designer
---
One of the main features of *KeymanWeb 17* is its ability to support distinct, user-customizable layouts for touch-screen keyboards on phones and tablets. *Keyman Developer 17* includes a layout designer to simplify the process of creating custom layouts for any keyboard.
One of the main features of *KeymanWeb* is its ability to support distinct, user-customizable
layouts for touch-screen keyboards on phones and tablets. *Keyman Developer* includes a layout
designer to simplify the process of creating custom layouts for any keyboard.

View file

@ -2,13 +2,19 @@
title: Layout Specifications
---
Touch-screen layouts for *KeymanWeb 17* are specified as JSON objects containing a member object for each specified device type. Currently supported device types are *tablet* and *phone*. Layouts for *desktop* computers may also be specified but desktop on-screen keyboard design is normally managed by the
standard *Keyman Developer* on-screen keyboard tool rather than the keyboard layout designer. If the same
layout is appropriate for both *tablet* and *phone* devices only one need be specified, and will be used for either type of device.
Touch-screen layouts for *KeymanWeb* are specified as JSON objects containing a member object for
each specified device type. Currently supported device types are *tablet* and *phone*. Layouts
for *desktop* computers may also be specified but desktop on-screen keyboard design is normally
managed by the standard *Keyman Developer* on-screen keyboard tool rather than the keyboard
layout designer. If the same layout is appropriate for both *tablet* and *phone* devices only one
need be specified, and will be used for either type of device.
File encoding for manually-created layout files may use either UTF-8 or 7-bit ANSI coding, but must not include a BOM. For easier editing and management without requiring special fonts, embedded Unicode characters with values above 127 may use the *\uXXXX* notation.
File encoding for manually-created layout files may use either UTF-8 or 7-bit ANSI coding, but
must not include a BOM. For easier editing and management without requiring special fonts,
embedded Unicode characters with values above 127 may use the *\uXXXX* notation.
For details of the JSON specification, see [The JSON Data Interchange Format(ECMA-404)](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
For details of the JSON specification, see [The JSON Data Interchange
Format(ECMA-404)](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
<table data-border="1">
<thead>
@ -249,7 +255,7 @@ Details of key member specifications are given below:
</tr>
</thead>
<tbody>
<tr>
<tr>
<td class="rowhead" style="text-align: left;">id</td>
<td style="text-align: left;">Each key id must start with <i>>K_</i> , for keys mapped to standard Keyman virtual key names, e.g. <i>K_HYPHEN</i> , or either <i>U_</i> or <i>T_</i> for user-defined names. Keys identified as <i>U_xxxx[_xxxx...]</i> specify one or more Unicode characters in hex format, e.g. <i>U_1363</i> for the Ethiopic Comma character, and will insert those characters if the key id is not matched by a rule. Other user-defined keys, such as <i>T_ZZZ</i> , will be ignored unless matched by a rule. The key id is required except for key styles 9 or 10 (blank or spacer keys).</td>
</tr>
@ -330,7 +336,11 @@ Details of key member specifications are given below:
</tbody>
</table>
For many keyboards, it is helpful to associate some keyboard layers with physical keyboard modifier states. This is reflected in the layer name, where a layer name of *shift* means that when a key in that layer is touched, the keystroke will be processed as if the keyboard Shift key is held.
For many keyboards, it is helpful to associate some keyboard layers with physical keyboard
modifier states. This is reflected in the layer name, where a layer name of *shift* means that
when a key in that layer is touched, the keystroke will be processed as if the keyboard Shift key
is held.
The special layer names of:
*leftalt* ,
@ -364,7 +374,9 @@ The following special key identifiers have been added to simplify layer selectio
| K_SHIFTED | 264 |
| K_ALTGR | 265 |
A special font is used to provide easily recognizable key graphics for various special purpose keys. The following key text strings will be recognized and cause the appropriate graphic to be used for the key cap instead of the actual text:
A special font is used to provide easily recognizable key graphics for various special purpose
keys. The following key text strings will be recognized and cause the appropriate graphic to be
used for the key cap instead of the actual text:
| Text string | Key purpose |
|:------------------|:--------------------------------------------------------|

View file

@ -4,11 +4,14 @@ title: Overview
*KeymanWeb* is a cross-browser JavaScript input method solution.
The *KeymanWeb 17 API* provides JavaScript functions to allow a website developer to integrate the use of
*KeymanWeb* multi-lingual keyboard mapping into a website, using either a standard or a custom-designed user-interface. The functions are exposed as API calls to the *KeymanWeb* core, the
*On-Screen Keyboard* module, a *Utility function* library, or one of the standard *User Interface* modules.
The *KeymanWeb API* provides JavaScript functions to allow a website developer to integrate the
use of *KeymanWeb* multi-lingual keyboard mapping into a website, using either a standard or a
custom-designed user-interface. The functions are exposed as API calls to the *KeymanWeb* core,
the *On-Screen Keyboard* module, a *Utility function* library, or one of the standard *User
Interface* modules.
A *KeymanWeb* instance is automatically constructed when you include the compiled KeymanWeb script (kmw-release.js) in your web page source.
A *KeymanWeb* instance is automatically constructed when you include the compiled KeymanWeb
script (kmw-release.js) in your web page source.
The *KeymanWeb API* comprises the following objects:

View file

@ -107,7 +107,7 @@
"@sentry/cli": "^2.31.0",
"@zip.js/zip.js": "^2.7.32",
"c8": "^7.12.0",
"express": "^4.19.2",
"express": "^4.22.2",
"jsdom": "^23.0.1",
"promise-status-async": "^1.2.10",
"tsx": "^4.19.0"

View file

@ -1,10 +0,0 @@
# top-most EditorConfig file
root = true
; applies to all files
[*]
indent_style = space
[*.{java, js, ts, sh, css, html, xml}]
indent_size = 2

View file

@ -149,7 +149,7 @@ export class FocusAssistant extends EventEmitter<EventMap> {
* @param {(boolean|number)} state Activate (true,false)
*/
setMaintainingFocus(state: boolean) {
this.maintainingFocus = state ? true : false;
this.maintainingFocus = !!state;
}
setFocusTimer(): void {

View file

@ -257,7 +257,7 @@ export class KeymanEngine extends KeymanEngineBase<BrowserConfiguration, Context
* activationPending (bool): KMW being activated
* activated (bool): KMW active
*
* See https://help.keyman.com/developer/engine/web/16.0/reference/core/getUIState
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/getUIState
*/
public getUIState(): FocusStateAPIObject {
return this.contextManager.focusAssistant.getUIState();
@ -266,7 +266,7 @@ export class KeymanEngine extends KeymanEngineBase<BrowserConfiguration, Context
/**
* Set or clear the IsActivatingKeymanWebUI flag (exposed function)
*
* See https://help.keyman.com/developer/engine/web/16.0/reference/core/activatingUI
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/activatingUI
*
* @param {(boolean|number)} state Activate (true,false)
*/
@ -275,35 +275,41 @@ export class KeymanEngine extends KeymanEngineBase<BrowserConfiguration, Context
}
/**
* Function setKeyboardForControl
* Scope Public
* @param {Element} Pelem Control element
* @param {string|null=} Pkbd JSKeyboard (Clears the set keyboard if set to null.)
* @param {string|null=} Plc Language Code
* Description Set default keyboard for the control
* Associate control with independent keyboard settings initialized to a specific keyboard.
* The pair of `keyboard` and `languageCode` (if not `null`) must refer to a
* registered keyboard stub.
*
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/setKeyboardForControl
*
* @param {Element} elem The control element to be managed manually
* @param {string|null=} keyboard The id of a keyboard, consisting of the word
* `Keyboard_` followed by the internal keyboard
* name. For a keyboard with the internal name
* `laokeys` this would be `Keyboard_laokeys`.
* If `null` clears the set keyboard.
* @param {string|null=} languageCode A BCP47 language code which was used when
* registering the keyboard stub.
*/
public setKeyboardForControl(Pelem: HTMLElement, Pkbd?: string, Plc?: string): void {
if(Pelem instanceof Pelem.ownerDocument.defaultView.HTMLIFrameElement) {
public setKeyboardForControl(elem: HTMLElement, keyboard?: string, languageCode?: string): void {
if(elem instanceof elem.ownerDocument.defaultView.HTMLIFrameElement) {
console.warn("'keymanweb.setKeyboardForControl' cannot set keyboard on iframes.");
return;
}
if(!this.isAttached(Pelem)) {
console.error("KeymanWeb is not attached to element " + Pelem);
if(!this.isAttached(elem)) {
console.error("KeymanWeb is not attached to element " + elem);
return;
}
let stub = null;
if(Pkbd) {
stub = this.keyboardRequisitioner.cache.getStub(Pkbd, Plc);
if(keyboard) {
stub = this.keyboardRequisitioner.cache.getStub(keyboard, languageCode);
if(!stub) {
throw new Error(`No keyboard has been registered with id ${Pkbd} and language code ${Plc}.`);
throw new Error(`No keyboard has been registered with id ${keyboard} and language code ${languageCode}.`);
}
}
this.contextManager.setKeyboardForTextStore(Pelem._kmwAttachment.textStore, Pkbd, Plc);
this.contextManager.setKeyboardForTextStore(elem._kmwAttachment.textStore, keyboard, languageCode);
}
/**
@ -400,7 +406,7 @@ export class KeymanEngine extends KeymanEngineBase<BrowserConfiguration, Context
*/
// TODO-web-core: check each property of Lstub for KMXKeyboard availability
private _GetKeyboardDetail(Lstub: KeyboardStub, Lkbd: Keyboard): KeyboardDetails { // I2078 - Full keyboard detail
return {
return Lstub && {
Name: Lstub.KN,
InternalName: Lstub.KI,
LanguageName: Lstub.KL, // I1300 - Add support for language names
@ -461,12 +467,7 @@ export class KeymanEngine extends KeymanEngineBase<BrowserConfiguration, Context
const stub = this.keyboardRequisitioner.cache.getStub(id, langCode);
const keyboard = this.keyboardRequisitioner.cache.getKeyboardForStub(stub);
if (keyboard instanceof JSKeyboard) {
return stub && this._GetKeyboardDetail(stub, keyboard);
} else {
// TODO-web-core: do this work in _GetKeyboardDetail (implement for KMX keyboards)
return null;
}
return this._GetKeyboardDetail(stub, keyboard);
}
/**
@ -483,11 +484,9 @@ export class KeymanEngine extends KeymanEngineBase<BrowserConfiguration, Context
const keyboardStubs = cache.getStubList()
for (const stub of keyboardStubs) {
const keyboard = cache.getKeyboardForStub(stub);
if (keyboard instanceof JSKeyboard) {
const keyboardDetails = this._GetKeyboardDetail(stub, keyboard);
const keyboardDetails = this._GetKeyboardDetail(stub, keyboard);
if (keyboardDetails) {
detailsForAllKeyboards.push(keyboardDetails);
} else {
// TODO-web-core: do this work in _GetKeyboardDetail (implement for KMX keyboards if needed)
}
}
return detailsForAllKeyboards;

View file

@ -261,12 +261,19 @@ if(!keymanweb) {
}
this.updateList = false;
// Set the menu selector to the currently saved keyboard
const sk = keymanweb.getSavedKeyboard().split(':');
if(sk.length < 2) {
sk[1] = '';
// Set the menu selector to the last active keyboard
let activeKeyboard = keymanweb.getActiveKeyboard();
let activeLanguage = '';
if (activeKeyboard) {
activeLanguage = keymanweb.getActiveLanguage();
} else {
// savedKeyboard is only correct if we use global keyboard settings,
// otherwise it's set to the first keyboard in the list
const savedKeyboard = keymanweb.getSavedKeyboard().split(':');
activeKeyboard = savedKeyboard[0];
activeLanguage = savedKeyboard.length < 2 ? '' : savedKeyboard[1];
}
this.updateMenu(sk[0],sk[1]);
this.updateMenu(activeKeyboard, activeLanguage);
// Redisplay the UI to correct width for any new language entries
if(keymanweb.getLastActiveElement()) {

View file

@ -152,12 +152,12 @@ if(!keymanweb) {
}
osk.addEventListener('show', (oskPosition) => {
// Ensure that the ui.controller is visible if help is displayed
this.controller.style.display = 'block';
this.oskButton._setSelected(true);
// Ensure that the ui.controller is visible if help is displayed
this.controller.style.display = 'block';
this.oskButton._setSelected(true);
return oskPosition;
});
return oskPosition;
});
osk.addEventListener('hide', (byUser) => {
if(byUser['HiddenByUser']) {
@ -542,8 +542,18 @@ if(!keymanweb) {
}
// Highlight the last active keyboard
const sk=keymanweb.getSavedKeyboard().split(':');
this.updateMenu(sk[0],sk[1]);
let activeKeyboard = keymanweb.getActiveKeyboard();
let activeLanguage = '';
if (activeKeyboard) {
activeLanguage = keymanweb.getActiveLanguage();
} else {
// savedKeyboard is only correct if we use global keyboard settings,
// otherwise it's set to the first keyboard in the list
const savedKeyboard = keymanweb.getSavedKeyboard().split(':');
activeKeyboard = savedKeyboard[0];
activeLanguage = savedKeyboard[1];
}
this.updateMenu(activeKeyboard, activeLanguage);
}
/* ----------------------------------------
@ -555,24 +565,24 @@ if(!keymanweb) {
/**
* Function selecKbd
* Scope Private
* @param {number} _kbd
* @param {number} kbdIndex
* Description Select a keyboard from the drop down menu
**/
private async selectKbd(_kbd: number): Promise<boolean> {
let _name,_lgCode;
if(_kbd < 0) {
_name = '';
_lgCode='';
private async selectKbd(kbdIndex: number): Promise<boolean> {
let name: string, languageCode: string;
if (kbdIndex < 0) {
name = '';
languageCode = '';
} else {
_name = this.keyboards[_kbd]._InternalName;
_lgCode = this.keyboards[_kbd]._LanguageCode;
name = this.keyboards[kbdIndex]._InternalName;
languageCode = this.keyboards[kbdIndex]._LanguageCode;
}
await keymanweb.setActiveKeyboard(_name,_lgCode);
await keymanweb.setActiveKeyboard(name, languageCode);
keymanweb.focusLastActiveElement();
this.kbdButton._setSelected(_name != '');
if(_kbd >= 0) {
this.lastActiveKeyboard = _kbd;
this.kbdButton._setSelected(name != '');
if (kbdIndex >= 0) {
this.lastActiveKeyboard = kbdIndex;
}
return false;
@ -687,6 +697,22 @@ if(!keymanweb) {
`;
}
private createMenuItem(index: number, id: string, name: string, isSelected: boolean): { li: HTMLLIElement, a: HTMLAnchorElement } {
const li = util.createElement('li');
const a = util.createElement('a');
a.innerHTML = name;
a.href = "#";
a.onclick = ((x) => {
return () => this.selectKbd(x);
})(index);
a.id = id;
if (isSelected) {
a.className = 'selected';
}
li.appendChild(a);
return { li, a };
}
/**
* Function createMenu
* Scope Private
@ -701,38 +727,29 @@ if(!keymanweb) {
this.keyboardMenu.innerHTML = ''; // I2403 - Allow toggle design to be loaded twice
}
const _li = util.createElement('li');
const _a = util.createElement('a');
_a.innerHTML='(System keyboard)';
_a.href="#";
_a.onclick = () => {
return this.selectKbd(-1);
};
_a.id='KMWSel_$';
_a.className='selected';
_li.appendChild(_a);
// Add the default (system) keyboard as the first entry in the menu
const { li, a } = this.createMenuItem(-1, 'KMWSel_$', '(System keyboard)', true);
this.selectedMenuItem = a;
this.keyboardMenu.appendChild(li);
this.selectedMenuItem=_a;
this.keyboardMenu.appendChild(_li);
const _kbds=keymanweb.getKeyboards(), _added: Record<string, number> = {};
// Add loaded keyboards to the menu
this.keyboards = [];
for(let _kbd = 0; _kbd < _kbds.length; _kbd++) {
const _li1=util.createElement('li');
const _a1=util.createElement('a');
_a1.innerHTML=_kbds[_kbd].LanguageName + ' - ' + _kbds[_kbd].Name;
if(!_added[_kbds[_kbd].InternalName]) _added[_kbds[_kbd].InternalName]=0;
_added[_kbds[_kbd].InternalName]++;
const keyboards = keymanweb.getKeyboards();
const added: Record<string, number> = {};
for (let kbdIndex = 0; kbdIndex < keyboards.length; kbdIndex++) {
if (!added[keyboards[kbdIndex].InternalName]) {
added[keyboards[kbdIndex].InternalName] = 0;
}
added[keyboards[kbdIndex].InternalName]++;
const _n=_added[_kbds[_kbd].InternalName];
this.keyboards.push({_InternalName: _kbds[_kbd].InternalName, _LanguageCode:_kbds[_kbd].LanguageCode, _Index: _n});
const kbdInstanceCount = added[keyboards[kbdIndex].InternalName];
this.keyboards.push({ _InternalName: keyboards[kbdIndex].InternalName, _LanguageCode: keyboards[kbdIndex].LanguageCode, _Index: kbdInstanceCount });
_a1.href="#";
_a1.onclick = ((x) => { return () => this.selectKbd(x); })(this.keyboards.length-1);
_a1.id='KMWSel_'+_kbds[_kbd].InternalName+'$'+_n;
_li1.appendChild(_a1);
this.keyboardMenu.appendChild(_li1);
const { li } = this.createMenuItem(this.keyboards.length - 1,
`KMWSel_${keyboards[kbdIndex].InternalName}\$${kbdInstanceCount}`,
`${keyboards[kbdIndex].LanguageName} - ${keyboards[kbdIndex].Name}`,
false);
this.keyboardMenu.appendChild(li);
}
//if(!ui.initialized) // I2403 - Allow toggle design to be loaded twice
@ -740,7 +757,6 @@ if(!keymanweb) {
this.kbdButton.getElem().appendChild(this.keyboardMenu);
}
};
}
@ -795,4 +811,4 @@ if(!keymanweb) {
ui.initialize();
} catch(ex){}
}
}

View file

@ -1150,8 +1150,8 @@ if(!keymanweb) {
internalName: string,
languageCode: string
}) => { // Uses a different format than .getKeyboards(), b/c why not?
// https://help.keyman.com/developer/engine/web/16.0/reference/core/getKeyboards vs
// https://help.keyman.com/developer/engine/web/16.0/reference/events/kmw.keyboardchange
// https://help.keyman.com/developer/engine/web/current-version/reference/core/getKeyboards vs
// https://help.keyman.com/developer/engine/web/current-version/reference/events/kmw.keyboardchange
this.lastSelectedKeyboard = null;
const kbName=p.internalName,
lgName=keymanweb.util.getLanguageCodes(p.languageCode)[0];

View file

@ -182,9 +182,9 @@ export class KeyboardStub extends KeyboardProperties {
}
}
public validateForCustomKeyboard(): Error {
if(super.validateForCustomKeyboard() || !this.KF || !this.KR) {
return new Error('To use a custom keyboard, you must specify file name, keyboard id, keyboard name, language, language code, and region.');
public validateForCustomKeyboard(): Error|null {
if(super.validateForCustomKeyboard() || !this.KF) {
return new Error('To use a custom keyboard, you must specify file name, keyboard id, keyboard name, language and language code.');
} else {
return null;
}

View file

@ -255,7 +255,7 @@ export class KeyboardProperties implements KeyboardInternalPropertySpec {
return null;
}
public validateForCustomKeyboard(): Error {
public validateForCustomKeyboard(): Error|null {
if(!this.KI || !this.KN || !this.KL || !this.KLC) {
return new Error("To use a custom keyboard, you must specify keyboard id, keyboard name, language and language code.");
} else {

View file

@ -85,7 +85,6 @@ export class KeyboardInterfaceBase<ContextManagerType extends ContextManagerBase
//
// It may also be used by documented legacy API:
// https://help.keyman.com/developer/engine/web/2.0/guide/examples/manual-control
// (See: referenced laokeys_load.js)
//
// The mobile apps typically have fully-preconfigured paths, but Developer's
// test-host page does not.
@ -123,4 +122,4 @@ export class KeyboardInterfaceBase<ContextManagerType extends ContextManagerBase
(function() {
KeyboardInterfaceBase.__publishShorthandAPI();
}());
}());

View file

@ -36,8 +36,7 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
root: '..',
// Operates from the configured root set above - the 'root' folder for this sample.
resources: '/resources',
@ -45,9 +44,9 @@
// KMW will look for them here - two folders up from root, which is the base sample directory.
keyboards: '/../../',
attachType:'auto'
}).then(function() {
}).then(async function() {
// Loads keyboards from the base sample folder
keyman.addKeyboards({
await keyman.addKeyboards({
id:'us',
name:'English',
languages: {
@ -58,7 +57,7 @@
filename: 'us-1.0.js'
});
// For contrast:
keyman.addKeyboards({
await keyman.addKeyboards({
id:'lao_2008_basic',
name:'Lao Basic',
languages: {
@ -74,12 +73,12 @@
// 1. keyboard name ('french'),
// 2. keyboard name and language code ('sil_euro_latin@no,sv'),
// 3. or just the BCP-47 language code ('@he').
kmw.addKeyboards('french', 'sil_euro_latin@no,sv', '@he');
await keyman.addKeyboards('french', 'sil_euro_latin@no,sv', '@he');
// Add a keyboard by language name. Note that the name must be spelled
// correctly, or the keyboard will not be found. (Using BCP-47 codes is
// usually easier.)
doAddKeyboardsForLanguage('Dzongkha');
await doAddKeyboardsForLanguage('Dzongkha');
});
</script>

View file

@ -1,51 +1,62 @@
// JavaScript Document samplehdr.js: Keyboard management for KeymanWeb demonstration pages
/*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Keyboard management for KeymanWeb demonstration pages
*/
/*
The keyboard name and/or BCP-47 language code must be specified for each keyboard that is to be available.
If the same keyboard is used for several languages, it must be listed for each
language, but the keyboard itself will only be loaded once.
If two (or more) keyboards are to be available for a given language, both must be listed.
Any number of keyboards may be specified in one or more calls.
Keyboard paths may be absolute (with respect to the server root) or relative to the keyboards option path.
The actual keyboard object will be downloaded asynchronously when first selected for use.
The keyboard name and/or BCP-47 language code must be specified for
each keyboard that is to be available. If the same keyboard is used
for several languages, it must be listed for each language, but the
keyboard itself will only be loaded once. If two (or more) keyboards
are to be available for a given language, both must be listed. Any
number of keyboards may be specified in one or more calls. Keyboard
paths may be absolute (with respect to the server root) or relative
to the keyboards option path. The actual keyboard object will be
downloaded asynchronously when first selected for use.
Each argument to addKeyboards() is a string, for example:
european2 loads the current version of the Eurolatin 2 keyboard (for its default language)
european2@fr loads the current version of the Eurolatin 2 keyboard for French
european2@fr@1.2 loads version 1.2 of the Eurolatin 2 keyboard for French
Each argument to addKeyboards() is a string, for example: european2
loads the current version of the Eurolatin 2 keyboard (for its
default language) european2@fr loads the current version of
the Eurolatin 2 keyboard for French european2@fr@1.2 loads
version 1.2 of the Eurolatin 2 keyboard for French
Argument syntax also supports the following extensions:
@fr load the current version of the default keyboard for French
@fr$ load all available keyboards (current version) for French
Argument syntax also supports the following extensions: @fr
load the current version of the default keyboard for French @fr$
load all available keyboards (current version) for French
Each call to addKeyboards() requires a single call to the remote server,
(unless all keyboards listed are local and fully specified) so it is better
to use multiple arguments rather than separate function calls.
Each call to addKeyboards() requires a single call to the remote
server, (unless all keyboards listed are local and fully specified)
so it is better to use multiple arguments rather than separate
function calls.
Calling addKeyboards() with no arguments returns a list of *all* available keyboards.
The Toolbar (desktop browser) UI is best suited for allowing users to select
the appropriate language and keyboard in this case.
Calling addKeyboards() with no arguments returns a list of *all*
available keyboards. The Toolbar (desktop browser) UI is best suited
for allowing users to select the appropriate language and keyboard
in this case.
Keyboards may also be specified by language name using addKeyboardsForLanguage()
for example:
keymanweb.addKeyboardsForLanguage('Burmese');
Keyboards may also be specified by language name using
addKeyboardsForLanguage() for example:
keymanweb.addKeyboardsForLanguage('Burmese');
Appending $ to the language name will again cause all available keyboards for that
language to be loaded rather than the default keyboard.
Appending $ to the language name will again cause all available
keyboards for that language to be loaded rather than the default
keyboard.
The first call to addKeyboardsForLanguage() makes an additional call to the
keyman API to load the current list of keyboard/language associations.
The first call to addKeyboardsForLanguage() makes an additional call
to the keyman API to load the current list of keyboard/language
associations.
In this example, the following function loads the indicated keyboards,
and is called when the page loads.
In this example, the following function loads the indicated
keyboards, and is called when the page loads.
*/
function errToString(err) {
// Painful? Kinda. But needed on un-updated Android API 21!
if(Array.isArray(err)) {
var result = '';
for(var i = 0; i < err.length; i++) {
var e = err[i];
let result = '';
for(let i = 0; i < err.length; i++) {
const e = err[i];
if(e.error instanceof Error) {
result += e.error.message + '\n';
} else {
@ -72,44 +83,42 @@
});
}
function loadKeyboards(nestLevel) {
var kmw=keyman;
var base_prefix = '../';
var prefix = './'; // The default - when prefix == 0.
async function loadKeyboards(nestLevel) {
const base_prefix = '../';
let prefix = './'; // The default - when prefix == 0.
if(nestLevel !== undefined && nestLevel > 0) {
prefix = '';
for(var i=0; i < nestLevel; i++) {
prefix = prefix + base_prefix;
for(let i=0; i < nestLevel; i++) {
prefix += base_prefix;
}
}
// The first keyboard added will be the default keyboard for touch devices.
// For faster loading, it may be best for the default keyboard to be
// locally sourced.
doAddKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'},
await doAddKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'},
filename:(prefix + 'us-1.0.js')});
// Add more keyboards to the language menu by:
// 1. keyboard name ('french'),
// 2. keyboard name and language code ('sil_euro_latin@no,sv'),
// 3. or just the BCP-47 language code ('@he').
kmw.addKeyboards('french', 'sil_euro_latin@no,sv', '@he');
await keyman.addKeyboards('french', 'sil_euro_latin@no,sv', '@he');
// Add a keyboard by language name. Note that the name must be spelled
// correctly, or the keyboard will not be found. (Using BCP-47 codes is
// usually easier.)
doAddKeyboardsForLanguage('Dzongkha');
await doAddKeyboardsForLanguage('Dzongkha');
// Add a fully-specified, locally-sourced, keyboard with custom font
doAddKeyboards({
id:'lao_2008_basic',
name:'Lao Basic',
await doAddKeyboards({
id: 'lao_2008_basic',
name: 'Lao Basic',
languages: {
id:'lo',name:'Lao',region:'Asia',
id: 'lo', name: 'Lao', region: 'Asia'
},
filename:(prefix + 'lao_2008_basic-1.2.js')
filename: (prefix + 'lao_2008_basic-1.2.js')
});
// Note that locally specified keyboards will be listed before keyboards
@ -118,29 +127,31 @@
}
// Script to allow a user to add any keyboard to the keyboard menu
function addKeyboard(n) {
var sKbd;
async function addKeyboard(n) {
let sKbd;
switch(n) {
case 1:
sKbd=document.getElementById('kbd_id1').value;
doAddKeyboards(sKbd);
await doAddKeyboards(sKbd);
break;
case 2:
sKbd=document.getElementById('kbd_id2').value.toLowerCase();
doAddKeyboards('@'+sKbd);
await doAddKeyboards('@'+sKbd);
break;
case 3:
// Add keyboard for comma-separated language name(s)
sKbd=document.getElementById('kbd_id3').value;
doAddKeyboardsForLanguage(sKbd);
await doAddKeyboardsForLanguage(sKbd);
break;
}
}
// Add keyboard on Enter (as well as pressing button)
function clickOnEnter(e,id)
async function clickOnEnter(e,id)
{
e = e || window.event;
if(e.keyCode == 13) addKeyboard(id);
if (e.keyCode == 13) {
await addKeyboard(id);
}
}

View file

@ -36,8 +36,7 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
}).then(function() {
// Loads the common keyboards from the previous folder

View file

@ -36,8 +36,7 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
}).then(function() {
// Loads the common keyboards from the previous folder

View file

@ -28,11 +28,10 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
}).then(function() {
loadKeyboards();
}).then(async function() {
await loadKeyboards();
});
</script>
<!--
@ -44,10 +43,9 @@
Keyboard paths may be absolute (with respect to the server root) or relative to the keyboards option path.
-->
<script>
function loadKeyboards() {
keyman.addKeyboards().then(function() {
keyman.setActiveKeyboard('basic_kbdus','en');
});
async function loadKeyboards() {
await keyman.addKeyboards();
await keyman.setActiveKeyboard('basic_kbdus','en');
}
</script>

View file

@ -63,7 +63,7 @@ describe('LanguageProcessor', function() {
assert.isTrue(languageProcessor.mayPredict);
// Some aspects of initialization must wait until after construction and overall
// load of the core. See /web/source/kmwbase.ts, in the final IIFE.
// load of the core.
assert.isOk(languageProcessor.lmEngine);
});
});

View file

@ -40,9 +40,10 @@
const attachType = (new URLSearchParams(window.location.search)).get("mode");
var attachText = attachType ? attachType : "default";
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType: attachType ? attachType : ''
}).then(function() {
loadKeyboards(1);
});
</script>
@ -56,7 +57,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards(1);'>
<body>
<h2>KeymanWeb Sample Page - Attachment API Testing</h2>
<p>This page is designed to stress-test the new attachment/enablement API model and its functions.</p>
<p id="modeSelect">You are currently testing under the <em id="mode"> </em> attachment mode. Two other modes are available:

View file

@ -1,7 +1,6 @@
// Page-global variable definitions.
{
var inputCounter = 0;
var kmw = keyman;
}
function generateDiagnosticDiv(elem) {
@ -23,8 +22,8 @@ function generateDiagnosticDiv(elem) {
attachBtn.type = 'button';
attachBtn.id = 'attach_' + elemId;
attachBtn.onclick = function() {
kmw.attachToControl(document.getElementById(elemId));
var attached = kmw.isAttached(elem);
keyman.attachToControl(document.getElementById(elemId));
var attached = keyman.isAttached(elem);
document.getElementById('attachment_' + elemId).textContent = " Currently " + (attached ? "attached." : "detached.");
const indepLabel = document.getElementById("independence_" + elemId);
// does not exist for iframes
@ -40,8 +39,8 @@ function generateDiagnosticDiv(elem) {
detachBtn.type = 'button';
detachBtn.id = 'detach_' + elemId;
detachBtn.onclick = function() {
kmw.detachFromControl(document.getElementById(elemId));
var attached = kmw.isAttached(elem);
keyman.detachFromControl(document.getElementById(elemId));
var attached = keyman.isAttached(elem);
document.getElementById('attachment_' + elemId).textContent = " Currently " + (attached ? "attached." : "detached.");
const indepLabel = document.getElementById("independence_" + elemId);
// does not exist for iframes
@ -58,7 +57,7 @@ function generateDiagnosticDiv(elem) {
var attachLabel = document.createElement("a");
attachLabel.id = 'attachment_' + elemId;
window.setTimeout(function() {
attachLabel.textContent = " Currently " + (kmw.isAttached(elem) ? "attached." : "detached.");
attachLabel.textContent = " Currently " + (keyman.isAttached(elem) ? "attached." : "detached.");
}, attachTimer);
div.appendChild(attachLabel);
@ -69,7 +68,7 @@ function generateDiagnosticDiv(elem) {
enableBtn.type = 'button';
enableBtn.id = 'enable_' + elemId;
enableBtn.onclick = function() {
kmw.enableControl(document.getElementById(elemId));
keyman.enableControl(document.getElementById(elemId));
};
enableBtn.value = 'Enable';
div.appendChild(enableBtn);
@ -79,7 +78,7 @@ function generateDiagnosticDiv(elem) {
disableBtn.type = 'button';
disableBtn.id = 'disable_' + elemId;
disableBtn.onclick = function() {
kmw.disableControl(document.getElementById(elemId));
keyman.disableControl(document.getElementById(elemId));
};
disableBtn.value = 'Disable';
div.appendChild(disableBtn);
@ -107,9 +106,9 @@ function generateDiagnosticDiv(elem) {
setBtn.type = 'button';
setBtn.id = 'set_' + elemId;
setBtn.onclick = function() {
kmw.setKeyboardForControl(document.getElementById(elemId), 'dzongkha', 'dz');
keyman.setKeyboardForControl(document.getElementById(elemId), 'dzongkha', 'dz');
if(kmw.isAttached(elem)) {
if(keyman.isAttached(elem)) {
kbdLabel.textContent = " Using independently-tracked keyboard.";
}
};
@ -121,9 +120,9 @@ function generateDiagnosticDiv(elem) {
clearBtn.type = 'button';
clearBtn.id = 'clear_' + elemId;
clearBtn.onclick = function() {
kmw.setKeyboardForControl(document.getElementById(elemId), null, null);
keyman.setKeyboardForControl(document.getElementById(elemId), null, null);
if(kmw.isAttached(elem)) {
if(keyman.isAttached(elem)) {
kbdLabel.textContent = " Using globally-selected keyboard.";
}
};
@ -133,8 +132,8 @@ function generateDiagnosticDiv(elem) {
var kbdLabel = document.createElement("a");
kbdLabel.id = 'independence_' + elemId;
window.setTimeout(function () {
if(kmw.isAttached(elem)) {
var attached = kmw.isAttached(elem);
if(keyman.isAttached(elem)) {
var attached = keyman.isAttached(elem);
kbdLabel.textContent = attached ? " Using globally-selected keyboard." : '';
}
}, 1);

View file

@ -36,8 +36,9 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init();
keyman.init().then(function() {
loadKeyboards(1);
});
</script>
<!-- Add keyboard management script for local selection of keyboards to use -->
@ -46,7 +47,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards(1);'>
<body>
<h2>KeymanWeb: Test IFrame connectivity</h2>
<div id='DynamicTextboxes'>

View file

@ -37,8 +37,6 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
var platform = 'desktop';
keyman.getOskWidth = function() {
@ -51,7 +49,7 @@
platform == 'phone' ? 240 : 360;
};
let shadowfy = function(container) {
function shadowfy(container) {
const shadow = container.attachShadow({ mode: "open" });
const docBase = container.children[0];
@ -67,7 +65,7 @@
shadow.appendChild(docBase);
}
let documentKeyboard = async function(kbdid, langid, formFactor, layer) {
async function documentKeyboard(kbdid, langid, formFactor, layer) {
let docTarget = document.getElementById("docTarget");
label = document.createElement('h3');
docTarget.appendChild(label);
@ -77,8 +75,8 @@
const container = document.createElement('div');
await kmw.addKeyboards(`${kbdid}@${langid}`);
await kmw.setActiveKeyboard(kbdid, langid);
await keyman.addKeyboards(`${kbdid}@${langid}`);
await keyman.setActiveKeyboard(kbdid, langid);
docBase = keyman.BuildVisualKeyboard(kbdid, 1, formFactor, layer);
label.textContent = `${keyman.getKeyboard(kbdid).Name} - ${formFactor} - ${layer} layer`;
@ -95,7 +93,8 @@
shadowfy(container);
}
kmw.init().then(async function() {
keyman.init().then(async function() {
await loadKeyboards(1);
await documentKeyboard("gff_amharic", "am", "phone", "default");
await documentKeyboard("sil_euro_latin", "no", "phone", "shift");
await documentKeyboard("lao_2008_basic", "lo", "tablet", "default");
@ -111,7 +110,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards(1);'>
<body>
<h2>KeymanWeb: Keyboard documentation rendering</h2>
<div id='DynamicTextboxes'>
@ -121,15 +120,12 @@
(It is ignored by other User Interfaces.)
-->
<div id='KeymanWebControl'></div>
<h3>This test page exists for testing KMW's keyboard-documentation rendering system.</h3>
<!--<iframe src="hello-world.html" height=100></iframe>-->
<hr>
<div id="docTarget"></div>
</div>
<h3><a href="../">Return to testing home page</a></h3>
<h3>This test page exists for testing KMW's keyboard-documentation rendering system.</h3>
<!--<iframe src="hello-world.html" height=100></iframe>-->
<hr>
<div id="docTarget"></div>
</div>
<h3><a href="../">Return to testing home page</a></h3>
</body>
<!--

View file

@ -36,10 +36,9 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType: 'auto'
});
}).then(loadKeyboards);
</script>
<!-- Add keyboard management script for local selection of keyboards to use -->
@ -51,7 +50,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards();'>
<body>
<h2>KeymanWeb Sample Page - Chirality Testing</h2>
<p>This page is designed to stress-test the new support for chiral modifiers and state keys.</p>
<p>Be sure to reference the developer console for additional feedback. </p>

View file

@ -1,19 +1,17 @@
function loadKeyboards()
{
var kmw=keyman;
async function loadKeyboards()
{
// A testing keyboard handwritten with chirality information.
kmw.addKeyboards({id:'chirality',name:'Chirality Testing',
await keyman.addKeyboards({id:'chirality',name:'Chirality Testing',
languages:{
id:'en',name:'English',region:'North America'
},
filename:'../../../../../build/test-resources/keyboards/chirality.js'
});
// A testing keyboard using 10.0 format and the KLS layout specifier
// without the 'shift' layer properly defined.
// Add a fully-specified, locally-sourced, keyboard with custom font
kmw.addKeyboards({id:'halfDefined',name:'Undefined Shift Layer Keyboard',
// Add a fully-specified, locally-sourced, keyboard with custom font
await keyman.addKeyboards({id:'halfDefined',name:'Undefined Shift Layer Keyboard',
languages:{
id:'en',name:'English',region:'North America'
},

View file

@ -30,10 +30,9 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType: 'auto'
});
}).then(loadKeyboards);
</script>
<!-- Add keyboard management script for local selection of keyboards to use -->
@ -44,7 +43,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards();'>
<body>
<h2>KeymanWeb Sample Page - Test CKEditor Integration</h2>
<p>Test CKEditor Integration</p>

View file

@ -30,10 +30,9 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType: 'auto'
});
}).then(loadKeyboards);
</script>
<!-- Add keyboard management script for local selection of keyboards to use -->
@ -44,7 +43,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards();'>
<body>
<h2>KeymanWeb Sample Page - Test CKEditor Integration</h2>
<p>Test CKEditor Integration</p>

View file

@ -1,15 +1,13 @@
function loadKeyboards()
async function loadKeyboards()
{
var kmw=keyman;
// Add more keyboards to the language menu, by keyboard name,
// keyboard name and language code, or just the BCP-47 language code.
// We use a different loading pattern here than in the samples version to provide a slightly different set of test cases.
kmw.addKeyboards('french','@he');
kmw.addKeyboards({id:'sil_euro_latin', name:'SIL EuroLatin', languages: [{id:'no'}, {id:'sv'}]}); // Loads from partial stub instead of the compact string.
await keyman.addKeyboards('french', '@he',
{ id: 'sil_euro_latin', name: 'SIL EuroLatin', languages: [{ id: 'no' }, { id: 'sv' }] }); // Loads from partial stub instead of the compact string.
// Add a keyboard by language name. Note that the name must be spelled
// correctly, or the keyboard will not be found. (Using BCP-47 codes is
// usually easier.)
kmw.addKeyboardsForLanguage('Dzongkha');
await keyman.addKeyboardsForLanguage('Dzongkha');
}

View file

@ -1,172 +1,122 @@
// JavaScript Document samplehdr.js: Keyboard management for KeymanWeb demonstration pages
/*
The keyboard name and/or BCP-47 language code must be specified for each keyboard that is to be available.
If the same keyboard is used for several languages, it must be listed for each
language, but the keyboard itself will only be loaded once.
If two (or more) keyboards are to be available for a given language, both must be listed.
Any number of keyboards may be specified in one or more calls.
Keyboard paths may be absolute (with respect to the server root) or relative to the keyboards option path.
The actual keyboard object will be downloaded asynchronously when first selected for use.
* Keyman is copyright (C) SIL Global. MIT License.
*/
Each argument to addKeyboards() is a string, for example:
european2 loads the current version of the Eurolatin 2 keyboard (for its default language)
european2@fr loads the current version of the Eurolatin 2 keyboard for French
european2@fr@1.2 loads version 1.2 of the Eurolatin 2 keyboard for French
Argument syntax also supports the following extensions:
@fr load the current version of the default keyboard for French
@fr$ load all available keyboards (current version) for French
Each call to addKeyboards() requires a single call to the remote server,
(unless all keyboards listed are local and fully specified) so it is better
to use multiple arguments rather than separate function calls.
Calling addKeyboards() with no arguments returns a list of *all* available keyboards.
The Toolbar (desktop browser) UI is best suited for allowing users to select
the appropriate language and keyboard in this case.
Keyboards may also be specified by language name using addKeyboardsForLanguage()
for example:
keymanweb.addKeyboardsForLanguage('Burmese');
Appending $ to the language name will again cause all available keyboards for that
language to be loaded rather than the default keyboard.
The first call to addKeyboardsForLanguage() makes an additional call to the
keyman API to load the current list of keyboard/language associations.
In this example, the following function loads the indicated keyboards,
and is called when the page loads.
*/
function errToString(err) {
// Painful? Kinda. But needed on un-updated Android API 21!
if(Array.isArray(err)) {
var result = '';
for(var i = 0; i < err.length; i++) {
var e = err[i];
if(e.error instanceof Error) {
result += e.error.message + '\n';
} else {
result += JSON.stringify(e) + '\n';
}
}
return result;
}
if(err instanceof Error) {
return err.message;
}
return JSON.stringify(err);
}
function doAddKeyboards(data) {
return keyman.addKeyboards(data).catch(function(err) {
console.error('keyman.addKeyboards failed with '+errToString(err)+' for '+JSON.stringify(data));
});
}
function doAddKeyboardsForLanguage(data) {
return keyman.addKeyboardsForLanguage(data).catch(function(err) {
console.error('keyman.addKeyboardsForLanguage failed with '+errToString(err)+' for '+JSON.stringify(data));
});
}
function loadKeyboards(nestLevel)
{
var kmw=keyman;
var base_prefix = '../';
var prefix = './'; // The default - when prefix == 0.
if(nestLevel !== undefined && nestLevel > 0) {
prefix = '';
for(var i=0; i < nestLevel; i++) {
prefix = prefix + base_prefix;
function errToString(err) {
// Painful? Kinda. But needed on un-updated Android API 21!
if(Array.isArray(err)) {
let result = '';
for(let i = 0; i < err.length; i++) {
const e = err[i];
if(e.error instanceof Error) {
result += e.error.message + '\n';
} else {
result += JSON.stringify(e) + '\n';
}
}
// The first keyboard added will be the default keyboard for touch devices.
// For faster loading, it may be best for the default keyboard to be
// locally sourced.
doAddKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'},
filename:(prefix + 'us-1.0.js')});
doAddKeyboards({id:'web_context_tests',name:'Web Context Tests',languages:{id:'en',name:'English'},
filename:(prefix + 'web_context_tests.js')});
doAddKeyboards({id:'test_chirality',name:'test_chirality',languages:{id:'en',name:'English'},
filename:(prefix + 'test_chirality.js')});
doAddKeyboards({id:'obolo_chwerty_6351',name:'obolo_chwerty_6351',languages:{id:'en',name:'English'},
filename:(prefix + 'obolo_chwerty_6351.js')});
doAddKeyboards({id:'gesture_prototyping',name:'Gesture Prototyping',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/gesture_prototyping/build/gesture_prototyping.js')});
doAddKeyboards({id:'diacritic_rota',name:'Diacritic 10-key Rota',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/diacritic_rota/build/diacritic_rota.js')});
doAddKeyboards({id:'ye_old_ten_key',name:'Classic 10-key',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/ye_old_ten_key/build/ye_old_ten_key.js')});
// Add more keyboards to the language menu, by keyboard name,
// keyboard name and language code, or just the BCP-47 language code.
// We use a different loading pattern here than in the samples version to provide a slightly different set of test cases.
doAddKeyboards('french','@he');
doAddKeyboards('khmer_angkor','@km');
doAddKeyboards({id:'sil_euro_latin', name:'SIL EuroLatin', languages: [{id:'no'}, {id:'sv'}]}); // Loads from partial stub instead of the compact string.
// Add a keyboard by language name. Note that the name must be spelled
// correctly, or the keyboard will not be found. (Using BCP-47 codes is
// usually easier.)
doAddKeyboardsForLanguage('Dzongkha');
// Add a fully-specified, locally-sourced, keyboard with custom font
doAddKeyboards({id:'lao_2008_basic',name:'Lao Basic',
languages: {
id:'lo',name:'Lao',region:'Asia',
},
filename:(prefix + 'lao_2008_basic-1.2.js')
});
// The following two optional calls should be delayed until language menus are fully loaded:
// (a) a specific mapped input element input is focused, to ensure that the OSK appears
// (b) a specific keyboard is loaded, rather than the keyboard last used.
//window.setTimeout(function(){kmw.setActiveElement('ta1',true);},2500);
//window.setTimeout(function(){kmw.setActiveKeyboard('Keyboard_french','fr');},3000);
// Note that locally specified keyboards will be listed before keyboards
// requested from the remote server by user interfaces that do not order
// keyboards alphabetically by language.
return result;
}
if(err instanceof Error) {
return err.message;
}
return JSON.stringify(err);
}
// Script to allow a user to add any keyboard to the keyboard menu
function addKeyboard(n)
{
var sKbd,kmw=keyman;
switch(n)
{
case 1:
sKbd=document.getElementById('kbd_id1').value;
doAddKeyboards(sKbd);
break;
case 2:
sKbd=document.getElementById('kbd_id2').value.toLowerCase();
doAddKeyboards('@'+sKbd);
break;
case 3:
// Add keyboard for comma-separated language name(s)
sKbd=document.getElementById('kbd_id3').value;
doAddKeyboardsForLanguage(sKbd);
break;
function doAddKeyboards(data) {
return keyman.addKeyboards(data).catch(function(err) {
console.error('keyman.addKeyboards failed with '+errToString(err)+' for '+JSON.stringify(data));
});
}
function doAddKeyboardsForLanguage(data) {
return keyman.addKeyboardsForLanguage(data).catch(function(err) {
console.error('keyman.addKeyboardsForLanguage failed with '+errToString(err)+' for '+JSON.stringify(data));
});
}
async function loadKeyboards(nestLevel) {
const base_prefix = '../';
let prefix = './'; // The default - when prefix == 0.
if(nestLevel !== undefined && nestLevel > 0) {
prefix = '';
for(let i=0; i < nestLevel; i++) {
prefix += base_prefix;
}
}
// Add keyboard on Enter (as well as pressing button)
function clickOnEnter(e,id)
{
e = e || window.event;
if(e.keyCode == 13) addKeyboard(id);
}
// The first keyboard added will be the default keyboard for touch devices.
// For faster loading, it may be best for the default keyboard to be
// locally sourced.
await doAddKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'},
filename:(prefix + 'us-1.0.js')});
await doAddKeyboards({id:'web_context_tests',name:'Web Context Tests',languages:{id:'en',name:'English'},
filename:(prefix + 'web_context_tests.js')});
await doAddKeyboards({id:'test_chirality',name:'test_chirality',languages:{id:'en',name:'English'},
filename:(prefix + 'test_chirality.js')});
await doAddKeyboards({id:'obolo_chwerty_6351',name:'obolo_chwerty_6351',languages:{id:'en',name:'English'},
filename:(prefix + 'obolo_chwerty_6351.js')});
await doAddKeyboards({id:'gesture_prototyping',name:'Gesture Prototyping',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/gesture_prototyping/build/gesture_prototyping.js')});
await doAddKeyboards({id:'diacritic_rota',name:'Diacritic 10-key Rota',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/diacritic_rota/build/diacritic_rota.js')});
await doAddKeyboards({id:'ye_old_ten_key',name:'Classic 10-key',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/ye_old_ten_key/build/ye_old_ten_key.js')});
// Add more keyboards to the language menu, by keyboard name,
// keyboard name and language code, or just the BCP-47 language code.
// We use a different loading pattern here than in the samples version to provide a slightly different set of test cases.
await doAddKeyboards('french','@he');
await doAddKeyboards('khmer_angkor','@km');
await doAddKeyboards({id:'sil_euro_latin', name:'SIL EuroLatin', languages: [{id:'no'}, {id:'sv'}]}); // Loads from partial stub instead of the compact string.
// Add a keyboard by language name. Note that the name must be spelled
// correctly, or the keyboard will not be found. (Using BCP-47 codes is
// usually easier.)
await doAddKeyboardsForLanguage('Dzongkha');
// Add a fully-specified, locally-sourced, keyboard with custom font
await doAddKeyboards({
id: 'lao_2008_basic',
name: 'Lao Basic',
languages: {
id: 'lo', name: 'Lao', region: 'Asia'
},
filename: (prefix + 'lao_2008_basic-1.2.js')
});
}
// Script to allow a user to add any keyboard to the keyboard menu
async function addKeyboard(n) {
let sKbd;
switch(n) {
case 1:
sKbd=document.getElementById('kbd_id1').value;
await doAddKeyboards(sKbd);
break;
case 2:
sKbd=document.getElementById('kbd_id2').value.toLowerCase();
await doAddKeyboards('@'+sKbd);
break;
case 3:
// Add keyboard for comma-separated language name(s)
sKbd=document.getElementById('kbd_id3').value;
await doAddKeyboardsForLanguage(sKbd);
break;
}
}
// Add keyboard on Enter (as well as pressing button)
async function clickOnEnter(e,id) {
e = e || window.event;
if (e.keyCode == 13) {
await addKeyboard(id);
}
}

View file

@ -36,11 +36,10 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
}).then(function() {
kmw.addKeyboards({id:'default_subkey',name:'English',languages:{id:'en',name:'English'}, filename:'./default_subkey.js'});
}).then(async function() {
await keyman.addKeyboards({id:'default_subkey',name:'English',languages:{id:'en',name:'English'}, filename:'./default_subkey.js'});
var pageRef = (window.location.protocol == 'file:')
? window.location.href.substr(0, window.location.href.lastIndexOf('/')+1)

View file

@ -39,8 +39,7 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
}).then(function() {
loadKeyboards(1);

View file

@ -39,8 +39,7 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
}).then(function() {
loadKeyboards(1);

View file

@ -39,9 +39,8 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
attachType:'auto'
keyman.init({
attachType:'auto'
}).then(function() {
loadKeyboards(1);
});

View file

@ -36,15 +36,16 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
}).then(async function () {
await keyman.addKeyboards();
});
</script>
</head>
<!-- Sample page HTML -->
<body onload='kmw.addKeyboards();'>
<body>
<h2>KeymanWeb Testing Page - Desktop UI module Testing</h2>
<p>This page is designed to facilitate testing of various aspects of the Toolbar UI for
KeymanWeb. Originally developed to help address issue #1275.

View file

@ -36,18 +36,16 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType: 'auto'
}).then(function() {
// A testing keyboard with empty rows on each layer
kmw.addKeyboards({id:'empty_row', name: 'Empty Row',
keyman.addKeyboards({id:'empty_row', name: 'Empty Row',
languages:{
id:'en', name:'English'
},
filename: 'empty_row-1.0.js'
});
kmw.addKeyboards({id:'afghan_turkmen', name:'Afghan Turkmen',
}, {id:'afghan_turkmen', name:'Afghan Turkmen',
languages:{
id:'tk-Arab', name:'Turkmen (Arabic)'
},

View file

@ -20,8 +20,10 @@
</head>
<body>
<h1>KeymanWeb 19 Testing</h1>
<!--
<h1>KeymanWeb 20 Testing</h1>
<h2><a href="./kmxkeyboard.html">Test KMX keyboard loaded in Keymanweb (kmxkeyboard)</a></h2>
-->
<h1>KeymanWeb 17 Testing</h1>
<h2><a href="./unminified.html">Test unminified Keymanweb (unminified)</a></h2>

View file

@ -36,9 +36,8 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
window.setTimeout(() => {
kmw.init({
keyman.init({
attachType:'auto',
}).then(function() {
loadKeyboards(1);

View file

@ -49,8 +49,7 @@
document.getElementById('ta1').focus();
}
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
}).then(function() {
setOSK('windows');

View file

@ -1,124 +1,63 @@
// JavaScript Document samplehdr.js: Keyboard management for KeymanWeb demonstration pages
/*
The keyboard name and/or BCP-47 language code must be specified for each keyboard that is to be available.
If the same keyboard is used for several languages, it must be listed for each
language, but the keyboard itself will only be loaded once.
If two (or more) keyboards are to be available for a given language, both must be listed.
Any number of keyboards may be specified in one or more calls.
Keyboard paths may be absolute (with respect to the server root) or relative to the keyboards option path.
The actual keyboard object will be downloaded asynchronously when first selected for use.
* Keyman is copyright (C) SIL Global. MIT License.
*/
async function loadKeyboards()
{
await keyman.addKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'},
filename:'../us-1.0.js'});
await keyman.addKeyboards('french','european2@sv','european2@no','@he');
await keyman.addKeyboardsForLanguage('Dzongkha');
await keyman.addKeyboards({id:'lao_2008_basic',name:'Lao Basic',
languages:{
id:'lo',name:'Lao',region:'Asia',
font:{family:'LaoWeb',source:['../font/saysettha_web.ttf','../font/saysettha_web.woff','../font/saysettha_web.eot']}
},
filename:'../lao_2008_basic-1.2.js'
});
}
Each argument to addKeyboards() is a string, for example:
european2 loads the current version of the Eurolatin 2 keyboard (for its default language)
european2@fr loads the current version of the Eurolatin 2 keyboard for French
european2@fr@1.2 loads version 1.2 of the Eurolatin 2 keyboard for French
Argument syntax also supports the following extensions:
@fr load the current version of the default keyboard for French
@fr$ load all available keyboards (current version) for French
Each call to addKeyboards() requires a single call to the remote server,
(unless all keyboards listed are local and fully specified) so it is better
to use multiple arguments rather than separate function calls.
Calling addKeyboards() with no arguments returns a list of *all* available keyboards.
The Toolbar (desktop browser) UI is best suited for allowing users to select
the appropriate language and keyboard in this case.
Keyboards may also be specified by language name using addKeyboardsForLanguage()
for example:
keymanweb.addKeyboardsForLanguage('Burmese');
Appending $ to the language name will again cause all available keyboards for that
language to be loaded rather than the default keyboard.
The first call to addKeyboardsForLanguage() makes an additional call to the
keyman API to load the current list of keyboard/language associations.
In this example, the following function loads the indicated keyboards,
and is called when the page loads.
*/
function loadKeyboards()
// Script to allow a user to add any keyboard to the keyboard menu
async function addKeyboard(n)
{
let sKbd;
switch(n)
{
var kmw=window['keyman'] ? keyman : tavultesoft.keymanweb;
// The first keyboard added will be the default keyboard for touch devices.
// For faster loading, it may be best for the default keyboard to be
// locally sourced.
kmw.addKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'},
filename:'../us-1.0.js'});
// Add more keyboards to the language menu, by keyboard name,
// keyboard name and language code, or just the BCP-47 language code.
kmw.addKeyboards('french','european2@sv','european2@no','@he');
// Add a keyboard by language name. Note that the name must be spelled
// correctly, or the keyboard will not be found. (Using BCP-47 codes is
// usually easier.)
kmw.addKeyboardsForLanguage('Dzongkha');
// Add a fully-specified, locally-sourced, keyboard with custom font
kmw.addKeyboards({id:'lao_2008_basic',name:'Lao Basic',
languages:{
id:'lo',name:'Lao',region:'Asia',
font:{family:'LaoWeb',source:['../font/saysettha_web.ttf','../font/saysettha_web.woff','../font/saysettha_web.eot']}
},
filename:'../lao_2008_basic-1.2.js'
});
// The following two optional calls should be delayed until language menus are fully loaded:
// (a) a specific mapped input element input is focused, to ensure that the OSK appears
// (b) a specific keyboard is loaded, rather than the keyboard last used.
//window.setTimeout(function(){kmw.setActiveElement('ta1',true);},2500);
//window.setTimeout(function(){kmw.setActiveKeyboard('Keyboard_french','fr');},3000);
// Note that locally specified keyboards will be listed before keyboards
// requested from the remote server by user interfaces that do not order
// keyboards alphabetically by language.
case 1:
sKbd=document.getElementById('kbd_id1').value;
await keyman.addKeyboards(sKbd);
break;
case 2:
sKbd=document.getElementById('kbd_id2').value.toLowerCase();
await keyman.addKeyboards('@'+sKbd);
break;
case 3:
sKbd=document.getElementById('kbd_id3').value;
await keyman.addKeyboardsForLanguage(sKbd);
break;
}
}
// Script to allow a user to add any keyboard to the keyboard menu
function addKeyboard(n)
{
var sKbd, kmw=window['keyman'] ? keyman : tavultesoft.keymanweb;
switch(n)
{
case 1:
sKbd=document.getElementById('kbd_id1').value;
kmw.addKeyboards(sKbd);
break;
case 2:
sKbd=document.getElementById('kbd_id2').value.toLowerCase();
kmw.addKeyboards('@'+sKbd);
break;
case 3:
sKbd=document.getElementById('kbd_id3').value;
kmw.addKeyboardsForLanguage(sKbd);
break;
}
// Add keyboard on Enter (as well as pressing button)
async function clickOnEnter(e,id)
{
e = e || window.event;
if (e.keyCode == 13) {
await addKeyboard(id);
}
}
// Add keyboard on Enter (as well as pressing button)
function clickOnEnter(e,id)
{
e = e || window.event;
if(e.keyCode == 13) addKeyboard(id);
}
function removeKeyboard() {
var kmw=window['keyman'] ? keyman : tavultesoft.keymanweb;
var sKbd = document.getElementById('kbd_id4').value;
var result = kmw.removeKeyboards(sKbd);
console.log("Keyboard '" + sKbd + "' removal success: " + result);
}
// Removes keyboard on Enter (as well as pressing button)
function removeOnEnter(e)
{
e = e || window.event;
if(e.keyCode == 13) removeKeyboard();
async function removeKeyboard() {
let sKbd = document.getElementById('kbd_id4').value;
let result = await keyman.removeKeyboards(sKbd);
console.log("Keyboard '" + sKbd + "' removal success: " + result);
}
// Removes keyboard on Enter (as well as pressing button)
async function removeOnEnter(e)
{
e = e || window.event;
if(e.keyCode == 13) {
await removeKeyboard();
}
}

View file

@ -34,12 +34,14 @@
-->
<script src="../../../../../build/publish/debug/kmwuitoggle.js"></script>
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
keyman.init({
attachType:'auto'
});
</script>
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
keyman.init({
attachType: 'auto'
}).then(function () {
loadKeyboards();
});
</script>
<!-- Add keyboard management script for local selection of keyboards to use -->
<script src="./header.js"></script>
@ -47,7 +49,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards();'>
<body>
<h2>KeymanWeb: 'Test case' for proper implementation of the removeKeyboards API function</h2>
<div id='DynamicTextboxes'>

View file

@ -36,10 +36,11 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
});
}).then(async function () {
await loadKeyboards();
});
</script>
<!-- Add keyboard management script for local selection of keyboards to use -->
@ -48,7 +49,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards();'>
<body>
<h2>KeymanWeb Sample Page - Keyboard Registration Check</h2>
<p>
This page is designed to test KeymanWeb's behavior when being told to load a keyboard multiple times, both via

View file

@ -1,70 +1,64 @@
// Modified version of commonHeader - contains repeated requests for the same language.
/*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Modified version of commonHeader.js - contains repeated requests for the same language.
*/
function loadKeyboards()
{
var kmw=keyman;
// The first keyboard added will be the default keyboard for touch devices.
// For faster loading, it may be best for the default keyboard to be
// locally sourced.
kmw.addKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'},
filename:'../us-1.0.js'});
// Add more keyboards to the language menu, by keyboard name,
// keyboard name and language code, or just the BCP-47 language code.
kmw.addKeyboards('french','european2@sv','european2@sv','european2@sv','european2@sv','european2@sv','european2@sv','european2@no','@he');
kmw.addKeyboards('@he');
kmw.addKeyboards('@he');
// Add a keyboard by language name. Note that the name must be spelled
// correctly, or the keyboard will not be found. (Using BCP-47 codes is
// usually easier.)
kmw.addKeyboardsForLanguage('Dzongkha');
// Add a fully-specified, locally-sourced, keyboard with custom font
kmw.addKeyboards({id:'lao_2008_basic',name:'Lao Basic',
languages:{
id:'lo',name:'Lao',region:'Asia'
},
filename:'../lao_2008_basic.js'
});
async function loadKeyboards() {
await keyman.addKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'},
filename:'../us-1.0.js'});
// The following two optional calls should be delayed until language menus are fully loaded:
// (a) a specific mapped input element input is focused, to ensure that the OSK appears
// (b) a specific keyboard is loaded, rather than the keyboard last used.
//window.setTimeout(function(){kmw.setActiveElement('ta1',true);},2500);
//window.setTimeout(function(){kmw.setActiveKeyboard('Keyboard_french','fr');},3000);
// Note that locally specified keyboards will be listed before keyboards
// requested from the remote server by user interfaces that do not order
// keyboards alphabetically by language.
}
// Script to allow a user to add any keyboard to the keyboard menu
function addKeyboard(n)
{
var sKbd,kmw=keyman;
switch(n)
{
case 1:
sKbd=document.getElementById('kbd_id1').value;
kmw.addKeyboards(sKbd);
break;
case 2:
sKbd=document.getElementById('kbd_id2').value.toLowerCase();
kmw.addKeyboards('@'+sKbd);
break;
case 3:
sKbd=document.getElementById('kbd_id3').value;
kmw.addKeyboardsForLanguage(sKbd);
break;
}
}
// Add keyboard on Enter (as well as pressing button)
function clickOnEnter(e,id)
{
e = e || window.event;
if(e.keyCode == 13) addKeyboard(id);
}
// Intentionally request the same keyboard multiple times to test that it
// is not added multiple times and doesn't cause an error.
await keyman.addKeyboards('french', 'european2@sv', 'european2@sv', 'european2@sv',
'european2@sv', 'european2@sv', 'european2@sv', 'european2@no', '@he');
await keyman.addKeyboards('@he');
await keyman.addKeyboards('@he'); // intentional duplicate
// Add a keyboard by language name. Note that the name must be spelled
// correctly, or the keyboard will not be found. (Using BCP-47 codes is
// usually easier.)
await keyman.addKeyboardsForLanguage('Dzongkha');
// Add a fully-specified, locally-sourced, keyboard with custom font
await keyman.addKeyboards({
id: 'lao_2008_basic',
name: 'Lao Basic',
languages:{
id: 'lo',
name: 'Lao',
region: 'Asia'
},
filename: '../lao_2008_basic.js'
});
}
// Script to allow a user to add any keyboard to the keyboard menu
async function addKeyboard(n)
{
let sKbd;
switch(n)
{
case 1:
sKbd=document.getElementById('kbd_id1').value;
await keyman.addKeyboards(sKbd);
break;
case 2:
sKbd=document.getElementById('kbd_id2').value.toLowerCase();
await keyman.addKeyboards('@'+sKbd);
break;
case 3:
sKbd=document.getElementById('kbd_id3').value;
await keyman.addKeyboardsForLanguage(sKbd);
break;
}
}
// Add keyboard on Enter (as well as pressing button)
async function clickOnEnter(e,id) {
e = e || window.event;
if (e.keyCode == 13) {
await addKeyboard(id);
}
}

View file

@ -36,11 +36,10 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
});
kmw.addKeyboards({id:'issue115a',name:'Issue 115',languages:{id:'en',name:'English'},
keyman.addKeyboards({id:'issue115a',name:'Issue 115',languages:{id:'en',name:'English'},
filename:'./issue115a.js'});
</script>

View file

@ -36,12 +36,12 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
}).then(function() {
keyman.addKeyboards({id:'next_layer_tests',name:'Issue 116',languages:{id:'en',name:'English'},
filename:'./next_layer_tests.js'});
});
kmw.addKeyboards({id:'next_layer_tests',name:'Issue 116',languages:{id:'en',name:'English'},
filename:'./next_layer_tests.js'});
</script>

View file

@ -40,9 +40,8 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
attachType:'auto',
keyman.init({
attachType:'auto',
});
// Note: explicitly NOT deferred or waiting on the `init` Promise. This is

View file

@ -1,171 +1,107 @@
// JavaScript Document samplehdr.js: Keyboard management for KeymanWeb demonstration pages
/*
The keyboard name and/or BCP-47 language code must be specified for each keyboard that is to be available.
If the same keyboard is used for several languages, it must be listed for each
language, but the keyboard itself will only be loaded once.
If two (or more) keyboards are to be available for a given language, both must be listed.
Any number of keyboards may be specified in one or more calls.
Keyboard paths may be absolute (with respect to the server root) or relative to the keyboards option path.
The actual keyboard object will be downloaded asynchronously when first selected for use.
* Keyman is copyright (C) SIL Global. MIT License.
*/
Each argument to addKeyboards() is a string, for example:
european2 loads the current version of the Eurolatin 2 keyboard (for its default language)
european2@fr loads the current version of the Eurolatin 2 keyboard for French
european2@fr@1.2 loads version 1.2 of the Eurolatin 2 keyboard for French
Argument syntax also supports the following extensions:
@fr load the current version of the default keyboard for French
@fr$ load all available keyboards (current version) for French
Each call to addKeyboards() requires a single call to the remote server,
(unless all keyboards listed are local and fully specified) so it is better
to use multiple arguments rather than separate function calls.
Calling addKeyboards() with no arguments returns a list of *all* available keyboards.
The Toolbar (desktop browser) UI is best suited for allowing users to select
the appropriate language and keyboard in this case.
Keyboards may also be specified by language name using addKeyboardsForLanguage()
for example:
keymanweb.addKeyboardsForLanguage('Burmese');
Appending $ to the language name will again cause all available keyboards for that
language to be loaded rather than the default keyboard.
The first call to addKeyboardsForLanguage() makes an additional call to the
keyman API to load the current list of keyboard/language associations.
In this example, the following function loads the indicated keyboards,
and is called when the page loads.
*/
function errToString(err) {
// Painful? Kinda. But needed on un-updated Android API 21!
if(Array.isArray(err)) {
var result = '';
for(var i = 0; i < err.length; i++) {
var e = err[i];
if(e.error instanceof Error) {
result += e.error.message + '\n';
} else {
result += JSON.stringify(e) + '\n';
}
}
return result;
}
if(err instanceof Error) {
return err.message;
}
return JSON.stringify(err);
}
function doAddKeyboards(data) {
return keyman.addKeyboards(data).catch(function(err) {
console.error('keyman.addKeyboards failed with '+errToString(err)+' for '+JSON.stringify(data));
});
}
function doAddKeyboardsForLanguage(data) {
return keyman.addKeyboardsForLanguage(data).catch(function(err) {
console.error('keyman.addKeyboardsForLanguage failed with '+errToString(err)+' for '+JSON.stringify(data));
});
}
function loadKeyboards(nestLevel)
{
var kmw=keyman;
var base_prefix = '../';
var prefix = './'; // The default - when prefix == 0.
if(nestLevel !== undefined && nestLevel > 0) {
prefix = '';
for(var i=0; i < nestLevel; i++) {
prefix = prefix + base_prefix;
function errToString(err) {
// Painful? Kinda. But needed on un-updated Android API 21!
if(Array.isArray(err)) {
let result = '';
for(let i = 0; i < err.length; i++) {
const e = err[i];
if(e.error instanceof Error) {
result += e.error.message + '\n';
} else {
result += JSON.stringify(e) + '\n';
}
}
// The first keyboard added will be the default keyboard for touch devices.
// For faster loading, it may be best for the default keyboard to be
// locally sourced.
// doAddKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'},
// filename:(prefix + 'us-1.0.js')});
// Do NOT link the us-1.0 keyboard here!
doAddKeyboards({id:'test_chirality',name:'test_chirality',languages:{id:'en',name:'English'},
filename:(prefix + 'test_chirality.js')});
doAddKeyboards({id:'obolo_chwerty_6351',name:'obolo_chwerty_6351',languages:{id:'en',name:'English'},
filename:(prefix + 'obolo_chwerty_6351.js')});
doAddKeyboards({id:'gesture_prototyping',name:'Gesture Prototyping',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/gesture_prototyping/build/gesture_prototyping.js')});
doAddKeyboards({id:'diacritic_rota',name:'Diacritic 10-key Rota',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/diacritic_rota/build/diacritic_rota.js')});
doAddKeyboards({id:'ye_old_ten_key',name:'Classic 10-key',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/ye_old_ten_key/build/ye_old_ten_key.js')});
// Add more keyboards to the language menu, by keyboard name,
// keyboard name and language code, or just the BCP-47 language code.
// We use a different loading pattern here than in the samples version to provide a slightly different set of test cases.
doAddKeyboards('french','@he');
doAddKeyboards('khmer_angkor','@km');
doAddKeyboards({id:'sil_euro_latin', name:'SIL EuroLatin', languages: [{id:'no'}, {id:'sv'}]}); // Loads from partial stub instead of the compact string.
// Add a keyboard by language name. Note that the name must be spelled
// correctly, or the keyboard will not be found. (Using BCP-47 codes is
// usually easier.)
doAddKeyboardsForLanguage('Dzongkha');
// Add a fully-specified, locally-sourced, keyboard with custom font
doAddKeyboards({id:'lao_2008_basic',name:'Lao Basic',
languages: {
id:'lo',name:'Lao',region:'Asia',
},
filename:(prefix + 'lao_2008_basic-1.2.js')
});
// The following two optional calls should be delayed until language menus are fully loaded:
// (a) a specific mapped input element input is focused, to ensure that the OSK appears
// (b) a specific keyboard is loaded, rather than the keyboard last used.
//window.setTimeout(function(){kmw.setActiveElement('ta1',true);},2500);
//window.setTimeout(function(){kmw.setActiveKeyboard('Keyboard_french','fr');},3000);
// Note that locally specified keyboards will be listed before keyboards
// requested from the remote server by user interfaces that do not order
// keyboards alphabetically by language.
return result;
}
if(err instanceof Error) {
return err.message;
}
return JSON.stringify(err);
}
// Script to allow a user to add any keyboard to the keyboard menu
function addKeyboard(n)
{
var sKbd,kmw=keyman;
switch(n)
{
case 1:
sKbd=document.getElementById('kbd_id1').value;
doAddKeyboards(sKbd);
break;
case 2:
sKbd=document.getElementById('kbd_id2').value.toLowerCase();
doAddKeyboards('@'+sKbd);
break;
case 3:
// Add keyboard for comma-separated language name(s)
sKbd=document.getElementById('kbd_id3').value;
doAddKeyboardsForLanguage(sKbd);
break;
async function doAddKeyboards(data) {
return keyman.addKeyboards(data).catch(function(err) {
console.error('keyman.addKeyboards failed with '+errToString(err)+' for '+JSON.stringify(data));
});
}
async function doAddKeyboardsForLanguage(data) {
return keyman.addKeyboardsForLanguage(data).catch(function(err) {
console.error('keyman.addKeyboardsForLanguage failed with '+errToString(err)+' for '+JSON.stringify(data));
});
}
async function loadKeyboards(nestLevel) {
const base_prefix = '../';
let prefix = './'; // The default - when prefix == 0.
if(nestLevel !== undefined && nestLevel > 0) {
prefix = '';
for(let i=0; i < nestLevel; i++) {
prefix += base_prefix;
}
}
// Add keyboard on Enter (as well as pressing button)
function clickOnEnter(e,id)
{
e = e || window.event;
if(e.keyCode == 13) addKeyboard(id);
}
// Do NOT link the us-1.0 keyboard here!
await doAddKeyboards({id:'test_chirality',name:'test_chirality',languages:{id:'en',name:'English'},
filename:(prefix + 'test_chirality.js')});
await doAddKeyboards({id:'obolo_chwerty_6351',name:'obolo_chwerty_6351',languages:{id:'en',name:'English'},
filename:(prefix + 'obolo_chwerty_6351.js')});
await doAddKeyboards({id:'gesture_prototyping',name:'Gesture Prototyping',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/gesture_prototyping/build/gesture_prototyping.js')});
await doAddKeyboards({id:'diacritic_rota',name:'Diacritic 10-key Rota',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/diacritic_rota/build/diacritic_rota.js')});
await doAddKeyboards({id:'ye_old_ten_key',name:'Classic 10-key',languages:{id:'en',name:'English'},
filename:(prefix + 'keyboards/ye_old_ten_key/build/ye_old_ten_key.js')});
await doAddKeyboards('french', '@he');
await doAddKeyboards('khmer_angkor','@km');
await doAddKeyboards({id:'sil_euro_latin', name:'SIL EuroLatin', languages: [{id:'no'}, {id:'sv'}]}); // Loads from partial stub instead of the compact string.
await doAddKeyboardsForLanguage('Dzongkha');
await doAddKeyboards({
id: 'lao_2008_basic', name: 'Lao Basic',
languages: {
id:'lo',name:'Lao',region:'Asia',
},
filename:(prefix + 'lao_2008_basic-1.2.js')
});
}
// Script to allow a user to add any keyboard to the keyboard menu
async function addKeyboard(n) {
let sKbd;
switch(n)
{
case 1:
sKbd=document.getElementById('kbd_id1').value;
await doAddKeyboards(sKbd);
break;
case 2:
sKbd=document.getElementById('kbd_id2').value.toLowerCase();
await doAddKeyboards('@'+sKbd);
break;
case 3:
// Add keyboard for comma-separated language name(s)
sKbd=document.getElementById('kbd_id3').value;
await doAddKeyboardsForLanguage(sKbd);
break;
}
}
// Add keyboard on Enter (as well as pressing button)
async function clickOnEnter(e,id) {
e = e || window.event;
if (e.keyCode == 13) {
await addKeyboard(id);
}
}

View file

@ -1,13 +1,11 @@
function loadKeyboards()
{
var kmw=keyman;
// Add a fully-specified, locally-sourced, keyboard with custom font
kmw.addKeyboards({id:'sil_cameroon_qwerty',name:'Cameroon QWERTY (SIL)',
function loadKeyboards()
{
// Add a fully-specified, locally-sourced, keyboard with custom font
keyman.addKeyboards({id:'sil_cameroon_qwerty',name:'Cameroon QWERTY (SIL)',
languages:{
id:'aal-Latn',name:'Afade (Latin)',region:'Africa',
font:{family:'AndikaAfr',source:['./ANDIKAAFR-R.TTF']}
},
filename:'./sil_cameroon_qwerty.js'
});
});
}

View file

@ -36,9 +36,10 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType: 'auto'
}).then(function() {
loadKeyboards();
});
</script>
@ -49,7 +50,7 @@
</head>
<!-- Sample page HTML -->
<body onload='loadKeyboards();'>
<body>
<h2>KeymanWeb Sample Page - Mixed Case of TTF font files</h2>
<p>Previously, KMW was not applying .TTF fonts on mobile devices.</p>
<p>See <a href='https://github.com/keymanapp/keyman/issues/1332'>issue #1332</a>

View file

@ -36,12 +36,12 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
});
kmw.addKeyboards({
id:'issue160',name:'Issue 160',languages:{id:'en',name:'English'}, filename:'./issue160.js'
}).then(function() {
keyman.addKeyboards({
id: 'issue160', name: 'Issue 160', languages: { id: 'en', name: 'English' }, filename: './issue160.js'
});
});
</script>

View file

@ -36,12 +36,12 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
var kmw=window.keyman;
kmw.init({
keyman.init({
attachType:'auto'
});
kmw.addKeyboards({
id:'issue266',name:'Issue 266',languages:{id:'en',name:'English'}, filename:'./issue266.js'
}).then(function() {
keyman.addKeyboards({
id:'issue266',name:'Issue 266',languages:{id:'en',name:'English'}, filename:'./issue266.js'
});
});
</script>

Some files were not shown because too many files have changed in this diff Show more