Merge pull request #16198 from keymanapp/auto/A19S32-merge-base-into-kmc-test
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled

auto: A19S32 merge epic/web-core into kmc-test ↙️
This commit is contained in:
Keyman Server 2026-07-04 16:07:43 +02:00 • committed by GitHub
commit b1ecff7f9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
203 changed files with 1692 additions and 2556 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

@ -81,7 +81,9 @@ jobs:
} else if(status.context == 'Ubuntu Packaging') {
summary += addStatus(o, 'build', status.context, status.state);
} else if(status.context == 'Linux source verification') {
summary += addStatus(o, 'build', status.context, status.state);
// Ignore Linux source verification -- we won't block automerge
// for this at this point
summary += addLog(`Skipping ${status.context}`);
} else if(status.context == 'npm pack/publish') {
summary += addStatus(o, 'build', status.context, status.state);
} else if(status.context == 'Keyman Core - ARM64 test') {

View file

@ -1,5 +1,67 @@
# 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)
* docs(web): reformat and small fixes (#16021)
## 19.0.239 alpha 2026-05-27
* chore(web): remove obsolete file (#16013)
## 19.0.238 alpha 2026-05-26
* maint(linux): Don't block merge if source verification check fails (#16010)
* maint(linux): fix Linux source tarball (#16011)
## 19.0.237 alpha 2026-05-22
* fix(windows): Ensure default locale always appears in list of locales (#15984)
* chore(ios): update First Voices distribution certificate (#15993)
## 19.0.236 alpha 2026-05-21
* docs(developer): write up basic internal docs on kmc modules (#15982)

View file

@ -1 +1 @@
19.0.237
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

@ -11,6 +11,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
builder_describe "Keyman common web modules" \
:keyman-version \
:langtags \
:sentry-manager \
:types \
clean \
configure \

View file

@ -11,7 +11,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
builder_describe "Sentry error reporting module for embedded Keyman Engine for Web" \
"@/common/tools/es-bundling" \
"@/common/web/keyman-version" \
clean configure build
clean configure build test
builder_describe_outputs \
configure /node_modules \
@ -22,7 +22,7 @@ builder_parse "$@"
# ---------------------------------------------------------------------------------
function do_build() {
tsc --build "./tsconfig.json"
tsc --build
node_es_bundle "${KEYMAN_ROOT}/common/web/sentry-manager/build/obj/src/index.js" \
--out "${KEYMAN_ROOT}/common/web/sentry-manager/build/lib/index.js" \

View file

@ -44,6 +44,7 @@ type
class function KeyboardsInstallDir: string; static;
class function KeymanConfigStaticHttpFilesPath(const filename: string = ''): string; static;
class function KeymanCustomisationPath: string; static;
class function KeymanLocalePath: string; static;
class function KeymanCoreLibraryPath(const Filename: string): string; static;
class function CEFPath: string; static; // Chromium Embedded Framework
class function CEFDataPath(const mode: string): string; static;
@ -389,6 +390,20 @@ begin
Result := Result + filename;
end;
class function TKeymanPaths.KeymanLocalePath: string;
var
keyman_root: string;
begin
// Look up KEYMAN_ROOT development variable -- if found and executable
// within that path then use that as source path
if TKeymanPaths.RunningFromSource(keyman_root) then
begin
Exit(keyman_root + 'windows\src\desktop\kmshell\locale\');
end;
Result := TKeymanPaths.KeymanDesktopInstallPath('locale\');
end;
class function TKeymanPaths.KeymanCustomisationPath: string;
var
keyman_root: string;

View file

@ -30,6 +30,17 @@ const
TIER_STABLE = 'stable';
const
ENVIRONMENT_TEST = 'test';
ENVIRONMENT_LOCAL = 'local';
ENVIRONMENT_ALPHA = 'alpha';
ENVIRONMENT_BETA = 'beta';
ENVIRONMENT_STABLE = 'stable';
const
SKeymanVersion190 = '19.0';
SKeymanVersion180 = '18.0';
SKeymanVersion170 = '17.0';
SKeymanVersion160 = '16.0';
SKeymanVersion150 = '15.0';
SKeymanVersion140 = '14.0';
SKeymanVersion130 = '13.0';

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

@ -37,7 +37,7 @@ km_core_event(
return KM_CORE_STATUS_INVALID_ARGUMENT;
}
km_core_status status = const_cast<km_core_state*>(state)->processor().external_event( const_cast<km_core_state*>(state), event, data);
km_core_status status = const_cast<km_core_state*>(state)->processor().external_event(const_cast<km_core_state*>(state), event, data);
const_cast<km_core_state*>(state)->apply_actions_and_merge_app_context();
return status;
}

View file

@ -1,185 +0,0 @@
/*
* Keyman is copyright (C) SIL International. MIT License.
*
* Keyman Keyboard Processor API - On-Screen Keyboard Layout Interfaces
*/
#pragma once
#include <string>
#include <map>
#include <vector>
#include "keyman_core_api.h"
#if defined(__cplusplus)
extern "C" {
#endif
/**
* Possible directions of a flick
*/
enum keyboard_layout_flick_direction {
/** flick up (north) */
n = 0,
/** flick down (south) */
s = 1,
/** flick right (east) */
e = 2,
/** flick left (west) */
w = 3,
/** flick up-right (north-east) */
ne = 4,
/** flick up-left (north-west) */
nw = 5,
/** flick down-right (south-east) */
se = 6,
/** flick down-left (south-west) */
sw = 7
};
/**
* key type like regular key, framekeys, deadkeys, blank, etc.
*/
enum keyboard_layout_key_type {
/** regular key */
normal = 0,
/** A 'frame' key, such as Shift or Enter */
special = 1,
/** A 'frame' key, such as Shift or Enter, which is is active, such as
* the shift key on a shift layer */
specialActive = 2,
/** **KeymanWeb runtime private use:** a variant of `special` with the
* keyboard font rather than 'KeymanwebOsk' font */
customSpecial = 3,
/** **KeymanWeb runtime private use:** a variant of `specialActive` with the
* keyboard font rather than 'KeymanwebOsk' font. */
customSpecialActive = 4,
/** A deadkey */
deadkey = 8,
/** A key which is rendered as a blank keycap, should block any interaction */
blank = 9,
/** Renders the key only as a gap or spacer, should block any interaction */
spacer = 10
};
/**
* A key on a touch layout/on-screen keyboard
*/
struct keyboard_layout_key {
/** key id */
std::u16string id; // TODO-WEB-CORE: perhaps necessary for special keys, Enter, etc? or can we get that from virtualKey?
/** the virtual key code */
int virtualKey; // TODO-WEB-CORE: do we need this? both id and virtualKey? Or just one of them?
/** text to display on key cap */
std::u16string display;
/** hint e.g. for longpress */
std::u16string hint;
/** the type of key */
keyboard_layout_key_type type;
/**
* the modifier combination (not layer) that should be used in key events,
* for this key, overriding the layer that the key is a part of.
*/
int modifiersOverride;
/** the next layer to switch to after this key is pressed */
std::u16string nextLayerId;
// touch layouts only
/** padding - space to the left of key (in what units?) */
int gap;
/** width of the key (in what units?) */
int width;
/** longpress keys, also known as subkeys */
std::vector<keyboard_layout_key> longpresses;
/** multitaps */
std::vector<keyboard_layout_key> multiTaps;
/** flicks */
std::map<keyboard_layout_flick_direction, keyboard_layout_key> flicks;
};
/**
* a row of keys on a touch layout/on-screen keyboard
*/
struct keyboard_layout_row {
/** row id */
int id; // TODO-WEB-CORE: do we need this? Web has it (`TouchLayoutRow`)
/** keys in this row */
std::vector<keyboard_layout_key> keys;
};
/**
* a layer with rows of keys on a touch layout/on-screen keyboard
*/
struct keyboard_layout_layer {
/** layer id */
std::u16string id;
/** layer modifiers */
// TODO-WEB-CORE: we added this during our discussion, but Web doesn't have it.
// Should be an enum if it's needed.
int modifiers; //? 0 = default, n = shift, etc. -1 = unspecified?
/** rows in this layer */
std::vector<keyboard_layout_row> rows;
};
/**
* layout specification for a specific platform like desktop, phone or tablet
*/
struct keyboard_layout_platform {
/** platform form factor, e.g. 'iso', 'touch', 'ansi', ... (see ldml spec) */
std::u16string form;
/** width of screen for touch layout */
int minWidthMm; // we don't have mobile vs tablet, instead use this
/** layers for this platform */
std::vector<keyboard_layout_layer> layers;
// TODO-WEB-CORE: Do we need these:
// Web additionally has:
// - font (should be in CSS; we have it in `keyboard_layout`)
// - fontsize (should be in CSS; we have it in `keyboard_layout`)
// - displayUnderlying
// - defaultHint ("none"|"dot"|"longpress"|"multitap"|"flick"|"flick-n"|"flick-ne"|
// "flick-e"|"flick-se"|"flick-s"|"flick-sw"|"flick-w"|"flick-nw")
};
/**
* On screen keyboard description consisting of specific layouts for different
* form factors.
*/
struct keyboard_layout {
/** layouts for different form factors */
std::vector<keyboard_layout_platform> platforms;
/** font face name to use for key caps*/
std::string fontFacename;
/** font size to use for key caps */
int fontSizeEm; // TODO-WEB-CORE: em? px? something else?
};
/**
* Get the on-screen keyboard layout for the specified keyboard.
*
* @param keyboard [in] The keyboard to get the layout for.
* @param layout [out] The on-screen keyboard layout.
* @return km_core_status `KM_CORE_STATUS_OK`: On success.
* `KM_CORE_STATUS_INVALID_ARGUMENT`: If `keyboard` is not a valid keyboard or `layout` is null.
*/
km_core_status
keyboard_get_layout(
km_core_keyboard const* keyboard,
keyboard_layout** layout
);
/**
* Dispose the on-screen keyboard layout.
*/
void
keyboard_layout_dispose(keyboard_layout* layout);
#if defined(__cplusplus)
}
#endif

View file

@ -207,7 +207,7 @@ if cpp_compiler.get_id() == 'emscripten'
name_suffix: 'mjs')
if get_option('buildtype') == 'release'
# TODO: #12888
# TODO-WEB-CORE
# Split debug symbols into separate wasm file for release builds only
# as the release symbols will be uploaded to sentry
# custom_target('core.wasm',

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

@ -36,6 +36,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,7 +197,28 @@ export class InfrastructureMessages {
`Failed to generate new project '${def(o.id)}'.`,
)});
static ERROR_InvalidTargetVersion = SevError | 0x0029;
// 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.`
)});
static ERROR_InvalidTargetVersion = SevError | 0x002C;
static Error_InvalidTargetVersion = (o:{targetVersion:string}) => m(
this.ERROR_InvalidTargetVersion,
`Target version parameter '${def(o.targetVersion)}' is not a valid Keyman version.`,

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

@ -10,6 +10,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
*/
@ -206,6 +212,7 @@ export function commanderOptionsToCompilerOptions(options: any, callbacks: Compi
targetVersion,
// 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

@ -233,7 +233,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>

View file

@ -71,7 +71,7 @@ After this, you can access the website at the following ports:
| status.keyman.com* | http://localhost:8060 | http://status-backend.keyman.com.localhost | status-keyman-website |
| | http://localhost:8061 | http://status.keyman.com.localhost | status-keyman-public |
\* Note that status.keyman.com runs in two containers in development to allow for live reload and separation of logs for backend and frontend.
\* Note that status.keyman.com runs in two containers in development to allow for live reload and separation of logs for backend and frontend.
In production, only port 8060 is used.
#### Remove the Docker container and image
@ -95,6 +95,55 @@ docker logs -f {Docker Container Name}
Refer to **Port lookup table** above for Docker container names
---------
## How to run composer updates
The Docker containers are setup with multi-stage images, so you will need to
target the composer-builder stage if you want to execute composer commands such
as `composer update` or `composer audit`. The following scripts show how to do
this; here the same id is used for the image tag and the container name for simplicity:
On macOS, Linux:
```bash
COMPOSER_ID=composer-temp
# setup
docker build -f Dockerfile --target composer-builder --tag $COMPOSER_ID .
docker run -v "$(pwd):/var/www/html/" --name $COMPOSER_ID --user root --rm -d $COMPOSER_ID
# run commands with `docker exec`, e.g.:
# docker exec $COMPOSER_ID composer audit
# docker exec $COMPOSER_ID composer update
# docker exec $COMPOSER_ID composer update --lock
# copy modified files to mounted volume:
docker exec $COMPOSER_ID cp composer.lock /var/www/html/
docker exec $COMPOSER_ID cp composer.json /var/www/html/
# cleanup
docker stop $COMPOSER_ID
docker rmi $COMPOSER_ID
```
On git bash for Windows, paths must start with `//`, e.g. `//$(pwd)`, `//var/www/html/`:
```bash
COMPOSER_ID=composer-temp
# setup
docker build -f Dockerfile --target composer-builder --tag $COMPOSER_ID .
docker run -v "//$(pwd):/var/www/html/" --name $COMPOSER_ID --user root --rm -d $COMPOSER_ID
# run commands with `docker exec`, e.g.:
# docker exec $COMPOSER_ID composer audit
# docker exec $COMPOSER_ID composer update
# docker exec $COMPOSER_ID composer update --lock
# copy modified files to mounted volume:
docker exec $COMPOSER_ID cp composer.lock //var/www/html/
docker exec $COMPOSER_ID cp composer.json //var/www/html/
# cleanup
docker stop $COMPOSER_ID
docker rmi $COMPOSER_ID
```
---------
## Website Dependencies

View file

@ -15,6 +15,101 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
# shellcheck disable=SC2154
. "${KEYMAN_ROOT}/linux/scripts/package-build.inc.sh"
create_tarball() {
# Include these files and folders:
# shellcheck disable=2034 # to_include appears to be unused, even though
# it is used indirectly by generate_tar_ignore_list.
to_include=(
common/build.sh \
common/cpp \
common/include \
common/linux \
common/test/keyboards/baseline \
core \
linux \
resources/build/*.sh \
resources/build/meson \
resources/standards-data \
resources/*.sh \
./*.md \
./build.sh \
./*.json \
)
# files and subfolders to exclude from paths included in 'to_include',
# i.e. the exceptions to 'to_include'.
# shellcheck disable=2034 # to_exclude appears to be unused, even though
# it is used indirectly by generate_tar_ignore_list.
to_exclude=(
build \
common/test/keyboards/baseline/kmcomp-*.zip \
linux/builddebs \
linux/docs/help \
linux/keyman-config/keyman_config/version.py \
linux/keyman-config/buildtools/build-langtags.py \
linux/upload \
)
# array to store list of --tar-ignore parameters generated from to_include and to_exclude.
ignored_files=()
generate_tar_ignore_list "./" to_include to_exclude ignored_files "$(basename "${KEYMAN_ROOT}")"
# Note: explicitly specify the --tar-ignores here for files/folders that we always
# want to ignore regardless of their location. Having them here allows us to pass
# the wildcards to dpkg-source - whereas the wildcards in 'to_exclude' will be
# resolved and replaced with multiple --tar-ignore entries.
dpkg-source \
--tar-ignore=*~ \
--tar-ignore=.git \
--tar-ignore=.gitattributes \
--tar-ignore=.gitignore \
--tar-ignore=experiments \
--tar-ignore=debian \
--tar-ignore=.github \
--tar-ignore=.vscode \
--tar-ignore=.configured \
--tar-ignore=.devcontainer \
--tar-ignore=.pc \
--tar-ignore=__pycache__ \
--tar-ignore=node_modules \
--tar-ignore=keyman_1* \
--tar-ignore=launchpad \
--tar-ignore=dist \
--tar-ignore=VERSION \
\
"${ignored_files[@]}" \
\
--compression=xz --build .
mv ../keyman_"${KEYMAN_VERSION}".tar.xz linux/dist/keyman-"${KEYMAN_VERSION}".tar.xz
}
replace_toplevel_buildsh() {
# extract the tarball and replace top-level build.sh, then recreate tarball
builder_echo heading "Replacing top-level build.sh"
cd "${KEYMAN_ROOT}/linux/dist"
tar xfJ keyman-"${KEYMAN_VERSION}".tar.xz
cat > "keyman/build.sh" << EOF
#!/usr/bin/env bash
linux/build.sh "\$@"
EOF
chmod +x "keyman/build.sh"
tar cfJ "keyman-${KEYMAN_VERSION}.tar.xz" "keyman"
rm -rf "keyman"
}
create_debian_origtarxz() {
builder_echo heading "Creating Debian orig.tar.xz"
cd "${KEYMAN_ROOT}/linux/dist"
pkgvers="keyman-${KEYMAN_VERSION}"
tar xfJ keyman-"${KEYMAN_VERSION}".tar.xz
mv -v keyman "${pkgvers}" 2>/dev/null || mv -v "$(find . -mindepth 1 -maxdepth 1 -type d)" "${pkgvers}"
tar cfJ "keyman_${KEYMAN_VERSION}.orig.tar.xz" "${pkgvers}"
rm "keyman-${KEYMAN_VERSION}.tar.xz"
rm -rf "${pkgvers}"
}
BASEDIR=$(pwd)
cd "${KEYMAN_ROOT}/linux"
@ -24,6 +119,8 @@ if [[ ! -z ${1+x} ]] && [[ "$1" == "origdist" ]]; then
shift
fi
builder_echo heading "Creating source tarball for Keyman ${KEYMAN_VERSION}"
rm -rf dist
mkdir -p dist
@ -34,130 +131,13 @@ echo "3.0 (native)" > debian/source/format
# shellcheck disable=SC2154
dch keyman --newversion "${KEYMAN_VERSION}" --force-bad-version --nomultimaint
# Create the tarball
# We always include these files which are the minimum files and
# folder required for Ubuntu/Debian packaging
# shellcheck disable=2034 # to_include appears to be unused
to_include=(
common/build.sh \
common/cpp \
common/include \
common/linux \
common/test/keyboards/baseline \
core \
linux \
resources/build/*.sh \
resources/build/meson \
resources/standards-data \
resources/*.sh \
./*.md \
./build.sh \
./*.json \
)
# files and subfolders to exclude from paths included in 'to_include',
# i.e. the exceptions to 'to_include'.
# shellcheck disable=2034 # to_exclude appears to be unused
to_exclude=(
build \
common/test/keyboards/baseline/kmcomp-*.zip \
linux/builddebs \
linux/docs/help \
linux/keyman-config/keyman_config/version.py \
linux/keyman-config/buildtools/build-langtags.py \
linux/upload \
)
if [[ -z "${create_origdist+x}" ]]; then
# If we build a full source tarball we include additional files
# so that it's possible to run `${KEYMAN_ROOT}/build.sh` on Linux
# shellcheck disable=2034 # to_include appears to be unused
to_include+=(
common/schemas \
common/tools/hextobin \
common/web/keyman-version \
common/web/langtags \
common/web/types \
common/windows/cpp \
common/windows/include \
developer/src/common/include \
developer/src/common/web \
developer/src/ext/json \
developer/src/kmc \
developer/src/kmc-analyze \
developer/src/kmc-copy \
developer/src/kmc-generate \
developer/src/kmc-keyboard-info \
developer/src/kmc-kmn \
developer/src/kmc-ldml \
developer/src/kmc-model \
developer/src/kmc-model-info \
developer/src/kmc-package \
developer/src/kmcmplib \
docs/minimum-versions.md.in
resources/build \
resources/standards-data \
)
# additional files and subfolders to exclude from paths included in 'to_include',
# i.e. the exceptions to 'to_include'.
# shellcheck disable=2034 # to_exclude appears to be unused
to_exclude+=(
*.exe \
resources/build/history \
resources/build/l10n \
resources/build/mac \
resources/build/win \
resources/build/*.lua \
)
fi
# array to store list of --tar-ignore parameters generated from to_include and to_exclude.
ignored_files=()
generate_tar_ignore_list "./" to_include to_exclude ignored_files "$(basename "${KEYMAN_ROOT}")"
# Note: explicitly specify the --tar-ignores here for files/folders that we always
# want to ignore regardless of their location. Having them here allows us to pass
# the wildcards to dpkg-source - whereas the wildcards in 'to_exclude' will be
# resolved and replaced with multiple --tar-ignore entries.
dpkg-source \
--tar-ignore=*~ \
--tar-ignore=.git \
--tar-ignore=.gitattributes \
--tar-ignore=.gitignore \
--tar-ignore=experiments \
--tar-ignore=debian \
--tar-ignore=.github \
--tar-ignore=.vscode \
--tar-ignore=.configured \
--tar-ignore=.devcontainer \
--tar-ignore=.pc \
--tar-ignore=__pycache__ \
--tar-ignore=node_modules \
--tar-ignore=keyman_1* \
--tar-ignore=launchpad \
--tar-ignore=dist \
--tar-ignore=VERSION \
\
"${ignored_files[@]}" \
\
-Zxz -b .
mv ../keyman_"${KEYMAN_VERSION}".tar.xz linux/dist/keyman-"${KEYMAN_VERSION}".tar.xz
create_tarball
echo "3.0 (quilt)" > debian/source/format
cd "${BASEDIR}"
replace_toplevel_buildsh
# create orig.tar.xz
if [[ ! -z "${create_origdist+x}" ]]; then
cd "${KEYMAN_ROOT}/linux/dist"
pkgvers="keyman-${KEYMAN_VERSION}"
tar xfJ keyman-"${KEYMAN_VERSION}".tar.xz
mv -v keyman "${pkgvers}" 2>/dev/null || mv -v "$(find . -mindepth 1 -maxdepth 1 -type d)" "${pkgvers}"
tar cfJ "keyman_${KEYMAN_VERSION}.orig.tar.xz" "${pkgvers}"
rm "keyman-${KEYMAN_VERSION}.tar.xz"
rm -rf "${pkgvers}"
create_debian_origtarxz
fi
cd "${BASEDIR}"

View file

@ -32,7 +32,7 @@ function downloadSource() {
cd ..
mv "keyman-${version}" "${KEYMAN_ROOT}/linux/${packageDir}"
mv "keyman_${version}.orig.tar.xz" "${KEYMAN_ROOT}/linux/${packageDir}"
mv "keyman_${version}.pkg.tar.xz" "${KEYMAN_ROOT}/linux/${packageDir}"
mv "keyman-${version}.tar.xz" "${KEYMAN_ROOT}/linux/${packageDir}"
mv "keyman"*.asc "${KEYMAN_ROOT}/linux/${packageDir}"
rm "keyman"*.debian.tar.xz
cd "${KEYMAN_ROOT}/linux/${packageDir}" || exit

View file

@ -36,10 +36,6 @@ create_source_tarball() {
./scripts/reconf.sh
PKG_CONFIG_PATH="${KEYMAN_ROOT}/core/build/arch/release/meson-private" ./scripts/dist.sh
mv "dist/keyman-${KEYMAN_VERSION}.tar.xz" "${target_dir}"
builder_echo heading "Make source for packaging"
PKG_CONFIG_PATH="${KEYMAN_ROOT}/core/build/arch/release/meson-private" ./scripts/dist.sh origdist
mv "dist/keyman_${KEYMAN_VERSION}.orig.tar.xz" "${target_dir}/keyman_${KEYMAN_VERSION}.pkg.tar.xz"
}
extract_source_tarball() {
@ -51,15 +47,6 @@ extract_source_tarball() {
tar -xvf "keyman-${KEYMAN_VERSION}.tar.xz"
}
extract_packaging_source_tarball() {
local target_dir="$1"
builder_echo heading "Extract packaging source tarball"
cd "${target_dir}"
rm -rf "keyman-${KEYMAN_VERSION}"
tar -xvf "keyman_${KEYMAN_VERSION}.pkg.tar.xz"
}
verify_can_build() {
local target_dir="$1"
builder_echo heading "Verifying build of tarball"
@ -81,7 +68,7 @@ create_source_package() {
cd launchpad
cp -r "../keyman-${KEYMAN_VERSION}" .
cp -r "${KEYMAN_ROOT}/linux/debian" "keyman-${KEYMAN_VERSION}"
cp "${target_dir}/keyman_${KEYMAN_VERSION}.pkg.tar.xz" "keyman_${KEYMAN_VERSION}.orig.tar.xz"
cp "${target_dir}/keyman-${KEYMAN_VERSION}.tar.xz" "keyman_${KEYMAN_VERSION}.orig.tar.xz"
"keyman-${KEYMAN_VERSION}/linux/scripts/launchpad.sh" --no-download \
--dist "$(lsb_release -c -s)" --outputdir "${target_dir}/launchpad" --no-lintian --no-sign
}
@ -124,7 +111,8 @@ fi
if ! builder_has_option --source-only; then
builder_echo start lintian "Verifying Launchpad source package"
extract_packaging_source_tarball "${TARGET_DIR}"
extract_source_tarball "${TARGET_DIR}"
mv "${TARGET_DIR}/keyman" "${TARGET_DIR}/keyman-${KEYMAN_VERSION}"
create_source_package "${TARGET_DIR}"
verify_lintian
rm -rf "${TARGET_DIR}/keyman-${KEYMAN_VERSION}"

View file

@ -1,3 +1,3 @@
version=4
# Tier replaced by package-build.inc.sh script
opts=pgpsigurlmangle=s/$/.asc/ https://downloads.keyman.com/linux/$tier/@ANY_VERSION@/@PACKAGE@@ANY_VERSION@.pkg@ARCHIVE_EXT@ debian uupdate
opts=pgpsigurlmangle=s/$/.asc/ https://downloads.keyman.com/linux/$tier/@ANY_VERSION@/@PACKAGE@@ANY_VERSION@@ARCHIVE_EXT@ debian uupdate

View file

@ -7,9 +7,9 @@
<key>teamID</key>
<string>D7TR486TEH</string>
<key>signingCertificate</key>
<string>D7B7973A30C90C1A04666667F24B4D5C7E913923</string>
<string>D2FEB39A439B44ACC1E5A710D20C17587EEE29E1</string>
<key>installerSigningCertificate</key>
<string>D7B7973A30C90C1A04666667F24B4D5C7E913923</string>
<string>D2FEB39A439B44ACC1E5A710D20C17587EEE29E1</string>
<key>provisioningProfiles</key>
<dict>
<key>com.firstvoices.keyboards</key>

View file

@ -833,11 +833,6 @@ keyboard that you use in Windows. Keyman for FirstVoices will adapt automatical
<!-- Introduced: 7.0.230.0 -->
<string name="SKLanguageCode" comment="The language code for the current translation">en</string>
<!-- Context: _LanguageInfo -->
<!-- String Type: FormatString -->
<!-- Introduced: 7.0.230.0 -->
<string name="SKDefaultLanguageCode" comment="The default language code for this product. This should be the language that the product is created in. For Keyman this must stay 'en', for all translations.">en</string>
<!-- Context: Formatted Messages -->
<!-- String Type: FormatString -->
<!-- Introduced: 7.0.230.0 -->

147
package-lock.json generated
View file

@ -1148,7 +1148,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",
@ -5195,22 +5195,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",
@ -5221,6 +5222,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"
}
@ -5229,14 +5231,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",
@ -5494,6 +5542,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",
@ -6266,6 +6315,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",
@ -7726,14 +7776,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",
@ -7752,7 +7802,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",
@ -7802,40 +7852,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",
@ -8561,6 +8577,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"
},
@ -11680,12 +11697,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"
@ -11740,6 +11757,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",
@ -12169,6 +12187,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",
@ -12236,14 +12255,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"
@ -14153,7 +14174,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

@ -61,15 +61,11 @@ function _make_release_source_tarball() {
./scripts/reconf.sh
PKG_CONFIG_PATH="${KEYMAN_ROOT}/core/build/arch/release/meson-private" ./scripts/dist.sh
mv dist/*.tar.xz "upload/${KEYMAN_VERSION}/"
builder_echo heading "Make source for packaging"
PKG_CONFIG_PATH="${KEYMAN_ROOT}/core/build/arch/release/meson-private" ./scripts/dist.sh origdist
mv "dist/keyman_${KEYMAN_VERSION}.orig.tar.xz" "dist/keyman_${KEYMAN_VERSION}.pkg.tar.xz"
mv dist/*.tar.xz "upload/${KEYMAN_VERSION}/"
(
cd "upload/${KEYMAN_VERSION}"
sha256sum ./*.tar.xz > SHA256SUMS
builder_echo end "make source tarball" success "Make source tarball"
)
builder_echo end "make source tarball" success "Make source tarball"
}
function _sign_source_tarball() {
@ -100,7 +96,6 @@ function _publish_to_downloads() {
chmod a+r "${UPLOAD_DIR}"/*
write_download_info "${UPLOAD_DIR}" "keyman-${KEYMAN_VERSION}.tar.xz" "Keyman for Linux source tarball" tar.xz linux
write_download_info "${UPLOAD_DIR}" "keyman_${KEYMAN_VERSION}.pkg.tar.xz" "Keyman for Linux source for packaging" tar.xz linux
tc_rsync_upload "${UPLOAD_DIR}" "linux/${KEYMAN_TIER}"
builder_echo end "publish to downloads" success "Publish to downloads.keyman.com"

View file

@ -1,5 +0,0 @@
Tavultesoft KeymanWeb 2.0
Copyright 2005-2014 Tavultesoft Pty Ltd
This product includes software developed at
Tavultesoft (http://www.tavultesoft.com/).

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
@ -26,7 +26,9 @@ keyman.addKeyboards(spec[, spec...])
fulfilled upon adding keyboards.
The promise is an array containing a combination of the following:
* successfully registered keyboard objects which define some or all of these [properties](../keyboard_properties)
* successfully registered keyboard objects which define some or all of these
[properties](../keyboard_properties)
* [ErrorStub](../keyboard_registration_errors) objects for keyboards that failed to register
## Description
@ -35,7 +37,8 @@ The keyboard spec can be a string or an object. Multiple keyboard specs can be
specified in a single call, which can reduce the round-trip cost of multiple
calls to Keyman Cloud servers (when using Keyman Cloud).
For general information and example uses of this method, please see the [Adding Keyboards](../../guide/adding-keyboards) page from the guide section.
For general information and example uses of this method, please see the
[Adding Keyboards](../../guide/adding-keyboards) page from the guide section.
### Using a `string`
@ -48,7 +51,7 @@ The string format is one of the following:
* `'keyboardID@languageID'`: Loads a specific keyboard + language combination
The keyboard catalogue is online at
[http://keyman.com/developer/keymanweb/keyboards](http://keyman.com/developer/keymanweb/keyboards).
[https://keyman.com/developer/keymanweb/keyboards](https://keyman.com/developer/keymanweb/keyboards).
### Using an `object`
@ -59,7 +62,6 @@ known as KeymanWeb Server Data API):
The `spec` object contains the following members:
`name`
: `string`
@ -143,7 +145,7 @@ The `spec.languages` object contains the following members:
: `array|object` <span class="optional">optional</span>
An array of Font objects (see definition below) or single object describing
An array of Font objects (see `font` definition below) or single object describing
fonts for input fields and the OSK (if `oskFont` is not present.)
`oskFont`
@ -177,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
@ -18,7 +18,8 @@ keyman.addKeyboardsForLanguage(spec[, spec...])
: Type: `string`
Language name string. Appending `$` to the language name will cause all available keyboards for that language to be loaded rather than the default keyboard.
Language name string. Appending `$` to the language name will cause all available keyboards for
that language to be loaded rather than only the default keyboard.
### Return Value
@ -26,7 +27,9 @@ keyman.addKeyboardsForLanguage(spec[, spec...])
fulfilled upon adding keyboards.
The promise is an array containing the following:
* successfully registered keyboard objects which define some or all of these [properties](../keyboard_properties)
* successfully registered keyboard objects which define some or all of these
[properties](../keyboard_properties)
## Description
@ -34,9 +37,12 @@ The language spec is a string. Multiple language specs can also be
specified in a single call, which can reduce the round-trip cost of multiple
calls to Keyman Cloud servers (when using Keyman Cloud).
The first call to `addKeyboardsForLanguage()` makes an additional call to the Keyman API to load the current list of keyboard/language associations. This determines the default keyboards that are added for the language.
The first call to `addKeyboardsForLanguage()` makes an additional call to the Keyman API to load
the current list of keyboard/language associations. This determines the default keyboards that
are added for the language.
For general information and example uses of this method, please see the [Adding Keyboards](../../guide/adding-keyboards) page from the guide section.
For general information and example uses of this method, please see the
[Adding Keyboards](../../guide/adding-keyboards) page from the guide section.
### Using a `string`

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

@ -117,7 +117,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);
}
/**

View file

@ -260,12 +260,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

@ -151,12 +151,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']) {
@ -541,8 +541,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);
}
/* ----------------------------------------
@ -554,24 +564,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;
@ -686,6 +696,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
@ -700,38 +726,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
@ -739,7 +756,6 @@ if(!keymanweb) {
this.kbdButton.getElem().appendChild(this.keyboardMenu);
}
};
}
@ -794,4 +810,4 @@ if(!keymanweb) {
ui.initialize();
} catch(ex){}
}
}

View file

@ -1149,8 +1149,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

@ -75,7 +75,7 @@ export type LanguageAPIPropertySpec = {
* Corresponds to the documented API for the Web engine's `addKeyboards` function
* when a single language object is specified - not an array.
*
* See https://help.keyman.com/developer/engine/web/15.0/reference/core/addKeyboards,
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/addKeyboards,
* "Using an `object`".
*/
export type KeyboardAPIPropertySpec = {
@ -93,7 +93,7 @@ export type KeyboardAPIPropertySpec = {
* Corresponds to the documented API for the Web engine's `addKeyboards` function
* when a language array is specified for the object.
*
* See https://help.keyman.com/developer/engine/web/15.0/reference/core/addKeyboards,
* See https://help.keyman.com/developer/engine/web/current-version/reference/core/addKeyboards,
* "Using an `object`".
*/
export type KeyboardAPIPropertyMultilangSpec = {
@ -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

@ -5,6 +5,7 @@
"outDir": "../../build/engine/obj/",
"tsBuildInfoFile": "../../build/engine/obj/tsconfig.tsbuildinfo",
"rootDir": "./src",
"strictNullChecks": false
},
"include": [ "src/**/*.ts", "src/core-processor/import/*.js" ],

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);

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