diff --git a/.github/workflows/core-arm64-windows-test.yml b/.github/workflows/core-arm64-windows-test.yml index 0eb4e8f8f62..76d273de22b 100644 --- a/.github/workflows/core-arm64-windows-test.yml +++ b/.github/workflows/core-arm64-windows-test.yml @@ -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 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index ee7fe7d03d9..9eb25a49e7a 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -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 diff --git a/.github/workflows/pr-build-status.yml b/.github/workflows/pr-build-status.yml index a3d82b31629..51571b33ef6 100644 --- a/.github/workflows/pr-build-status.yml +++ b/.github/workflows/pr-build-status.yml @@ -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') { diff --git a/HISTORY.md b/HISTORY.md index 2901fca3d6b..f2d3cab56b0 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -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) diff --git a/VERSION.md b/VERSION.md index 128da2f9232..a0b5f4d5f24 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -19.0.237 \ No newline at end of file +19.0.248 \ No newline at end of file diff --git a/common/test/resources/keyboards/test_8568_deadkeys.html b/common/test/resources/keyboards/test_8568_deadkeys.html index 1b4cf314247..054f19b4f81 100644 --- a/common/test/resources/keyboards/test_8568_deadkeys.html +++ b/common/test/resources/keyboards/test_8568_deadkeys.html @@ -36,22 +36,20 @@ + }); + diff --git a/common/web/build.sh b/common/web/build.sh index 182de85d624..052061946fb 100755 --- a/common/web/build.sh +++ b/common/web/build.sh @@ -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 \ diff --git a/common/web/sentry-manager/build.sh b/common/web/sentry-manager/build.sh index 4e725290e49..b2bcea8d94f 100755 --- a/common/web/sentry-manager/build.sh +++ b/common/web/sentry-manager/build.sh @@ -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" \ diff --git a/common/windows/delphi/general/KeymanPaths.pas b/common/windows/delphi/general/KeymanPaths.pas index a0cc07d5b4d..2e5bff0aab6 100644 --- a/common/windows/delphi/general/KeymanPaths.pas +++ b/common/windows/delphi/general/KeymanPaths.pas @@ -44,6 +44,7 @@ TKeymanPaths = class 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 @@ class function TKeymanPaths.KeymanConfigStaticHttpFilesPath(const filename: stri 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; diff --git a/common/windows/delphi/general/KeymanVersion.pas b/common/windows/delphi/general/KeymanVersion.pas index 524973e025e..59794854236 100644 --- a/common/windows/delphi/general/KeymanVersion.pas +++ b/common/windows/delphi/general/KeymanVersion.pas @@ -30,6 +30,17 @@ interface 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'; diff --git a/core/cross-arm64.build b/core/cross-arm64.build index 9fd7cc6d147..7bea64992ee 100644 --- a/core/cross-arm64.build +++ b/core/cross-arm64.build @@ -1,5 +1,6 @@ [host_machine] system = 'windows' +subsystem = 'windows' cpu_family = 'aarch64' cpu = 'arm64' endian = 'little' diff --git a/core/cross-x64.build b/core/cross-x64.build index df3ab0b5dc6..ecc0486e088 100644 --- a/core/cross-x64.build +++ b/core/cross-x64.build @@ -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 diff --git a/core/src/km_core_processevent_api.cpp b/core/src/km_core_processevent_api.cpp index 78d87e2a2b8..d37ca45518c 100644 --- a/core/src/km_core_processevent_api.cpp +++ b/core/src/km_core_processevent_api.cpp @@ -37,7 +37,7 @@ km_core_event( return KM_CORE_STATUS_INVALID_ARGUMENT; } - km_core_status status = const_cast(state)->processor().external_event( const_cast(state), event, data); + km_core_status status = const_cast(state)->processor().external_event(const_cast(state), event, data); const_cast(state)->apply_actions_and_merge_app_context(); return status; } diff --git a/core/src/layout.hpp b/core/src/layout.hpp deleted file mode 100644 index 2e3d2833b4a..00000000000 --- a/core/src/layout.hpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Keyman is copyright (C) SIL International. MIT License. - * - * Keyman Keyboard Processor API - On-Screen Keyboard Layout Interfaces - */ - -#pragma once - -#include -#include -#include - -#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 longpresses; - /** multitaps */ - std::vector multiTaps; - /** flicks */ - std::map 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 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 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 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 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 diff --git a/core/src/meson.build b/core/src/meson.build index f20b23e2384..1590954daa2 100644 --- a/core/src/meson.build +++ b/core/src/meson.build @@ -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', diff --git a/core/tests/unit/wasm/package-lock.json b/core/tests/unit/wasm/package-lock.json index f2c3485da56..b34e01caee9 100644 --- a/core/tests/unit/wasm/package-lock.json +++ b/core/tests/unit/wasm/package-lock.json @@ -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": { diff --git a/developer/docs/help/reference/kmc/cli/get-started.md b/developer/docs/help/reference/kmc/cli/get-started.md index 348bf4c4b9b..2316abef38c 100644 --- a/developer/docs/help/reference/kmc/cli/get-started.md +++ b/developer/docs/help/reference/kmc/cli/get-started.md @@ -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:{ diff --git a/developer/docs/help/reference/kmc/cli/reference.md b/developer/docs/help/reference/kmc/cli/reference.md index 1d5ed9fc201..662d85a0a3f 100644 --- a/developer/docs/help/reference/kmc/cli/reference.md +++ b/developer/docs/help/reference/kmc/cli/reference.md @@ -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 diff --git a/developer/src/kmc/README.md b/developer/src/kmc/README.md index 0490998e4d8..05fd265a50f 100644 --- a/developer/src/kmc/README.md +++ b/developer/src/kmc/README.md @@ -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:{ diff --git a/developer/src/kmc/src/commands/build.ts b/developer/src/kmc/src/commands/build.ts index a23181354c3..71c7cd3321a 100644 --- a/developer/src/kmc/src/commands/build.ts +++ b/developer/src/kmc/src/commands/build.ts @@ -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 diff --git a/developer/src/kmc/src/commands/buildClasses/BuildKeyboardInfo.ts b/developer/src/kmc/src/commands/buildClasses/BuildKeyboardInfo.ts index 3109038a3a6..d3d42f9ce6a 100644 --- a/developer/src/kmc/src/commands/buildClasses/BuildKeyboardInfo.ts +++ b/developer/src/kmc/src/commands/buildClasses/BuildKeyboardInfo.ts @@ -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); diff --git a/developer/src/kmc/src/commands/buildClasses/BuildModelInfo.ts b/developer/src/kmc/src/commands/buildClasses/BuildModelInfo.ts index f6a44ced5f9..7f641a9b8ee 100644 --- a/developer/src/kmc/src/commands/buildClasses/BuildModelInfo.ts +++ b/developer/src/kmc/src/commands/buildClasses/BuildModelInfo.ts @@ -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? diff --git a/developer/src/kmc/src/commands/buildClasses/BuildProject.ts b/developer/src/kmc/src/commands/buildClasses/BuildProject.ts index 82e1347aa5c..76fdfe1d04d 100644 --- a/developer/src/kmc/src/commands/buildClasses/BuildProject.ts +++ b/developer/src/kmc/src/commands/buildClasses/BuildProject.ts @@ -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(!await this.buildProjectTargets(builder)) { - return false; + 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; + } } } @@ -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; } diff --git a/developer/src/kmc/src/messages/infrastructureMessages.ts b/developer/src/kmc/src/messages/infrastructureMessages.ts index 373736fbd34..feb819dd9d4 100644 --- a/developer/src/kmc/src/messages/infrastructureMessages.ts +++ b/developer/src/kmc/src/messages/infrastructureMessages.ts @@ -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.`, diff --git a/developer/src/kmc/src/util/NodeCompilerCallbacks.ts b/developer/src/kmc/src/util/NodeCompilerCallbacks.ts index a92314aa5f0..68209d95cea 100644 --- a/developer/src/kmc/src/util/NodeCompilerCallbacks.ts +++ b/developer/src/kmc/src/util/NodeCompilerCallbacks.ts @@ -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; diff --git a/developer/src/kmc/src/util/extendedCompilerOptions.ts b/developer/src/kmc/src/util/extendedCompilerOptions.ts index 8dd3eed9c8a..63b5a9fd29a 100644 --- a/developer/src/kmc/src/util/extendedCompilerOptions.ts +++ b/developer/src/kmc/src/util/extendedCompilerOptions.ts @@ -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, } } diff --git a/developer/src/server/package.json b/developer/src/server/package.json index 662be2add6c..37c92b7f518 100644 --- a/developer/src/server/package.json +++ b/developer/src/server/package.json @@ -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", diff --git a/developer/src/tike/compile/Keyman.Developer.System.KmcWrapper.pas b/developer/src/tike/compile/Keyman.Developer.System.KmcWrapper.pas index 168ea8c77df..6c33760ffc7 100644 --- a/developer/src/tike/compile/Keyman.Developer.System.KmcWrapper.pas +++ b/developer/src/tike/compile/Keyman.Developer.System.KmcWrapper.pas @@ -17,6 +17,7 @@ TKmcWrapper = class function Run(const parameters: TArray; 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 @@ implementation { TKmcWrapper } +function TKmcWrapper.CompileForPublishing( + ProjectFile: TProjectFile; + const projectFilename: string; + debug: Boolean; + Callback: TUtilExecuteCallbackEvent +): Boolean; +var + cmdline: TArray; +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; diff --git a/developer/src/tike/main/Keyman.System.KeymanCore.pas b/developer/src/tike/main/Keyman.System.KeymanCore.pas index ee9a3c0e828..1df9e42d8c2 100644 --- a/developer/src/tike/main/Keyman.System.KeymanCore.pas +++ b/developer/src/tike/main/Keyman.System.KeymanCore.pas @@ -233,7 +233,7 @@ function km_core_state_options_update( 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; diff --git a/developer/src/tike/main/Keyman.System.KeymanCoreDebug.pas b/developer/src/tike/main/Keyman.System.KeymanCoreDebug.pas index 0b5600152b9..3c902391737 100644 --- a/developer/src/tike/main/Keyman.System.KeymanCoreDebug.pas +++ b/developer/src/tike/main/Keyman.System.KeymanCoreDebug.pas @@ -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: diff --git a/developer/src/tike/main/UfrmMessages.pas b/developer/src/tike/main/UfrmMessages.pas index 9a76254c969..924bec9acd5 100644 --- a/developer/src/tike/main/UfrmMessages.pas +++ b/developer/src/tike/main/UfrmMessages.pas @@ -170,6 +170,12 @@ implementation 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 @@ procedure TfrmMessages.Add(state: TProjectLogState; filename, msg: WideString; M 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 diff --git a/developer/src/tike/project/Keyman.Developer.System.Project.kpsProjectFileAction.pas b/developer/src/tike/project/Keyman.Developer.System.Project.kpsProjectFileAction.pas index c7a7cafcc5c..94cb28c151c 100644 --- a/developer/src/tike/project/Keyman.Developer.System.Project.kpsProjectFileAction.pas +++ b/developer/src/tike/project/Keyman.Developer.System.Project.kpsProjectFileAction.pas @@ -29,6 +29,8 @@ function TkpsProjectFileAction.CompilePackage: Boolean; 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; diff --git a/developer/src/tike/xml/help/contexthelp.xml b/developer/src/tike/xml/help/contexthelp.xml index 646496f5c7d..7bada6adbf8 100644 --- a/developer/src/tike/xml/help/contexthelp.xml +++ b/developer/src/tike/xml/help/contexthelp.xml @@ -419,11 +419,6 @@

Choose a help file to include in the keyboard

- -

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.

-
-

Tests your KeymanWeb keyboard in an Internet Explorer embedded window

diff --git a/docs/websites/README.md b/docs/websites/README.md index 9813fcc2aec..165fb6a718a 100644 --- a/docs/websites/README.md +++ b/docs/websites/README.md @@ -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 diff --git a/linux/scripts/dist.sh b/linux/scripts/dist.sh index b66615ad9dc..a09225e3ea3 100755 --- a/linux/scripts/dist.sh +++ b/linux/scripts/dist.sh @@ -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}" diff --git a/linux/scripts/package-build.inc.sh b/linux/scripts/package-build.inc.sh index ca58fb9d54d..f1d6f15c692 100644 --- a/linux/scripts/package-build.inc.sh +++ b/linux/scripts/package-build.inc.sh @@ -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 diff --git a/linux/scripts/verify_source.sh b/linux/scripts/verify_source.sh index bec62599f55..2ed00b9a6f8 100755 --- a/linux/scripts/verify_source.sh +++ b/linux/scripts/verify_source.sh @@ -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}" diff --git a/linux/scripts/watch.in b/linux/scripts/watch.in index 82a5ce6cde7..b4c0039a846 100644 --- a/linux/scripts/watch.in +++ b/linux/scripts/watch.in @@ -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 diff --git a/oem/firstvoices/ios/exportAppStore.plist b/oem/firstvoices/ios/exportAppStore.plist index 35155c59ea9..a981855b265 100644 --- a/oem/firstvoices/ios/exportAppStore.plist +++ b/oem/firstvoices/ios/exportAppStore.plist @@ -7,9 +7,9 @@ teamID D7TR486TEH signingCertificate - D7B7973A30C90C1A04666667F24B4D5C7E913923 + D2FEB39A439B44ACC1E5A710D20C17587EEE29E1 installerSigningCertificate - D7B7973A30C90C1A04666667F24B4D5C7E913923 + D2FEB39A439B44ACC1E5A710D20C17587EEE29E1 provisioningProfiles com.firstvoices.keyboards diff --git a/oem/firstvoices/windows/src/xml/strings.xml b/oem/firstvoices/windows/src/xml/strings.xml index e69f624e0c7..e476f1daf89 100644 --- a/oem/firstvoices/windows/src/xml/strings.xml +++ b/oem/firstvoices/windows/src/xml/strings.xml @@ -833,11 +833,6 @@ keyboard that you use in Windows. Keyman for FirstVoices will adapt automatical en - - - - en - diff --git a/package-lock.json b/package-lock.json index bdf2510433b..5ac21c94d1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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" diff --git a/resources/teamcity/linux/keyman-linux-release.sh b/resources/teamcity/linux/keyman-linux-release.sh index 9fb73ef91e9..9cc1b76a1b6 100755 --- a/resources/teamcity/linux/keyman-linux-release.sh +++ b/resources/teamcity/linux/keyman-linux-release.sh @@ -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" diff --git a/web/NOTICE b/web/NOTICE deleted file mode 100644 index 116bef67679..00000000000 --- a/web/NOTICE +++ /dev/null @@ -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/). diff --git a/web/docs/engine/guide/adding-keyboards.md b/web/docs/engine/guide/adding-keyboards.md index 221f495de1b..957df80270c 100644 --- a/web/docs/engine/guide/adding-keyboards.md +++ b/web/docs/engine/guide/adding-keyboards.md @@ -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. diff --git a/web/docs/engine/guide/examples/__auto-control.html b/web/docs/engine/guide/examples/__auto-control.html index 050e03a9f03..c2a8b472b09 100644 --- a/web/docs/engine/guide/examples/__auto-control.html +++ b/web/docs/engine/guide/examples/__auto-control.html @@ -1,20 +1,17 @@ + - - @@ -28,4 +25,4 @@

Automatic Mode Example

Back to Document - \ No newline at end of file + diff --git a/web/docs/engine/guide/examples/__control-by-control.html b/web/docs/engine/guide/examples/__control-by-control.html index 93a481a75f6..f694b582ecb 100644 --- a/web/docs/engine/guide/examples/__control-by-control.html +++ b/web/docs/engine/guide/examples/__control-by-control.html @@ -1,35 +1,33 @@ + - @@ -38,7 +36,7 @@

Control-by-Control Example

Email to (KeymanWeb not enabled)

-

Subject (Defaults to 'English' or 'off' unless on a touch-based device)

+

Subject (Defaults to '(System keyboard)' or 'off' unless on a touch-based device)

Message body (Defaults to 'LaoKeys')

@@ -46,4 +44,4 @@

Control-by-Control Example

Back to Document - \ No newline at end of file + diff --git a/web/docs/engine/guide/examples/__first-example.html b/web/docs/engine/guide/examples/__first-example.html index feb96f4daa0..24a58c48916 100644 --- a/web/docs/engine/guide/examples/__first-example.html +++ b/web/docs/engine/guide/examples/__first-example.html @@ -6,15 +6,14 @@ - @@ -23,4 +22,4 @@

First Example

Back to Document - \ No newline at end of file + diff --git a/web/docs/engine/guide/examples/__full-manual-control.html b/web/docs/engine/guide/examples/__full-manual-control.html index 5a70c7183ff..6313c135184 100644 --- a/web/docs/engine/guide/examples/__full-manual-control.html +++ b/web/docs/engine/guide/examples/__full-manual-control.html @@ -1,45 +1,39 @@ + - diff --git a/web/docs/engine/guide/examples/__manual-control.html b/web/docs/engine/guide/examples/__manual-control.html index 4680400e4df..a1cfa72c83c 100644 --- a/web/docs/engine/guide/examples/__manual-control.html +++ b/web/docs/engine/guide/examples/__manual-control.html @@ -1,29 +1,27 @@ + - - - @@ -38,4 +36,4 @@

Manual Mode Example

Back to Document - \ No newline at end of file + diff --git a/web/docs/engine/guide/examples/automatic-control.md b/web/docs/engine/guide/examples/automatic-control.md index c804556e180..d21946aa536 100644 --- a/web/docs/engine/guide/examples/automatic-control.md +++ b/web/docs/engine/guide/examples/automatic-control.md @@ -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 - + @@ -31,8 +31,6 @@ In this example, we use only the LaoKey keyboard. Please click [this link](./__a ``` -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). diff --git a/web/docs/engine/guide/examples/control-by-control.md b/web/docs/engine/guide/examples/control-by-control.md index 974338ef1fe..b3b0b9b3a5e 100644 --- a/web/docs/engine/guide/examples/control-by-control.md +++ b/web/docs/engine/guide/examples/control-by-control.md @@ -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 ``` @@ -40,18 +38,18 @@ Also include the following HTML code: ```html - - + + - - - - ``` + --- -**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. --- diff --git a/web/docs/engine/guide/examples/full-manual-control.md b/web/docs/engine/guide/examples/full-manual-control.md index e9e8d2908a8..39be4d34b3d 100644 --- a/web/docs/engine/guide/examples/full-manual-control.md +++ b/web/docs/engine/guide/examples/full-manual-control.md @@ -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: + - - - ``` - File: [unified_loader.js](js/unified_loader.js) diff --git a/web/docs/engine/guide/examples/js/devanagari_inscript_load.js b/web/docs/engine/guide/examples/js/devanagari_inscript_load.js deleted file mode 100644 index c2fd4d90fe0..00000000000 --- a/web/docs/engine/guide/examples/js/devanagari_inscript_load.js +++ /dev/null @@ -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)";} diff --git a/web/docs/engine/guide/examples/js/european2_load.js b/web/docs/engine/guide/examples/js/european2_load.js deleted file mode 100644 index 96ecc9a9be2..00000000000 --- a/web/docs/engine/guide/examples/js/european2_load.js +++ /dev/null @@ -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";} \ No newline at end of file diff --git a/web/docs/engine/guide/examples/js/hebrew_load.js b/web/docs/engine/guide/examples/js/hebrew_load.js deleted file mode 100644 index ebcd50e4d0e..00000000000 --- a/web/docs/engine/guide/examples/js/hebrew_load.js +++ /dev/null @@ -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";} diff --git a/web/docs/engine/guide/examples/js/keymanwebtest_load.js b/web/docs/engine/guide/examples/js/keymanwebtest_load.js deleted file mode 100644 index c06cbba38e1..00000000000 --- a/web/docs/engine/guide/examples/js/keymanwebtest_load.js +++ /dev/null @@ -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";} diff --git a/web/docs/engine/guide/examples/js/korean_korda_load.js b/web/docs/engine/guide/examples/js/korean_korda_load.js deleted file mode 100644 index 7fa96e4bc03..00000000000 --- a/web/docs/engine/guide/examples/js/korean_korda_load.js +++ /dev/null @@ -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";} diff --git a/web/docs/engine/guide/examples/js/korean_morse_load.js b/web/docs/engine/guide/examples/js/korean_morse_load.js deleted file mode 100644 index fb7abbdb912..00000000000 --- a/web/docs/engine/guide/examples/js/korean_morse_load.js +++ /dev/null @@ -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";} diff --git a/web/docs/engine/guide/examples/js/laokeys_load.js b/web/docs/engine/guide/examples/js/laokeys_load.js deleted file mode 100644 index 522e21fd5ad..00000000000 --- a/web/docs/engine/guide/examples/js/laokeys_load.js +++ /dev/null @@ -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" -}); \ No newline at end of file diff --git a/web/docs/engine/guide/examples/js/unified_loader.js b/web/docs/engine/guide/examples/js/unified_loader.js index 2987ce34139..f4a695d24a9 100644 --- a/web/docs/engine/guide/examples/js/unified_loader.js +++ b/web/docs/engine/guide/examples/js/unified_loader.js @@ -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)); + }); } diff --git a/web/docs/engine/guide/examples/manual-control.md b/web/docs/engine/guide/examples/manual-control.md index 1087c518c8c..d67db736e96 100644 --- a/web/docs/engine/guide/examples/manual-control.md +++ b/web/docs/engine/guide/examples/manual-control.md @@ -10,21 +10,19 @@ Include the following script in the HEAD of your page: ```js - - + - - - ``` -- 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). diff --git a/web/docs/engine/guide/get-started.md b/web/docs/engine/guide/get-started.md index 8d3cd1baa49..96e7a699f43 100644 --- a/web/docs/engine/guide/get-started.md +++ b/web/docs/engine/guide/get-started.md @@ -22,12 +22,10 @@ The source code for the page may be seen below. diff --git a/web/docs/engine/reference/core/addKeyboards.md b/web/docs/engine/reference/core/addKeyboards.md index 4a2dc77f5db..e5f48680a77 100644 --- a/web/docs/engine/reference/core/addKeyboards.md +++ b/web/docs/engine/reference/core/addKeyboards.md @@ -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` optional - 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` optional 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. \ No newline at end of file diff --git a/web/docs/engine/reference/core/addKeyboardsForLanguage.md b/web/docs/engine/reference/core/addKeyboardsForLanguage.md index d06ce2ede22..e3a1e553c7a 100644 --- a/web/docs/engine/reference/core/addKeyboardsForLanguage.md +++ b/web/docs/engine/reference/core/addKeyboardsForLanguage.md @@ -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` diff --git a/web/docs/engine/reference/core/getKeyboard.md b/web/docs/engine/reference/core/getKeyboard.md index 0d6f8eb52e9..9b728995d07 100644 --- a/web/docs/engine/reference/core/getKeyboard.md +++ b/web/docs/engine/reference/core/getKeyboard.md @@ -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. diff --git a/web/docs/engine/reference/core/getKeyboards.md b/web/docs/engine/reference/core/getKeyboards.md index 8d84521b31a..cd8b0f2d9c6 100644 --- a/web/docs/engine/reference/core/getKeyboards.md +++ b/web/docs/engine/reference/core/getKeyboards.md @@ -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) diff --git a/web/docs/engine/reference/interface/registerStub.md b/web/docs/engine/reference/interface/registerStub.md index 2d9c8ef5a8e..9dc8e10c34a 100644 --- a/web/docs/engine/reference/interface/registerStub.md +++ b/web/docs/engine/reference/interface/registerStub.md @@ -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 ``` diff --git a/web/docs/engine/reference/layouts.md b/web/docs/engine/reference/layouts.md index df3e3b50c7f..56d1e40d625 100644 --- a/web/docs/engine/reference/layouts.md +++ b/web/docs/engine/reference/layouts.md @@ -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. diff --git a/web/docs/engine/reference/layoutspec.md b/web/docs/engine/reference/layoutspec.md index 2cdaec1fffa..b9d2353901d 100644 --- a/web/docs/engine/reference/layoutspec.md +++ b/web/docs/engine/reference/layoutspec.md @@ -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). @@ -249,7 +255,7 @@ Details of key member specifications are given below: - + @@ -330,7 +336,11 @@ Details of key member specifications are given below:
id Each key id must start with >K_ , for keys mapped to standard Keyman virtual key names, e.g. K_HYPHEN , or either U_ or T_ for user-defined names. Keys identified as U_xxxx[_xxxx...] specify one or more Unicode characters in hex format, e.g. U_1363 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 T_ZZZ , will be ignored unless matched by a rule. The key id is required except for key styles 9 or 10 (blank or spacer keys).
-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 | |:------------------|:--------------------------------------------------------| diff --git a/web/docs/engine/reference/overview.md b/web/docs/engine/reference/overview.md index 574e704abff..712a20640bf 100644 --- a/web/docs/engine/reference/overview.md +++ b/web/docs/engine/reference/overview.md @@ -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. - -A *KeymanWeb* instance is automatically constructed when you include the compiled KeymanWeb script (kmw-release.js) in your web page source. +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. The *KeymanWeb API* comprises the following objects: diff --git a/web/package.json b/web/package.json index 0ce1a7d9935..5ec0135add8 100644 --- a/web/package.json +++ b/web/package.json @@ -17,16 +17,6 @@ "types": "./build/engine/obj/attachment/index.d.ts", "import": "./build/engine/obj/attachment/index.js" }, - "./engine/core-adapter": { - "es6-bundling": "./src/engine/src/core-adapter/index.ts", - "types": "./build/engine/obj/core-adapter/index.d.ts", - "import": "./build/engine/obj/core-adapter/index.js" - }, - "./engine/core-processor": { - "es6-bundling": "./src/engine/src/core-processor/index.ts", - "types": "./build/engine/obj/core-processor/index.d.ts", - "import": "./build/engine/obj/core-processor/index.js" - }, "./engine/dom-utils": { "es6-bundling": "./src/engine/src/dom-utils/index.ts", "types": "./build/engine/obj/dom-utils/index.d.ts", @@ -117,7 +107,7 @@ "@sentry/cli": "^2.31.0", "@zip.js/zip.js": "^2.7.32", "c8": "^7.12.0", - "express": "^4.19.2", + "express": "^4.22.2", "jsdom": "^23.0.1", "promise-status-async": "^1.2.10", "tsx": "^4.19.0" diff --git a/web/src/.editorconfig b/web/src/.editorconfig deleted file mode 100644 index 34f03b7b15b..00000000000 --- a/web/src/.editorconfig +++ /dev/null @@ -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 - diff --git a/web/src/app/browser/build.sh b/web/src/app/browser/build.sh index 5396b9baa12..41fc9c5230b 100755 --- a/web/src/app/browser/build.sh +++ b/web/src/app/browser/build.sh @@ -72,10 +72,6 @@ compile_and_copy() { mkdir -p "$KEYMAN_ROOT/web/build/app/resources/osk" cp -R "$KEYMAN_ROOT/web/src/resources/osk/." "$KEYMAN_ROOT/web/build/app/resources/osk/" - # Copy Keyman Core build artifacts for local reference - cp "${KEYMAN_ROOT}/web/build/engine/obj/core-adapter/import/core/"km-core.{js,wasm} "${KEYMAN_ROOT}/web/build/app/browser/debug/" - cp "${KEYMAN_ROOT}/web/build/engine/obj/core-adapter/import/core/"km-core.{js,wasm} "${KEYMAN_ROOT}/web/build/app/browser/release/" - # Update the build/publish copy of our build artifacts prepare diff --git a/web/src/app/browser/src/context/focusAssistant.ts b/web/src/app/browser/src/context/focusAssistant.ts index accb9b811ec..ade72ea65a8 100644 --- a/web/src/app/browser/src/context/focusAssistant.ts +++ b/web/src/app/browser/src/context/focusAssistant.ts @@ -149,7 +149,7 @@ export class FocusAssistant extends EventEmitter { * @param {(boolean|number)} state Activate (true,false) */ setMaintainingFocus(state: boolean) { - this.maintainingFocus = state ? true : false; + this.maintainingFocus = !!state; } setFocusTimer(): void { diff --git a/web/src/app/browser/src/keymanEngine.ts b/web/src/app/browser/src/keymanEngine.ts index d5f2a15c4ed..715192701cd 100644 --- a/web/src/app/browser/src/keymanEngine.ts +++ b/web/src/app/browser/src/keymanEngine.ts @@ -8,7 +8,7 @@ import { } from 'keyman/engine/osk'; import { ErrorStub, KeyboardStub, CloudQueryResult, toPrefixedKeyboardId } from 'keyman/engine/keyboard-storage'; import { DeviceSpec } from 'keyman/common/web-utils'; -import { JSKeyboard, Keyboard, KMXKeyboard } from "keyman/engine/keyboard"; +import { JSKeyboard, Keyboard } from "keyman/engine/keyboard"; import KeyboardObject = KeymanWebKeyboard.KeyboardObject; import * as views from './viewsAnchorpoint.js'; @@ -257,7 +257,7 @@ export class KeymanEngine extends KeymanEngineBase { - // 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 { - let _name,_lgCode; - if(_kbd < 0) { - _name = ''; - _lgCode=''; + private async selectKbd(kbdIndex: number): Promise { + 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 = {}; + // 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 _n=_added[_kbds[_kbd].InternalName]; - this.keyboards.push({_InternalName: _kbds[_kbd].InternalName, _LanguageCode:_kbds[_kbd].LanguageCode, _Index: _n}); + const keyboards = keymanweb.getKeyboards(); + const added: Record = {}; + for (let kbdIndex = 0; kbdIndex < keyboards.length; kbdIndex++) { + if (!added[keyboards[kbdIndex].InternalName]) { + added[keyboards[kbdIndex].InternalName] = 0; + } + added[keyboards[kbdIndex].InternalName]++; - _a1.href="#"; - _a1.onclick = ((x) => { return () => this.selectKbd(x); })(this.keyboards.length-1); - _a1.id='KMWSel_'+_kbds[_kbd].InternalName+'$'+_n; + const kbdInstanceCount = added[keyboards[kbdIndex].InternalName]; + this.keyboards.push({ _InternalName: keyboards[kbdIndex].InternalName, _LanguageCode: keyboards[kbdIndex].LanguageCode, _Index: kbdInstanceCount }); - _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){} -} \ No newline at end of file +} diff --git a/web/src/app/ui/kmwuitoolbar.ts b/web/src/app/ui/kmwuitoolbar.ts index 6897494cef0..43c2c950d1e 100644 --- a/web/src/app/ui/kmwuitoolbar.ts +++ b/web/src/app/ui/kmwuitoolbar.ts @@ -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]; diff --git a/web/src/app/webview/build.sh b/web/src/app/webview/build.sh index e6ff4795d01..cce218ba6d6 100755 --- a/web/src/app/webview/build.sh +++ b/web/src/app/webview/build.sh @@ -61,10 +61,6 @@ compile_and_copy() { mkdir -p "$KEYMAN_ROOT/web/build/app/resources/osk" cp -R "$KEYMAN_ROOT/web/src/resources/osk/." "$KEYMAN_ROOT/web/build/app/resources/osk/" - # Copy Keyman Core build artifacts for local reference - cp "${KEYMAN_ROOT}/web/build/engine/obj/core-adapter/import/core/"km-core.{js,wasm} "${KEYMAN_ROOT}/web/build/app/webview/debug/" - cp "${KEYMAN_ROOT}/web/build/engine/obj/core-adapter/import/core/"km-core.{js,wasm} "${KEYMAN_ROOT}/web/build/app/webview/release/" - # Clean the sourcemaps of .. and . components for script in "$KEYMAN_ROOT/web/build/$SUBPROJECT_NAME/debug/"*.js; do if [[ "${script}" == *"/km-core.js" ]]; then diff --git a/web/src/engine/build.sh b/web/src/engine/build.sh index cdf39ca569d..115792926e3 100755 --- a/web/src/engine/build.sh +++ b/web/src/engine/build.sh @@ -38,16 +38,6 @@ builder_parse "$@" #### Build action definitions #### copy_deps() { - mkdir -p "${KEYMAN_ROOT}/web/src/engine/src/core-adapter/import/core/" - # we don't need this file for release builds, but it's nice to have - # for reference and auto-completion - cp "${KEYMAN_ROOT}/core/build/wasm/${BUILDER_CONFIGURATION}/src/keymancore.d.ts" "${KEYMAN_ROOT}/web/src/engine/src/core-adapter/import/core/" - - mkdir -p "${KEYMAN_ROOT}/web/build/engine/obj/core-adapter/import/core/" - cp "${KEYMAN_ROOT}/core/build/wasm/${BUILDER_CONFIGURATION}/src/keymancore.d.ts" "${KEYMAN_ROOT}/web/build/engine/obj/core-adapter/import/core/" - cp "${KEYMAN_ROOT}/core/build/wasm/${BUILDER_CONFIGURATION}/src/"km-core{.js,.wasm} "${KEYMAN_ROOT}/web/build/engine/obj/core-adapter/import/core/" - cp "${KEYMAN_ROOT}/core/build/wasm/${BUILDER_CONFIGURATION}/src/"km-core-node{.mjs,.wasm} "${KEYMAN_ROOT}/web/build/engine/obj/core-adapter/import/core/" - cp "${KEYMAN_ROOT}/common/resources/fonts/keymanweb-osk.ttf" "${KEYMAN_ROOT}/web/src/resources/osk/" } diff --git a/web/src/engine/src/core-adapter/.gitignore b/web/src/engine/src/core-adapter/.gitignore deleted file mode 100644 index 735618b5a4d..00000000000 --- a/web/src/engine/src/core-adapter/.gitignore +++ /dev/null @@ -1 +0,0 @@ -import/ \ No newline at end of file diff --git a/web/src/engine/src/core-adapter/KM_Core.ts b/web/src/engine/src/core-adapter/KM_Core.ts deleted file mode 100644 index f0acae06522..00000000000 --- a/web/src/engine/src/core-adapter/KM_Core.ts +++ /dev/null @@ -1,76 +0,0 @@ -// Keyman is copyright (C) SIL International. MIT License. - -import { type MainModule as KMXCoreModule } from './import/core/keymancore.js'; - -// Unfortunately embind has an open issue with enums and typescript where it -// only generates a type for the enum, but not the values in a usable way. -// So we have to re-define the enum here. -// See https://github.com/emscripten-core/emscripten/issues/18585 -// NOTE: Keep in sync with core/include/keyman/keyman_core_api.h#L311 -export enum KM_CORE_STATUS { - OK = 0, - NO_MEM = 1, - IO_ERROR = 2, - INVALID_ARGUMENT = 3, - KEY_ERROR = 4, - INSUFFICENT_BUFFER = 5, - INVALID_UTF = 6, - INVALID_KEYBOARD = 7, - NOT_IMPLEMENTED = 8, - OS_ERROR = 0x80000000 -} - -export enum KM_CORE_OPTION_SCOPE { - OPT_UNKNOWN = 0, - OPT_KEYBOARD = 1, - OPT_ENVIRONMENT = 2, - OPT_MAX_SCOPES -} - -export enum KM_CORE_KMX_ENV { - PLATFORM = "platform", - BASELAYOUT = "baseLayout", - BASELAYOUTALT = "baseLayoutAlt", - SIMULATEALTGR = "simulateAltgr", - CAPSLOCK = "capsLock", - BASELAYOUTGIVESCTRLRALTFORRALT = "baseLayoutGivesCtrlRAltForRAlt", -} - -export enum KM_CORE_CT { - END = 0, - CHAR = 1, - MARKER = 2, -} - -export enum KM_CORE_EVENT_FLAG { - DEFAULT = 0, - TOUCH = 1, -}; - -export class KM_Core { - private static km_core: KMXCoreModule = null; - - static get instance(): KMXCoreModule { - return this.km_core; - } - - private static isNode() { - // from https://stackoverflow.com/a/31456668 - return (typeof process !== "undefined" && process?.versions?.node); - } - - public static async createCoreProcessor(baseurl: string): Promise { - const coreModuleName = this.isNode() ? 'km-core-node.mjs' : 'km-core.js'; - const module = await import(`${baseurl}/${coreModuleName}`); - const createCoreProcessor = module.default; - const km_core = createCoreProcessor({ - locateFile: function (path: string, scriptDirectory: string) { - return baseurl + '/' + path; - } - }); - km_core.then((core: KMXCoreModule) => { - this.km_core = core; - }); - return km_core; - } -} \ No newline at end of file diff --git a/web/src/engine/src/core-adapter/index.ts b/web/src/engine/src/core-adapter/index.ts deleted file mode 100644 index f5a4ff2c785..00000000000 --- a/web/src/engine/src/core-adapter/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { KM_Core, KM_CORE_STATUS, KM_CORE_OPTION_SCOPE, KM_CORE_KMX_ENV, KM_CORE_CT } from './KM_Core.js'; -import { type km_core_keyboard, type km_core_state, type km_core_context, type km_core_context_item, type km_core_context_items, type km_core_option_item } from './import/core/keymancore.js'; -export { km_core_keyboard, km_core_state, km_core_context, km_core_context_item, km_core_context_items, km_core_option_item }; diff --git a/web/src/engine/src/core-processor/coreKeyboardInterface.ts b/web/src/engine/src/core-processor/coreKeyboardInterface.ts deleted file mode 100644 index 2b811d58912..00000000000 --- a/web/src/engine/src/core-processor/coreKeyboardInterface.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Keyman is copyright (C) SIL Global. MIT License. - */ -import { KM_Core, km_core_option_item, KM_CORE_OPTION_SCOPE } from 'keyman/engine/core-adapter'; -import { KeyboardMinimalInterface, VariableStoreSerializer, KMXKeyboard } from 'keyman/engine/keyboard'; -import { toPrefixedKeyboardId } from 'keyman/engine/keyboard-storage'; - -export class CoreKeyboardInterface implements KeyboardMinimalInterface { - private _activeKeyboard: KMXKeyboard; - - public constructor(public readonly variableStoreSerializer: VariableStoreSerializer) { - if (!variableStoreSerializer) { - throw new Error('variableStoreSerializer is required'); - } - } - - public get activeKeyboard(): KMXKeyboard { - return this._activeKeyboard; - } - public set activeKeyboard(keyboard: KMXKeyboard) { - this._activeKeyboard = keyboard; - const options = this.loadSerializedOptions(); - - if (options.length > 0) { - KM_Core.instance.state_options_update(keyboard.state, options); - } - } - - private loadSerializedOptions(): km_core_option_item[] { - const options: km_core_option_item[] = []; - const attrs = KM_Core.instance.keyboard_get_attrs(this.activeKeyboard.keyboard); - const prefixedKeyboardId = toPrefixedKeyboardId(this.activeKeyboard.id); - const defaultOptions = attrs.object.default_options as km_core_option_item[]; - for (const defaultOption of defaultOptions) { - if (defaultOption.scope == KM_CORE_OPTION_SCOPE.OPT_KEYBOARD) { - const savedValue = this.variableStoreSerializer.loadStore(prefixedKeyboardId, defaultOption.key); - if (savedValue !== undefined) { - const item: km_core_option_item = { - key: defaultOption.key, - value: savedValue, - scope: defaultOption.scope - }; - options.push(item); - } - } - } - attrs.delete(); - return options; - } -} diff --git a/web/src/engine/src/core-processor/coreKeyboardProcessor.ts b/web/src/engine/src/core-processor/coreKeyboardProcessor.ts deleted file mode 100644 index 40d8d8ca665..00000000000 --- a/web/src/engine/src/core-processor/coreKeyboardProcessor.ts +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Keyman is copyright (C) SIL Global. MIT License. - */ - -import { KM_Core, KM_CORE_STATUS, KM_CORE_CT, km_core_context, km_core_context_items, km_core_option_item } from 'keyman/engine/core-adapter'; -import { - AbstractKeyboardProcessor, - KeyEvent, KMXKeyboard, SyntheticTextStore, MutableSystemStore, TextStore, ProcessorAction, - Deadkey, - VariableStoreSerializer -} from "keyman/engine/keyboard"; -import { KM_CORE_EVENT_FLAG, KM_CORE_OPTION_SCOPE } from '../core-adapter/KM_Core.js'; -import { DeviceSpec } from 'keyman/common/web-utils'; -import { CoreKeyboardInterface } from './coreKeyboardInterface.js'; -import { toPrefixedKeyboardId } from 'keyman/engine/keyboard-storage'; - -/** - * Implements the core keyboard processing engine that interacts with the - * shared Keyman Core component which handles .kmx keyboards. - */ -export class CoreKeyboardProcessor extends AbstractKeyboardProcessor { - private _newLayerStore: MutableSystemStore = new MutableSystemStore(0, 'default'); - private _oldLayerStore: MutableSystemStore = new MutableSystemStore(0, 'default'); - private _layerStore: MutableSystemStore = new MutableSystemStore(0, 'default'); - - public constructor(device: DeviceSpec, baseLayout: string) { - super(device, baseLayout, null); - } - - /** - * Initialize the core processor with the provided base path. - * Sets up the necessary environment for processing keyboard events. - * - * @param {string} basePath The path for the core processor - * resources, i.e. where the - * km-core.js file is located. - * @param {VariableStoreSerializer} storeSerializer Optional serializer for variable stores. - * @returns {Promise} A promise that resolves when initialization is complete. - */ - public async init(basePath: string, storeSerializer: VariableStoreSerializer): Promise { - await KM_Core.createCoreProcessor(basePath); - this._keyboardInterface = new CoreKeyboardInterface(storeSerializer); - } - - /** - * The store representing the currently active keyboard layer. - * - * @type {MutableSystemStore} - */ - public get layerStore(): MutableSystemStore { - // TODO-web-core: link to .kmx layer store (#15284) - return this._layerStore; - } - - /** - * A writable store used when transitioning to a new layer - * - * @type {MutableSystemStore} - */ - public get newLayerStore(): MutableSystemStore { - // TODO-web-core: link to .kmx new-layer store (#15284) - return this._newLayerStore; - } - - /** - * A store representing the previously active layer - * - * @type {MutableSystemStore} - */ - public get oldLayerStore(): MutableSystemStore { - // TODO-web-core: link to .kmx old-layer store (#15284) - return this._oldLayerStore; - } - - /** - * Identifier of the currently active layer. - * - * @type {string} - */ - public get layerId(): string { - return this._layerStore.value; - } - public set layerId(value: string) { - this._layerStore.set(value); - } - - protected getLayerId(modifier: number): string { - // TODO-web-core: implement - // return Layouts.getLayerId(modifier); - return 'default'; // TODO-web-core: put into LayerNames enum - } - - /** - * Retrieve context including deadkeys from TextStore and apply to Core's context - * - * @param context Context from Keyman Core - * @param textStore Web TextStore - */ - private applyContextFromTextStore(context: km_core_context, textStore: TextStore): void { - // Unlike the desktop Engines, we still track markers (deadkeys) in Engine - // for Web at this time. This is for two reasons: - // 1. We still have the legacy JSKeyboard code paths which manage deadkey - // state - // 2. SyntheticTextStores which are used for rewinding and replaying key - // events in predictive text and multitap need to also replay deadkeys - // - // TODO: Once we make CoreKeyboardProcessor the primary keyboard processor - // and fully deprecate JSKeyboardProcessor, we should consider moving the - // ownership of context back into opaque Core objects within - // SyntheticTextStore, so ownership of context and marker state can be - // managed entirely within Core, KeymanWeb does not need to have knowledge - // of markers, and then we better align with the desktop Engines. - - const caretPosition = textStore.getCaret(); - const text = textStore.getText().substring(0, caretPosition); - const deadkeys = textStore.deadkeys().dks.sort((a, b) => a.p != b.p ? a.p - b.p : a.o - b.o); - const contextItems = new KM_Core.instance.km_core_context_items(); - - const deadkeyIterator = deadkeys.values(); - let deadkey = deadkeyIterator.next(); - let textIndex = 0; - while (!deadkey.done || textIndex < text.length) { - // flush out invalid deadkeys - while (!deadkey.done && (deadkey.value.p < textIndex || deadkey.value.p > text.length)) { - // this should never happen -- it would mean that a deadkey position was < 0, in the - // middle of a surrogate pair, or after the caret. - console.warn(`invalid deadkey '${deadkey.value.d}' position ${deadkey.value.p}`); - deadkey = deadkeyIterator.next(); - } - - // insert 0 or more deadkeys at current index - while (!deadkey.done && deadkey.value.p == textIndex) { - const contextItem = new KM_Core.instance.km_core_context_item(); - contextItem.marker = deadkey.value.d; - contextItems.push_back(contextItem); - deadkey = deadkeyIterator.next(); - } - - // insert next character - if (textIndex < text.length) { - const contextItem = new KM_Core.instance.km_core_context_item(); - contextItem.character = text.codePointAt(textIndex); - contextItems.push_back(contextItem); - textIndex++; - if (contextItem.character > 0xFFFF) { - // we have a surrogate pair, skip other half of surrogate, codePointAt() - // already handled that for us - textIndex++; - } - } - } - - // Add end element - contextItems.push_back(KM_Core.instance.create_end_context()); - - KM_Core.instance.context_set(context, contextItems); - } - - /** - * Saves marker entries from Core's context into a TextStore's deadkey list. - * - * @param context Context from Keyman Core - * @param textStore Web TextStore - */ - private saveMarkersToTextStore(context: km_core_context, textStore: TextStore): void { - const { status, object } = KM_Core.instance.context_get(context); - if (status != KM_CORE_STATUS.OK) { - console.error('KeymanWeb: km_core_context_get failed with status: ' + status); - return; - } - textStore.deadkeys().clear(); - let textIndex = 0; - const contextItems: km_core_context_items = object; - for (let i = 0; i < contextItems.size(); i++) { - const contextItem = contextItems.get(i); - if (contextItem.type !== KM_CORE_CT.MARKER) { - textIndex++; - if (contextItem.character > 0xFFFF) { - // character will be a surrogate pair in the text store - textIndex++; - } - continue; - } - textStore.deadkeys().add(new Deadkey(textIndex, contextItem.marker)); - } - } - - /** - * Processes a keystroke event and updates the text store accordingly. - * Handles the main logic for interpreting and applying keyboard input. - * - * @param {KeyEvent} keyEvent The key event to process. - * @param {TextStore} textStore The current text store context. - * - * @returns {ProcessorAction} The resulting processor action. - */ - public processKeystroke(keyEvent: KeyEvent, textStore: TextStore): ProcessorAction { - - const preInput = SyntheticTextStore.from(textStore, true); - const coreContext = KM_Core.instance.state_context(this.activeKeyboard.state); - - this.applyContextFromTextStore(coreContext, textStore); - - const status = KM_Core.instance.process_event(this.activeKeyboard.state, keyEvent.Lcode, keyEvent.Lmodifiers, keyEvent.source?.type === 'keydown', KM_CORE_EVENT_FLAG.DEFAULT); - // TODO-web-core: properly set flags (#15283) - if (status != KM_CORE_STATUS.OK) { - console.error('KeymanWeb: km_core_process_event failed with status: ' + status); - return null; - } - const processorAction = new ProcessorAction(); - const core_actions = KM_Core.instance.state_get_actions(this.activeKeyboard.state); - - textStore.deleteCharsBeforeCaret(core_actions.code_points_to_delete); - textStore.insertTextBeforeCaret(core_actions.output); - this.saveMarkersToTextStore(coreContext, textStore); - - processorAction.beep = core_actions.do_alert; - if (this.beepHandler && processorAction.beep) { - this.beepHandler(textStore); - } - - processorAction.triggerKeyDefault = core_actions.emit_keystroke; - - this.process_persist_action(core_actions.persist_options); - - // TODO-web-core: do we have to do anything with the new_caps_lock_state? (#15285) - // process_capslock_action(actions->new_caps_lock_state); - - processorAction.transcription = textStore.buildTranscriptionFrom(preInput, keyEvent, false); - - return processorAction; - } - - private process_persist_action(options: km_core_option_item[]): void { - if (this.keyboardInterface.variableStoreSerializer) { - for (const option of options) { - if (option.scope !== KM_CORE_OPTION_SCOPE.OPT_KEYBOARD) { - console.error(`Unsupported option scope: ${option.scope}`); - continue; - } - this.keyboardInterface.variableStoreSerializer.saveStore(toPrefixedKeyboardId(this.activeKeyboard.id), option.key, option.value); - } - } - } - - /** - * Processes post-keystroke actions for the given device and text store. - * Handles any actions that should occur after a keystroke is processed - * or after applying suggestions. - * - * @param {DeviceSpec} device The device specification. - * @param {TextStore} textStore The current text store context. - * - * @returns {ProcessorAction} The resulting processor action, or null if not applicable. - */ - public processPostKeystroke(device: DeviceSpec, textStore: TextStore): ProcessorAction { - // TODO-embed-osk-in-kmx: Implement this method (#15286) - // This gets called after processing a keystroke to process the PostKeystroke group - // (irrelevant for web-core since that is handled in Core), but also after - // applying a suggestion, which we do need to handle. - return null; - } - - /** - * Resets the keyboard context, optionally using the provided text store. - * Clears or reinitializes the context for subsequent keyboard processing. - * - * @param {TextStore} [textStore] - The optional text store to use for resetting context. - */ - public resetContext(textStore?: TextStore): void {} - - /** - * Finalizes the processor action and applies any final changes to the text store. - * Ensures that all necessary updates are completed after processing a key event. - * - * @param {ProcessorAction} data The processor action to finalize. - * @param {TextStore} textStore The text store to update. - */ - public finalizeProcessorAction(data: ProcessorAction, textStore: TextStore): void { } - - /** - * Selects the next keyboard layer based on the provided key event. - * Determines and applies the appropriate layer switch for the OSK. - * - * @param {KeyEvent} keyEvent The key event used to determine the next layer. - * - * @returns {boolean} True if the keyboard layer changed, false otherwise. - */ - public selectLayer(keyEvent: KeyEvent): boolean { - // TODO-embed-osk-in-kmx: Implement this method (#15284) - return false; - } - - /** - * Sets the numeric layer for the given device. - * Switches the keyboard to a numeric input layer if supported. - * - * @param {DeviceSpec} device - The device for which to set the numeric layer. - */ - public setNumericLayer(device: DeviceSpec): void {} - - - /** @internal */ - public unitTestEndPoints = { - saveMarkersToTextStore: this.saveMarkersToTextStore.bind(this), - applyContextFromTextStore: this.applyContextFromTextStore.bind(this), - }; -} diff --git a/web/src/engine/src/core-processor/index.ts b/web/src/engine/src/core-processor/index.ts deleted file mode 100644 index 9f746199a72..00000000000 --- a/web/src/engine/src/core-processor/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { CoreKeyboardInterface } from './coreKeyboardInterface.js'; - -export { CoreKeyboardProcessor } from './coreKeyboardProcessor.js'; - -/** - * these are exported only for unit tests, do not use - */ -export const unitTestEndPoints = { - CoreKeyboardInterface, -}; diff --git a/web/src/engine/src/keyboard-storage/keyboardStub.ts b/web/src/engine/src/keyboard-storage/keyboardStub.ts index 17877dcfe83..cc48db7a1b0 100644 --- a/web/src/engine/src/keyboard-storage/keyboardStub.ts +++ b/web/src/engine/src/keyboard-storage/keyboardStub.ts @@ -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; } diff --git a/web/src/engine/src/keyboard-storage/stubAndKeyboardCache.ts b/web/src/engine/src/keyboard-storage/stubAndKeyboardCache.ts index 1c9117a4334..8564e80ef4f 100644 --- a/web/src/engine/src/keyboard-storage/stubAndKeyboardCache.ts +++ b/web/src/engine/src/keyboard-storage/stubAndKeyboardCache.ts @@ -1,4 +1,4 @@ -import { Keyboard, JSKeyboard, KeyboardLoaderBase as KeyboardLoader, KMXKeyboard } from "keyman/engine/keyboard"; +import { Keyboard, JSKeyboard, KeyboardLoaderBase as KeyboardLoader } from "keyman/engine/keyboard"; import { EventEmitter } from "eventemitter3"; import { KeyboardStub } from "./keyboardStub.js"; @@ -49,13 +49,6 @@ export class StubAndKeyboardCache extends EventEmitter { } shutdown(): void { - for (const kbdId in this.keyboardTable) { - const kbd = this.keyboardTable[kbdId]; - if (kbd instanceof KMXKeyboard) { - (kbd as KMXKeyboard).shutdown(); - } - // TODO-web-core: do we have to do something if instanceof Promise? - } } getKeyboardForStub(stub: KeyboardStub): Keyboard { @@ -203,7 +196,7 @@ export class StubAndKeyboardCache extends EventEmitter { let keyboardID: string; const languageID = arg1 || '---'; - if (arg0 instanceof JSKeyboard || arg0 instanceof KMXKeyboard) { + if (arg0 instanceof JSKeyboard) { keyboardID = arg0.id; } else { keyboardID = arg0 as string; @@ -235,7 +228,7 @@ export class StubAndKeyboardCache extends EventEmitter { * If `false`, only forgets the metadata (stubs). */ forgetKeyboard(keyboard: string | Keyboard, purge: boolean = false) { - const id: string = (keyboard instanceof JSKeyboard || keyboard instanceof KMXKeyboard) ? keyboard.id : toPrefixedKeyboardId(keyboard as string); + const id: string = (keyboard instanceof JSKeyboard) ? keyboard.id : toPrefixedKeyboardId(keyboard as string); if(this.stubSetTable[id]) { delete this.stubSetTable[id]; diff --git a/web/src/engine/src/keyboard/index.ts b/web/src/engine/src/keyboard/index.ts index 6ba350209d6..9d1ebfcc765 100644 --- a/web/src/engine/src/keyboard/index.ts +++ b/web/src/engine/src/keyboard/index.ts @@ -3,7 +3,6 @@ export { ButtonClass, ButtonClasses, LayoutLayer, LayoutFormFactor, LayoutRow, L export { JSKeyboard, LayoutState } from "./keyboards/jsKeyboard.js"; export { Keyboard } from './keyboards/keyboard.js'; export { KeyboardMinimalInterface } from './keyboards/keyboardMinimalInterface.js'; -export { KMXKeyboard } from './keyboards/kmxKeyboard.js'; export { KeyboardHarness, KeyboardKeymanGlobal, MinimalCodesInterface, MinimalKeymanGlobal } from "./keyboards/keyboardHarness.js"; export { NotifyEventCode, KeyboardLoaderBase } from "./keyboards/keyboardLoaderBase.js"; export { KeyboardLoadErrorBuilder, KeyboardMissingError, KeyboardScriptError, KeyboardDownloadError, InvalidKeyboardError } from './keyboards/keyboardLoadError.js' diff --git a/web/src/engine/src/keyboard/keyboards/keyboardLoaderBase.ts b/web/src/engine/src/keyboard/keyboards/keyboardLoaderBase.ts index 2236d696535..ee112130127 100644 --- a/web/src/engine/src/keyboard/keyboards/keyboardLoaderBase.ts +++ b/web/src/engine/src/keyboard/keyboards/keyboardLoaderBase.ts @@ -1,5 +1,3 @@ -import { KM_Core, KM_CORE_STATUS } from 'keyman/engine/core-adapter'; -import { KMXKeyboard } from './kmxKeyboard.js'; import { KeyboardHarness } from "./keyboardHarness.js"; import { KeyboardProperties } from "./keyboardProperties.js"; import { KeyboardLoadErrorBuilder, StubBasedErrorBuilder, UriBasedErrorBuilder } from './keyboardLoadError.js'; @@ -59,12 +57,8 @@ export abstract class KeyboardLoaderBase { if (this.isKMXKeyboard(byteArray)) { // KMX or LDML (KMX+) keyboard - const name = this.extractIdFromUrl(uri); - const result = KM_Core.instance.keyboard_load_from_blob(name, byteArray); - if (result.status == KM_CORE_STATUS.OK) { - return new KMXKeyboard(result.object); - } - throw errorBuilder.invalidKeyboard(new Error(`Loading KMX keyboard from ${uri} failed with status ${result.status}`)); + // For version 19, disable loading of .kmx keyboards + throw new Error("TODO: .kmx files are not currently supported"); } let script: string; @@ -78,11 +72,6 @@ export abstract class KeyboardLoaderBase { return await this.loadKeyboardFromScript(script, errorBuilder); } - private extractIdFromUrl(uri: string): string { - // Extract filename without extension from the URL - return uri.split('/').pop().replace(/\.[^/.]+$/, ''); - } - protected abstract loadKeyboardBlob(uri: string, errorBuilder: KeyboardLoadErrorBuilder): Promise; protected abstract loadKeyboardFromScript(scriptSrc: string, errorBuilder: KeyboardLoadErrorBuilder): Promise; diff --git a/web/src/engine/src/keyboard/keyboards/keyboardProperties.ts b/web/src/engine/src/keyboard/keyboards/keyboardProperties.ts index 411271e2546..b9b32b444a9 100644 --- a/web/src/engine/src/keyboard/keyboards/keyboardProperties.ts +++ b/web/src/engine/src/keyboard/keyboards/keyboardProperties.ts @@ -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 { diff --git a/web/src/engine/src/keyboard/keyboards/kmxKeyboard.ts b/web/src/engine/src/keyboard/keyboards/kmxKeyboard.ts deleted file mode 100644 index dfce5943a7d..00000000000 --- a/web/src/engine/src/keyboard/keyboards/kmxKeyboard.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { DeviceSpec } from 'keyman/common/web-utils'; -import { - KM_Core, km_core_keyboard, KM_CORE_KMX_ENV, - KM_CORE_OPTION_SCOPE, km_core_state, KM_CORE_STATUS -} from 'keyman/engine/core-adapter'; -import { ActiveKey, ActiveLayout, ActiveSubKey } from './activeLayout.js'; -import { StateKeyMap } from './stateKeyMap.js'; -import { KeyEvent } from '../keyEvent.js'; -import { TextStore } from '../textStore.js'; -import { NotifyEventCode } from './keyboardLoaderBase.js'; -import { Keyboard } from './keyboard.js'; - -/** - * Acts as a wrapper class for KMX(+) Keyman keyboards - */ -export class KMXKeyboard implements Keyboard { - private _state: km_core_state | null = null; - - public constructor(private _keyboard: km_core_keyboard) { - const environment_opts = - [ - { - scope: KM_CORE_OPTION_SCOPE.OPT_ENVIRONMENT, - key: KM_CORE_KMX_ENV.PLATFORM, - // TODO-web-core: Turn touch off for non-touch targets; read proper platform string from web (#15289) - value: "web iphone ipad androidphone androidtablet mobile touch hardware android phone" - }, { - scope: KM_CORE_OPTION_SCOPE.OPT_ENVIRONMENT, - key: KM_CORE_KMX_ENV.BASELAYOUT, - value: "kbdus.dll" // TODO: Base layout assumed to be US in v19 - }, { - scope: KM_CORE_OPTION_SCOPE.OPT_ENVIRONMENT, - key: KM_CORE_KMX_ENV.BASELAYOUTALT, - value: "us", // TODO: Base layout assumed to be US in v19 - },{ - scope: KM_CORE_OPTION_SCOPE.OPT_ENVIRONMENT, - key: KM_CORE_KMX_ENV.SIMULATEALTGR, - value: "0" // TODO: We won't support simulating AltGr option in v19 - see also emulatesAltGr - } - ] - const result = KM_Core.instance.state_create(_keyboard, environment_opts); - if (result.status == KM_CORE_STATUS.OK) { - this._state = result.object; - } - } - - public shutdown() { - if (this._state) { - this._state.delete(); - this._state = null; - } - if (this._keyboard) { - KM_Core.instance.keyboard_dispose(this._keyboard); - this._keyboard = null; - } - } - - public constructKeyEvent(key: ActiveKey | ActiveSubKey, device: DeviceSpec, stateKeys: StateKeyMap): KeyEvent { - // TODO-embed-osk-in-kmx: Implement this method (#15290) - return null; - } - - public get isMnemonic(): boolean { - return false; - } - - public get version(): string { - const attrs = KM_Core.instance.keyboard_get_attrs(this._keyboard); - const version = attrs.object.version_string; - attrs.delete(); - return version; - } - - public get id(): string { - const attrs = KM_Core.instance.keyboard_get_attrs(this._keyboard); - const id = attrs.object.id; - attrs.delete(); - return id; - } - - public get name(): string { - // TODO-WEB-CORE: get keyboard name from Core API (to be implemented) - return this.id; - } - - public get keyboard(): km_core_keyboard { - return this._keyboard; - } - - public get state(): km_core_state { - return this._state; - } - - public get isChiral(): boolean { - // TODO-embed-osk-in-kmx: Implement this method (#15290) - // Only relevant for OSK - return false; - } - - /** - * Signifies whether or not a layout or OSK should include AltGr / Right-alt emulation for this keyboard. - * @return {boolean} - */ - public get emulatesAltGr(): boolean { - // TODO: We won't support simulating AltGr option in v19 - see also c'tor - return false; - } - - /** - * Notifies keyboard of keystroke or other event - * - * @param {NotifyEventCode} eventCode key code (16-18: Shift, Control or Alt), - * or 0 for focus - * @param {TextStore} textStore textStore - * @param {number} data 1 for KeyDown or FocusReceived, - * 0 for KeyUp or FocusLost - */ - public notify(eventCode: NotifyEventCode, textStore: TextStore, data: number): void { - // TODO: implement for IMX (cf #2239 and #7928) - } - - /** - * Indicates whether the keyboard layout is designed for right-to-left scripts. - * This can be used by consumers to adjust UI layout and text direction for RTL keyboards. - * - * @returns {boolean} True if the keyboard is right-to-left, otherwise false. - */ - public get isRTL(): boolean { - // TODO-embed-osk-in-kmx: Hook up with Core API (#15482) - return false; - } - - /** - * Returns always false because (legacy) pick lists (Chinese, Japanese, Korean, etc.) - * are not supported when using KMX keyboards. - */ - public get isCJK(): boolean { - // always return false - return false; - } - - /** - * Returns an ActiveLayout object representing the keyboard's layout for this - * form factor. May return null if a custom desktop "help" OSK is defined, - * as with sil_euro_latin. - * - * In such cases, please use either `helpText` or `insertHelpHTML` instead. - * @param formFactor {string} The desired form factor for the layout. - */ - public layout(formFactor: DeviceSpec.FormFactor): ActiveLayout { - // TODO-embed-osk-in-kmx: Implement this method (#15290) - return null; - } - - /** - * Indicates whether the keyboard's desktop layout should be used for the - * specified device. - * - * @param device {DeviceSpec} The device specification to check - * @returns {boolean} True if the desktop layout should be used for - * this device, otherwise false. - */ - public usesDesktopLayoutOnDevice(device: DeviceSpec): boolean { - // TODO-embed-osk-in-kmx: Implement this method (#15290) - return true; - } - - /** - * CSS styling for the on-screen keyboard. - * - * @returns {string} CSS style string for the OSK - */ - public get oskStyling(): string { - // TODO-embed-osk-in-kmx: Implement this method (#15290) - return ''; - } - - -} diff --git a/web/src/engine/src/main/headless/inputProcessor.ts b/web/src/engine/src/main/headless/inputProcessor.ts index 46b049ce930..b7c4c77305c 100644 --- a/web/src/engine/src/main/headless/inputProcessor.ts +++ b/web/src/engine/src/main/headless/inputProcessor.ts @@ -5,8 +5,6 @@ import { LanguageProcessor } from "./languageProcessor.js"; import type { ModelSpec, PathConfiguration } from "keyman/engine/interfaces"; import { globalObject, DeviceSpec, isEmptyTransform } from "keyman/common/web-utils"; -import { CoreKeyboardProcessor } from 'keyman/engine/core-processor'; - import { Codes, JSKeyboard, // required to be able to distinguish between JS and Core kbd processor @@ -38,7 +36,6 @@ export class InputProcessor { */ private contextDevice: DeviceSpec; private jsKbdProcessor: JSKeyboardProcessor; - private coreKbdProcessor: CoreKeyboardProcessor; private _keyboardProcessor: KeyboardProcessor; private _languageProcessor: LanguageProcessor; @@ -58,7 +55,6 @@ export class InputProcessor { } public async init(paths: PathConfiguration, storeSerializer: VariableStoreSerializer): Promise { - await this.coreKbdProcessor.init(paths.basePath, storeSerializer); } public get languageProcessor(): LanguageProcessor { @@ -79,13 +75,7 @@ export class InputProcessor { public set activeKeyboard(keyboard: Keyboard) { - if (keyboard instanceof JSKeyboard || keyboard == null) { - // TODO-web-core: consider keyboard==null scenario; which keyboardProcessor should be active? - this._keyboardProcessor = this.jsKbdProcessor; - } else { - this._keyboardProcessor = this.coreKbdProcessor; - } - + this._keyboardProcessor = this.jsKbdProcessor; this.keyboardInterface.activeKeyboard = keyboard; // All old deadkeys and keyboard-specific cache should immediately be invalidated diff --git a/web/src/engine/src/main/keyboardInterfaceBase.ts b/web/src/engine/src/main/keyboardInterfaceBase.ts index 4f830561035..fbf1d20f24b 100644 --- a/web/src/engine/src/main/keyboardInterfaceBase.ts +++ b/web/src/engine/src/main/keyboardInterfaceBase.ts @@ -85,7 +85,6 @@ export class KeyboardInterfaceBase diff --git a/web/src/samples/samplehdr.js b/web/src/samples/samplehdr.js index 129d54fc011..1eedf3a04d0 100644 --- a/web/src/samples/samplehdr.js +++ b/web/src/samples/samplehdr.js @@ -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. - - 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. + 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 + + 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]; + 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); + } } diff --git a/web/src/samples/simplest/index.html b/web/src/samples/simplest/index.html index c2ed9fd70bc..55b8cb2067a 100644 --- a/web/src/samples/simplest/index.html +++ b/web/src/samples/simplest/index.html @@ -36,8 +36,7 @@ diff --git a/web/src/test/auto/e2e/baseline/baseline.tests.ts b/web/src/test/auto/e2e/baseline/baseline.tests.ts index 527099e6c3a..dbc4382548a 100644 --- a/web/src/test/auto/e2e/baseline/baseline.tests.ts +++ b/web/src/test/auto/e2e/baseline/baseline.tests.ts @@ -79,7 +79,7 @@ test.describe('Baseline tests', () => { // Find all k_*.kmn files in testDir const files = fs.readdirSync(KeymanRoot() + testDir).filter(f => f.match(/^k_.*\.kmn$/)); - for (const ext of ['.kmx', '.js']) { + for (const ext of ['.js']) { test.describe(`${ext} tests`, () => { for (const file of files) { test(file, async ({ page }) => { diff --git a/web/src/test/auto/e2e/keyboard.tests.ts b/web/src/test/auto/e2e/keyboard.tests.ts index 590da8a840b..a9f2c0b69eb 100644 --- a/web/src/test/auto/e2e/keyboard.tests.ts +++ b/web/src/test/auto/e2e/keyboard.tests.ts @@ -3,37 +3,6 @@ */ import { test, expect, type Page, type Locator } from '@playwright/test'; -test.describe('KMX keyboards', function () { - let textarea: Locator; - const beforeEach = async (page: Page) => { - await page.goto('http://localhost:3000/src/test/manual/web/kmxkeyboard.html?type=kmx'); - await page.evaluate(async () => { await window.KmwLoaded; }); - textarea = page.locator('#inputarea'); - await textarea.click(); - // we don't have a OSK yet, so don't wait for it to appear - } - - test('can type on a (simulated) hardware keyboard', async ({ page }) => { - await beforeEach(page); - await page.keyboard.press('x'); - await page.keyboard.press('j'); - await page.keyboard.press('m'); - await page.keyboard.press('Shift+e'); - await page.keyboard.press('r'); - await expect(textarea).toHaveValue('ខ្មែរ', { timeout: 500 }); - }); - - test('can type on a (simulated) hardware keyboard with reordering', async ({ page }) => { - await beforeEach(page); - await page.keyboard.press('x'); - await page.keyboard.press('Shift+e'); - await page.keyboard.press('j'); - await page.keyboard.press('m'); - await page.keyboard.press('r'); - await expect(textarea).toHaveValue('ខ្មែរ', { timeout: 500 }); - }); -}); - test.describe('JS keyboards', function () { let textarea: Locator; const beforeEach = async (page: Page) => { diff --git a/web/src/test/auto/headless/engine/core-adapter/core-adapter.tests.ts b/web/src/test/auto/headless/engine/core-adapter/core-adapter.tests.ts deleted file mode 100644 index c688214d259..00000000000 --- a/web/src/test/auto/headless/engine/core-adapter/core-adapter.tests.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Keyman is copyright (C) SIL Global. MIT License. - */ - -import { assert } from 'chai'; -import { KM_Core, KM_CORE_STATUS } from 'keyman/engine/core-adapter'; -import { coreurl, loadKeyboardBlob } from '../loadKeyboardHelper.js'; - -// Test the KM_Core interface. -describe('KM_Core', function () { - this.timeout(5000); // increased timeout for async loading - - it('can initialize without errors', async function () { - const km_core = await KM_Core.createCoreProcessor(coreurl); - assert.isNotNull(km_core); - }); - - it('can call temp function', async function () { - const km_core = await KM_Core.createCoreProcessor(coreurl); - const a = km_core.tmp_wasm_attributes(); - assert.isOk(a); - assert.isNumber(a.max_context); - console.dir(a); - }); - - it('can load a keyboard from blob', async function () { - const km_core = await KM_Core.createCoreProcessor(coreurl); - const blob = await loadKeyboardBlob('/common/test/resources/keyboards/test_8568_deadkeys.kmx') - const result = km_core.keyboard_load_from_blob('test', blob); - assert.equal(result.status, KM_CORE_STATUS.OK); - assert.isOk(result.object); - result.delete(); - }); - - it('can get version from keyboard', async function () { - const km_core = await KM_Core.createCoreProcessor(coreurl); - const blob = await loadKeyboardBlob('/common/test/resources/keyboards/test_8568_deadkeys.kmx') - const keyboard = km_core.keyboard_load_from_blob('test', blob); - const result = km_core.keyboard_get_attrs(keyboard.object); - - assert.equal(result.status, KM_CORE_STATUS.OK); - assert.isOk(result.object); - assert.equal(result.object.version_string, '0.0'); - result.delete(); - }); -}); diff --git a/web/src/test/auto/headless/engine/core-processor/coreKeyboardProcessor.tests.ts b/web/src/test/auto/headless/engine/core-processor/coreKeyboardProcessor.tests.ts deleted file mode 100644 index 3534c9cf371..00000000000 --- a/web/src/test/auto/headless/engine/core-processor/coreKeyboardProcessor.tests.ts +++ /dev/null @@ -1,616 +0,0 @@ -/* - * Keyman is copyright (C) SIL Global. MIT License. - */ - -import { assert } from 'chai'; -import sinon from 'sinon'; -import { KM_Core, km_core_context, km_core_keyboard, km_core_state, KM_CORE_CT, KM_CORE_STATUS, km_core_context_items } from 'keyman/engine/core-adapter'; -import { coreurl, loadKeyboardBlob } from '../loadKeyboardHelper.js'; -import { DeviceSpec } from 'keyman/common/web-utils'; -import { Codes, Deadkey, KeyEvent, KMXKeyboard, SyntheticTextStore } from 'keyman/engine/keyboard'; -import { CoreKeyboardProcessor } from 'keyman/engine/core-processor'; -import { VariableStoreCookieSerializer } from 'keyman/engine/main'; - -describe('CoreKeyboardProcessor', function () { - const loadKeyboard = function (name: string): km_core_keyboard { - const blob = loadKeyboardBlob(name); - const result = KM_Core.instance.keyboard_load_from_blob(name, blob); - assert.equal(result.status, 0); - assert.isOk(result.object); - return result.object; - }; - - const createState = function (keyboardName: string): km_core_state { - const keyboard = loadKeyboard(keyboardName); - const state = KM_Core.instance.state_create(keyboard, []); - assert.equal(state.status, 0); - assert.isOk(state.object); - return state.object; - }; - - const addContextItem = function (contextItems: km_core_context_items, c: string | number, isMarker: boolean) { - const item = new KM_Core.instance.km_core_context_item(); - if (isMarker) { - item.marker = c as number; - } else if (typeof c == 'number') { - item.character = c; - } else { - item.character = c.codePointAt(0); - } - contextItems.push_back(item); - }; - - let coreProcessor: CoreKeyboardProcessor; - let state: km_core_state; - let context: km_core_context; - let textStore: SyntheticTextStore; - let sandbox: sinon.SinonSandbox; - const device = { - formFactor: 'desktop', - OS: 'windows', - browser: 'native', - touchable: false - } as DeviceSpec; - - describe('saveMarkersToTextStore', function () { - beforeEach(async function () { - coreProcessor = new CoreKeyboardProcessor(device, 'us'); - await coreProcessor.init(coreurl, new VariableStoreCookieSerializer()); - state = createState('/common/test/resources/keyboards/test_8568_deadkeys.kmx'); - context = KM_Core.instance.state_context(state); - sandbox = sinon.createSandbox(); - Deadkey.ordinalSeed = 0; - }); - - afterEach(() => { - sandbox.restore(); - sandbox = null; - }) - - it('saves markers to TextStore (BMP)', function() { - // Setup - textStore = new SyntheticTextStore('abcd', 3); - // Text index : 0 1 1 1 2 3 3 - // context index: 0 1 2 3 4 5 6 - // ContextItems : a dk1 dk2 b c dk3 d - const contextItems = new KM_Core.instance.km_core_context_items(); - addContextItem(contextItems, 'a', false); - addContextItem(contextItems, 1, true); // deadkey 1 - addContextItem(contextItems, 2, true); // deadkey 2 - addContextItem(contextItems, 'b', false); - addContextItem(contextItems, 'c', false); - addContextItem(contextItems, 3, true); // deadkey 3 - addContextItem(contextItems, 'd', false); - contextItems.push_back(KM_Core.instance.create_end_context()); - - sandbox.stub(KM_Core.instance, 'context_get').returns({ - status: KM_CORE_STATUS.OK, - object: contextItems, - delete: function (): void {} - }); - - // Execute - coreProcessor.unitTestEndPoints.saveMarkersToTextStore(context, textStore); - - // Verify - assert.equal(textStore.deadkeys().count(), 3, 'Should have 3 deadkeys'); - assert.equal(textStore.deadkeys().dks[0].toString(), 'Deadkey { p: 1, d: 1, o: 0, matched: 0}', 'dks[0]'); - assert.equal(textStore.deadkeys().dks[1].toString(), 'Deadkey { p: 1, d: 2, o: 1, matched: 0}', 'dks[1]'); - assert.equal(textStore.deadkeys().dks[2].toString(), 'Deadkey { p: 3, d: 3, o: 2, matched: 0}', 'dks[2]'); - }); - - it('saves markers to TextStore (SMP)', function () { - // Setup - textStore = new SyntheticTextStore('𐌀𐌁𐌂𐌃', 6); // U+10300 U+10301 U+10302 U+10303 - // Text index : 0 1 2 2 2 3 4 5 6 6 7 - // context index: 0 1 2 3 4 5 6 - // ContextItems : 𐌀 dk1 dk2 𐌁 𐌂 dk3 END - // ContextItem.character is a UTF-32 value! - const contextItems = new KM_Core.instance.km_core_context_items(); - addContextItem(contextItems, '𐌀', false); - addContextItem(contextItems, 1, true); // deadkey 1 - addContextItem(contextItems, 2, true); // deadkey 2 - addContextItem(contextItems, '𐌁', false); - addContextItem(contextItems, '𐌂', false); - addContextItem(contextItems, 3, true); // deadkey 3 - addContextItem(contextItems, '𐌃', false); - contextItems.push_back(KM_Core.instance.create_end_context()); - - sandbox.stub(KM_Core.instance, 'context_get').returns({ - status: KM_CORE_STATUS.OK, - object: contextItems, - delete: function (): void { } - }); - - // Execute - coreProcessor.unitTestEndPoints.saveMarkersToTextStore(context, textStore); - - // Verify - assert.equal(textStore.deadkeys().count(), 3, 'Should have 3 deadkeys'); - assert.equal(textStore.deadkeys().dks[0].toString(), 'Deadkey { p: 2, d: 1, o: 0, matched: 0}', 'dks[0]'); - assert.equal(textStore.deadkeys().dks[1].toString(), 'Deadkey { p: 2, d: 2, o: 1, matched: 0}', 'dks[1]'); - assert.equal(textStore.deadkeys().dks[2].toString(), 'Deadkey { p: 6, d: 3, o: 2, matched: 0}', 'dks[2]'); - }); - - it('can save to TextStore without markers (BMP)', function () { - // Setup - textStore = new SyntheticTextStore('abcd', 3); - // Text index : 0 1 2 3 - // context index: 0 1 2 3 - // ContextItems : a b c d - const contextItems = new KM_Core.instance.km_core_context_items(); - addContextItem(contextItems, 'a', false); - addContextItem(contextItems, 'b', false); - addContextItem(contextItems, 'c', false); - addContextItem(contextItems, 'd', false); - contextItems.push_back(KM_Core.instance.create_end_context()); - - sandbox.stub(KM_Core.instance, 'context_get').returns({ - status: KM_CORE_STATUS.OK, - object: contextItems, - delete: function (): void { } - }); - - // Execute - coreProcessor.unitTestEndPoints.saveMarkersToTextStore(context, textStore); - - // Verify - assert.equal(textStore.deadkeys().count(), 0, 'Should have 0 deadkeys'); - }); - - it('can save to TextStore without markers (SMP)', function () { - // Setup - textStore = new SyntheticTextStore('𐌀𐌁𐌂𐌃', 6); // U+10300 U+10301 U+10302 U+10303 - // Text index : 0 1 2 3 4 5 - // context index: 0 1 2 3 - // ContextItems : 𐌀 𐌁 𐌂 END - // ContextItem.character is a UTF-32 value! - const contextItems = new KM_Core.instance.km_core_context_items(); - addContextItem(contextItems, '𐌀', false); - addContextItem(contextItems, '𐌁', false); - addContextItem(contextItems, '𐌂', false); - addContextItem(contextItems, '𐌃', false); - contextItems.push_back(KM_Core.instance.create_end_context()); - - sandbox.stub(KM_Core.instance, 'context_get').returns({ - status: KM_CORE_STATUS.OK, - object: contextItems, - delete: function (): void { } - }); - - // Execute - coreProcessor.unitTestEndPoints.saveMarkersToTextStore(context, textStore); - - // Verify - assert.equal(textStore.deadkeys().count(), 0, 'Should have 0 deadkeys'); - }); - - it('saves markers to TextStore for empty text', function () { - // Setup - textStore = new SyntheticTextStore('', 0); - // Text index : 0 0 0 - // context index: 0 1 2 - // ContextItems : dk1 dk2 END - const contextItems = new KM_Core.instance.km_core_context_items(); - addContextItem(contextItems, 1, true); // deadkey 1 - addContextItem(contextItems, 2, true); // deadkey 2 - contextItems.push_back(KM_Core.instance.create_end_context()); - - sandbox.stub(KM_Core.instance, 'context_get').returns({ - status: KM_CORE_STATUS.OK, - object: contextItems, - delete: function (): void { } - }); - - // Execute - coreProcessor.unitTestEndPoints.saveMarkersToTextStore(context, textStore); - - // Verify - assert.equal(textStore.deadkeys().count(), 2, 'Should have 2 deadkeys'); - assert.equal(textStore.deadkeys().dks[0].toString(), 'Deadkey { p: 0, d: 1, o: 0, matched: 0}', 'dks[0]'); - assert.equal(textStore.deadkeys().dks[1].toString(), 'Deadkey { p: 0, d: 2, o: 1, matched: 0}', 'dks[1]'); - }); - }); - - - describe('applyContextFromTextStore', function () { - beforeEach(async function () { - coreProcessor = new CoreKeyboardProcessor(device, 'us'); - await coreProcessor.init(coreurl, new VariableStoreCookieSerializer()); - state = createState('/common/test/resources/keyboards/test_8568_deadkeys.kmx'); - context = KM_Core.instance.state_context(state); - }); - - it('applies deadkeys from TextStore to Core context (BMP)', function () { - // Setup - textStore = new SyntheticTextStore('abcd', 3); - textStore.deadkeys().add(new Deadkey(1, 1)); // before 'b' - textStore.deadkeys().add(new Deadkey(1, 2)); - textStore.deadkeys().add(new Deadkey(3, 3)); // before 'd' - - // Execute - coreProcessor.unitTestEndPoints.applyContextFromTextStore(context, textStore); - - // Verify - const result = KM_Core.instance.context_get(context); - assert.equal(result.status, KM_CORE_STATUS.OK); - const items = result.object; - - // Text index : 0 1 1 1 2 3 3 - // Text: : a b c | d - // context index: 0 1 2 3 4 5 6 - // ContextItems : a dk1 dk2 b c dk3 END - assert.equal(items.size(), 7, 'Should have 7 context items'); - assert.equal(items.get(0).type, KM_CORE_CT.CHAR, 'Item 0 should be CHAR'); - assert.equal(items.get(0).character, 'a'.charCodeAt(0), 'Item 0 should be "a"'); - assert.equal(items.get(1).type, KM_CORE_CT.MARKER, 'Item 1 should be MARKER'); - assert.equal(items.get(1).marker, 1, 'Item 1 should be marker 1'); - assert.equal(items.get(2).type, KM_CORE_CT.MARKER, 'Item 2 should be MARKER'); - assert.equal(items.get(2).marker, 2, 'Item 2 should be marker 2'); - assert.equal(items.get(3).type, KM_CORE_CT.CHAR, 'Item 3 should be CHAR'); - assert.equal(items.get(3).character, 'b'.charCodeAt(0), 'Item 3 should be "b"'); - assert.equal(items.get(4).type, KM_CORE_CT.CHAR, 'Item 4 should be CHAR'); - assert.equal(items.get(4).character, 'c'.charCodeAt(0), 'Item 4 should be "c"'); - assert.equal(items.get(5).type, KM_CORE_CT.MARKER, 'Item 5 should be MARKER'); - assert.equal(items.get(5).marker, 3, 'Item 5 should be marker 3'); - assert.equal(items.get(6).type, KM_CORE_CT.END, 'Item 6 should be END'); - result.delete(); - }); - - it('applies deadkeys from TextStore to Core context (SMP)', function () { - // Setup - // Use a string with Old Italic letters which will consist of surrogate pairs - // in a UTF-16 string. - textStore = new SyntheticTextStore('𐌀𐌁𐌂𐌃', 6); // U+10300 U+10301 U+10302 U+10303 - textStore.deadkeys().add(new Deadkey(2, 1)); // before '𐌁' - textStore.deadkeys().add(new Deadkey(2, 2)); - textStore.deadkeys().add(new Deadkey(6, 3)); // before '𐌃' - - // Execute - coreProcessor.unitTestEndPoints.applyContextFromTextStore(context, textStore); - - // Verify - const result = KM_Core.instance.context_get(context); - assert.equal(result.status, KM_CORE_STATUS.OK); - const items = result.object; - - // Text index : 0 1 2 2 2 3 4 5 6 6 7 - // Text: : 𐌀 𐌁 𐌂 | 𐌃 - // context index: 0 1 2 3 4 5 6 - // ContextItems : 𐌀 dk1 dk2 𐌁 𐌂 dk3 END - // ContextItem.character is a UTF-32 value! - assert.equal(items.size(), 7, 'Should have 7 context items'); - assert.equal(items.get(0).type, KM_CORE_CT.CHAR, 'Item 0 should be CHAR'); - assert.equal(items.get(0).character, 0x10300, 'Item 0 should be "𐌀"'); - assert.equal(items.get(1).type, KM_CORE_CT.MARKER, 'Item 1 should be MARKER'); - assert.equal(items.get(1).marker, 1, 'Item 1 should be marker 1'); - assert.equal(items.get(2).type, KM_CORE_CT.MARKER, 'Item 2 should be MARKER'); - assert.equal(items.get(2).marker, 2, 'Item 2 should be marker 2'); - assert.equal(items.get(3).type, KM_CORE_CT.CHAR, 'Item 3 should be CHAR'); - assert.equal(items.get(3).character, 0x10301, 'Item 3 should be "𐌁"'); - assert.equal(items.get(4).type, KM_CORE_CT.CHAR, 'Item 4 should be CHAR'); - assert.equal(items.get(4).character, 0x10302, 'Item 4 should be "𐌂"'); - assert.equal(items.get(5).type, KM_CORE_CT.MARKER, 'Item 5 should be MARKER'); - assert.equal(items.get(5).marker, 3, 'Item 5 should be marker 3'); - assert.equal(items.get(6).type, KM_CORE_CT.END, 'Item 6 should be END'); - result.delete(); - }); - - it('applies text from TextStore to Core context without deadkeys (BMP)', function () { - // Setup - textStore = new SyntheticTextStore('abcd', 3); - - // Execute - coreProcessor.unitTestEndPoints.applyContextFromTextStore(context, textStore); - - // Verify - const result = KM_Core.instance.context_get(context); - assert.equal(result.status, KM_CORE_STATUS.OK); - const items = result.object; - - // Text index : 0 1 2 3 - // Text: : a b c | d - // context index: 0 1 2 3 - // ContextItems : a b c END - assert.equal(items.size(), 4, 'Should have 4 context items'); - assert.equal(items.get(0).type, KM_CORE_CT.CHAR, 'Item 0 should be CHAR'); - assert.equal(items.get(0).character, 'a'.charCodeAt(0), 'Item 0 should be "a"'); - assert.equal(items.get(1).type, KM_CORE_CT.CHAR, 'Item 1 should be CHAR'); - assert.equal(items.get(1).character, 'b'.charCodeAt(0), 'Item 1 should be "b"'); - assert.equal(items.get(2).type, KM_CORE_CT.CHAR, 'Item 2 should be CHAR'); - assert.equal(items.get(2).character, 'c'.charCodeAt(0), 'Item 2 should be "c"'); - assert.equal(items.get(3).type, KM_CORE_CT.END, 'Item 3 should be END'); - result.delete(); - }); - - it('applies text from TextStore to Core context without deadkeys (SMP)', function () { - // Setup - // Use a string with Old Italic letters which will consist of surrogate pairs - // in a UTF-16 string. - textStore = new SyntheticTextStore('𐌀𐌁𐌂𐌃', 6); // U+10300 U+10301 U+10302 U+10303 - - // Execute - coreProcessor.unitTestEndPoints.applyContextFromTextStore(context, textStore); - - // Verify - const result = KM_Core.instance.context_get(context); - assert.equal(result.status, KM_CORE_STATUS.OK); - const items = result.object; - - // Text index : 0 1 2 3 4 5 6 7 - // Text: : 𐌀 𐌁 𐌂 | 𐌃 - // context index: 0 1 2 3 - // ContextItems : 𐌀 𐌁 𐌂 END - // ContextItem.character is a UTF-32 value! - assert.equal(items.size(), 4, 'Should have 4 context items'); - assert.equal(items.get(0).type, KM_CORE_CT.CHAR, 'Item 0 should be CHAR'); - assert.equal(items.get(0).character, 0x10300, 'Item 0 should be "𐌀"'); - assert.equal(items.get(1).type, KM_CORE_CT.CHAR, 'Item 1 should be CHAR'); - assert.equal(items.get(1).character, 0x10301, 'Item 1 should be "𐌁"'); - assert.equal(items.get(2).type, KM_CORE_CT.CHAR, 'Item 2 should be CHAR'); - assert.equal(items.get(2).character, 0x10302, 'Item 2 should be "𐌂"'); - assert.equal(items.get(3).type, KM_CORE_CT.END, 'Item 3 should be END'); - result.delete(); - }); - - it('applies text from TextStore to Core context also after deadkeys', function () { - // Setup - textStore = new SyntheticTextStore('abcd', 3); - textStore.deadkeys().add(new Deadkey(1, 1)); // before 'b' - - // Execute - coreProcessor.unitTestEndPoints.applyContextFromTextStore(context, textStore); - - // Verify - const result = KM_Core.instance.context_get(context); - assert.equal(result.status, KM_CORE_STATUS.OK); - const items = result.object; - - // Text index : 0 1 1 2 3 - // Text: : a b c | d - // context index: 0 1 2 3 4 - // ContextItems : a dk1 b c END - assert.equal(items.size(), 5, 'Should have 5 context items'); - assert.equal(items.get(0).type, KM_CORE_CT.CHAR, 'Item 0 should be CHAR'); - assert.equal(items.get(0).character, 'a'.charCodeAt(0), 'Item 0 should be "a"'); - assert.equal(items.get(1).type, KM_CORE_CT.MARKER, 'Item 1 should be MARKER'); - assert.equal(items.get(1).marker, 1, 'Item 1 should be marker 1'); - assert.equal(items.get(2).type, KM_CORE_CT.CHAR, 'Item 2 should be CHAR'); - assert.equal(items.get(2).character, 'b'.charCodeAt(0), 'Item 2 should be "b"'); - assert.equal(items.get(3).type, KM_CORE_CT.CHAR, 'Item 3 should be CHAR'); - assert.equal(items.get(3).character, 'c'.charCodeAt(0), 'Item 3 should be "c"'); - assert.equal(items.get(4).type, KM_CORE_CT.END, 'Item 4 should be END'); - result.delete(); - }); - - it('works with empty text', function () { - // Setup - textStore = new SyntheticTextStore('', 0); - - // Execute - coreProcessor.unitTestEndPoints.applyContextFromTextStore(context, textStore); - - // Verify - const result = KM_Core.instance.context_get(context); - assert.equal(result.status, KM_CORE_STATUS.OK); - const items = result.object; - - // Text index : 0 - // Text: : | - // context index: 0 - // ContextItems : END - assert.equal(items.size(), 1, 'Should have 1 context item'); - assert.equal(items.get(0).type, KM_CORE_CT.END, 'Item 0 should be END'); - result.delete(); - }); - - it('works with deadkey before the text', function () { - // Setup - textStore = new SyntheticTextStore('abcd', 3); - textStore.deadkeys().add(new Deadkey(0, 1)); // before 'a' - - // Execute - coreProcessor.unitTestEndPoints.applyContextFromTextStore(context, textStore); - - // Verify - const result = KM_Core.instance.context_get(context); - assert.equal(result.status, KM_CORE_STATUS.OK); - const items = result.object; - - // Text index : 0 0 1 2 3 - // Text: : a b c | d - // context index: 0 1 2 3 4 - // ContextItems : dk1 a b c END - assert.equal(items.size(), 5, 'Should have 5 context items'); - assert.equal(items.get(0).type, KM_CORE_CT.MARKER, 'Item 0 should be MARKER'); - assert.equal(items.get(0).marker, 1, 'Item 0 should be marker 1'); - assert.equal(items.get(1).type, KM_CORE_CT.CHAR, 'Item 1 should be CHAR'); - assert.equal(items.get(1).character, 'a'.charCodeAt(0), 'Item 1 should be "a"'); - assert.equal(items.get(2).type, KM_CORE_CT.CHAR, 'Item 2 should be CHAR'); - assert.equal(items.get(2).character, 'b'.charCodeAt(0), 'Item 2 should be "b"'); - assert.equal(items.get(3).type, KM_CORE_CT.CHAR, 'Item 3 should be CHAR'); - assert.equal(items.get(3).character, 'c'.charCodeAt(0), 'Item 3 should be "c"'); - assert.equal(items.get(4).type, KM_CORE_CT.END, 'Item 4 should be END'); - result.delete(); - }); - - it('works with deadkeys before empty text', function () { - // Setup - textStore = new SyntheticTextStore('', 0); - textStore.deadkeys().add(new Deadkey(0, 1)); // before caret - textStore.deadkeys().add(new Deadkey(0, 2)); // before caret - - // Execute - coreProcessor.unitTestEndPoints.applyContextFromTextStore(context, textStore); - - // Verify - const result = KM_Core.instance.context_get(context); - assert.equal(result.status, KM_CORE_STATUS.OK); - const items = result.object; - - // Text index : 0 - // Text: : | - // context index: 0 1 2 - // ContextItems : dk1 dk2 END - assert.equal(items.size(), 3, 'Should have 3 context items'); - assert.equal(items.get(0).type, KM_CORE_CT.MARKER, 'Item 0 should be MARKER'); - assert.equal(items.get(0).marker, 1, 'Item 0 should be marker 1'); - assert.equal(items.get(1).type, KM_CORE_CT.MARKER, 'Item 1 should be MARKER'); - assert.equal(items.get(1).marker, 2, 'Item 1 should be marker 2'); - assert.equal(items.get(2).type, KM_CORE_CT.END, 'Item 2 should be END'); - result.delete(); - }); - - it('skips invalid deadkeys', function () { - // Setup - textStore = new SyntheticTextStore('abcde', 3); - textStore.deadkeys().add(new Deadkey(-1, 1)); // invalid - textStore.deadkeys().add(new Deadkey(1, 2)); // after 'b' - textStore.deadkeys().add(new Deadkey(4, 3)); // invalid (after caret) - - // Execute - coreProcessor.unitTestEndPoints.applyContextFromTextStore(context, textStore); - - // Verify - const result = KM_Core.instance.context_get(context); - assert.equal(result.status, KM_CORE_STATUS.OK); - const items = result.object; - - // Text index : 0 1 1 2 3 - // Text: : | - // context index: 0 1 2 3 4 - // ContextItems : a dk2 b c END - assert.equal(items.size(), 5, 'Should have 5 context items'); - assert.equal(items.get(0).type, KM_CORE_CT.CHAR, 'Item 0 should be CHAR'); - assert.equal(items.get(0).character, 'a'.charCodeAt(0), 'Item 0 should be "a"'); - assert.equal(items.get(1).type, KM_CORE_CT.MARKER, 'Item 1 should be MARKER'); - assert.equal(items.get(1).marker, 2, 'Item 1 should be marker 2'); - assert.equal(items.get(2).type, KM_CORE_CT.CHAR, 'Item 2 should be CHAR'); - assert.equal(items.get(2).character, 'b'.charCodeAt(0), 'Item 2 should be "b"'); - assert.equal(items.get(3).type, KM_CORE_CT.CHAR, 'Item 3 should be CHAR'); - assert.equal(items.get(3).character, 'c'.charCodeAt(0), 'Item 3 should be "c"'); - assert.equal(items.get(4).type, KM_CORE_CT.END, 'Item 4 should be END'); - result.delete(); - }); - }); - - describe('processKeystroke', function () { - let process_event_spy: any; - - beforeEach(async function () { - coreProcessor = new CoreKeyboardProcessor(device, 'us'); - await coreProcessor.init(coreurl, new VariableStoreCookieSerializer()); - state = createState('/common/test/resources/keyboards/test_8568_deadkeys.kmx'); - context = KM_Core.instance.state_context(state); - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - sandbox = null; - }) - - for (const eventType of ['keydown', 'keyup']) { - const isKeyDown = eventType === 'keydown'; - - it(`passes the correct value for a ${eventType} event`, function () { - // Setup - const keyEvent = new KeyEvent({ - Lcode: Codes.keyCodes.K_A, - Lmodifiers: Codes.modifierCodes.SHIFT, - Lstates: Codes.modifierCodes.NO_CAPS | Codes.modifierCodes.NO_NUM_LOCK | Codes.modifierCodes.NO_SCROLL_LOCK, - LisVirtualKey: true, - device: null, - kName: 'K_A' - }); - keyEvent.source = { type: eventType }; - - const coreKeyboard = loadKeyboard('/common/test/resources/keyboards/test_8568_deadkeys.kmx'); - const kmxKeyboard = new KMXKeyboard(coreKeyboard); - sandbox.replaceGetter(coreProcessor, 'activeKeyboard', () => { return kmxKeyboard; }); - // We return a non-ok value just so that we can return early from - // processKeyStroke() - process_event_spy = sandbox.spy(KM_Core.instance, 'process_event'); - - // Execute - coreProcessor.processKeystroke(keyEvent, new SyntheticTextStore()); - - // Verify - check fourth argument (is_key_down) - assert.equal(process_event_spy.args[0][3], isKeyDown); - }); - } - }); - - describe('doModifierPress', function () { - // const touchable = true; - const nonTouchable = false; - - beforeEach(async function () { - coreProcessor = new CoreKeyboardProcessor(device, 'us'); - await coreProcessor.init(coreurl, new VariableStoreCookieSerializer()); - state = createState('/common/test/resources/keyboards/test_8568_deadkeys.kmx'); - context = KM_Core.instance.state_context(state); - sandbox = sinon.createSandbox(); - const coreKeyboard = loadKeyboard('/common/test/resources/keyboards/test_8568_deadkeys.kmx'); - coreProcessor.activeKeyboard = new KMXKeyboard(coreKeyboard); - }); - - afterEach(() => { - sandbox.restore(); - sandbox = null; - }) - - for (const key of [ - { code: Codes.keyCodes.K_SHIFT, name: 'Shift' }, - { code: Codes.keyCodes.K_CONTROL, name: 'Control' }, - { code: Codes.keyCodes.K_ALT, name: 'Alt' }, - { code: Codes.keyCodes.K_CAPS, name: 'CapsLock' }, - { code: Codes.keyCodes.K_NUMLOCK, name: 'NumLock' }, - { code: Codes.keyCodes.K_SCROLL, name: 'ScrollLock' }, - ]) { - it(`recognizes ${key.name} as modifier`, function () { - // Setup - const keyEvent = new KeyEvent({ - Lcode: key.code, - Lmodifiers: 0, - Lstates: Codes.modifierCodes.NO_CAPS | Codes.modifierCodes.NO_NUM_LOCK | Codes.modifierCodes.NO_SCROLL_LOCK, - LisVirtualKey: true, - device: new DeviceSpec('chrome', 'desktop', 'windows', nonTouchable), - kName: key.name - }); - keyEvent.source = { type: 'keydown' }; - - // Execute - const result = coreProcessor.doModifierPress(keyEvent, new SyntheticTextStore(), true); - - // Verify - assert.isTrue(result); - }); - } - - for (const key of [ - { modifiers: 0, name: 'a' }, - { modifiers: Codes.modifierCodes.SHIFT, name: 'A' } - ]) { - it(`recognizes ${key.name} not as modifier`, function () { - // Setup - const keyEvent = new KeyEvent({ - Lcode: Codes.keyCodes.K_A, - Lmodifiers: key.modifiers, - Lstates: Codes.modifierCodes.NO_CAPS | Codes.modifierCodes.NO_NUM_LOCK | Codes.modifierCodes.NO_SCROLL_LOCK, - LisVirtualKey: true, - device: new DeviceSpec('chrome', 'desktop', 'windows', nonTouchable), - kName: 'K_A' - }); - keyEvent.source = { type: 'keydown' }; - - // Execute - const result = coreProcessor.doModifierPress(keyEvent, new SyntheticTextStore(), true); - - // Verify - assert.isFalse(result); - }); - } - - }); -}); diff --git a/web/src/test/auto/headless/engine/keyboard/kmxkeyboard.tests.ts b/web/src/test/auto/headless/engine/keyboard/kmxkeyboard.tests.ts deleted file mode 100644 index 9754e179775..00000000000 --- a/web/src/test/auto/headless/engine/keyboard/kmxkeyboard.tests.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Keyman is copyright (C) SIL Global. MIT License. - */ -import { assert } from 'chai'; -import { createRequire } from 'module'; -const require = createRequire(import.meta.url); - -import { KeyboardHarness, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; -import { NodeKeyboardLoader } from 'keyman/test/resources'; - -describe('KMXKeyboard tests', function () { - // TODO-embed-osk-in-kmx: Enable when hooking up with RTL Core API (#15482) - it.skip('supports RTL layouts', async () => { - const rtlPath = require.resolve('@keymanapp/common-test-resources/keyboards/test_rtl.kmx'); - // -- START: Standard Recorder-based unit test loading boilerplate -- - const harness = new KeyboardHarness({}, MinimalKeymanGlobal); - const keyboardLoader = new NodeKeyboardLoader(harness); - const keyboard = await keyboardLoader.loadKeyboardFromPath(rtlPath); - // -- END: Standard Recorder-based unit test loading boilerplate -- - - assert.isTrue(keyboard.isRTL); - }); -}); diff --git a/web/src/test/auto/headless/engine/loadKeyboardHelper.ts b/web/src/test/auto/headless/engine/loadKeyboardHelper.ts index 5d1dc8b8387..5ffaed719e7 100644 --- a/web/src/test/auto/headless/engine/loadKeyboardHelper.ts +++ b/web/src/test/auto/headless/engine/loadKeyboardHelper.ts @@ -3,14 +3,11 @@ */ import fs from 'node:fs'; -import { pathToFileURL } from 'node:url'; import { getKeymanRoot } from 'keyman/test/resources'; const KEYMAN_ROOT = getKeymanRoot(); -export const coreurl = pathToFileURL(`${KEYMAN_ROOT}/web/build/engine/obj/core-adapter/import/core`).toString(); - export function loadKeyboardBlob(filename: string) { const data = fs.readFileSync(`${KEYMAN_ROOT}${filename}`, null); return new Uint8Array(data); diff --git a/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js b/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js index 33752796491..6364832fc45 100644 --- a/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js +++ b/web/src/test/auto/headless/engine/main/headless/languageProcessor.tests.js @@ -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); }); }); diff --git a/web/src/test/manual/web/attachment-api/index.html b/web/src/test/manual/web/attachment-api/index.html index f904d25f08e..331e9d2ab25 100644 --- a/web/src/test/manual/web/attachment-api/index.html +++ b/web/src/test/manual/web/attachment-api/index.html @@ -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); }); @@ -56,7 +57,7 @@ - +

KeymanWeb Sample Page - Attachment API Testing

This page is designed to stress-test the new attachment/enablement API model and its functions.

You are currently testing under the attachment mode. Two other modes are available: diff --git a/web/src/test/manual/web/attachment-api/utilities.js b/web/src/test/manual/web/attachment-api/utilities.js index 5793c12af6f..f6c404932ea 100644 --- a/web/src/test/manual/web/attachment-api/utilities.js +++ b/web/src/test/manual/web/attachment-api/utilities.js @@ -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); diff --git a/web/src/test/manual/web/basic-iframe/index.html b/web/src/test/manual/web/basic-iframe/index.html index 8a935f2cc65..56441455ddf 100644 --- a/web/src/test/manual/web/basic-iframe/index.html +++ b/web/src/test/manual/web/basic-iframe/index.html @@ -36,8 +36,9 @@ @@ -46,7 +47,7 @@ - +

KeymanWeb: Test IFrame connectivity

diff --git a/web/src/test/manual/web/build-visual-keyboard/index.html b/web/src/test/manual/web/build-visual-keyboard/index.html index 2845f3db86c..b7f2a67e741 100644 --- a/web/src/test/manual/web/build-visual-keyboard/index.html +++ b/web/src/test/manual/web/build-visual-keyboard/index.html @@ -37,8 +37,6 @@ @@ -51,7 +50,7 @@ - +

KeymanWeb Sample Page - Chirality Testing

This page is designed to stress-test the new support for chiral modifiers and state keys.

Be sure to reference the developer console for additional feedback.

diff --git a/web/src/test/manual/web/chirality/utilities.js b/web/src/test/manual/web/chirality/utilities.js index 16c9caaf026..7aea2825dd5 100644 --- a/web/src/test/manual/web/chirality/utilities.js +++ b/web/src/test/manual/web/chirality/utilities.js @@ -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' }, diff --git a/web/src/test/manual/web/ckeditor/index.html b/web/src/test/manual/web/ckeditor/index.html index 950f8bcb9b0..16de19ba021 100644 --- a/web/src/test/manual/web/ckeditor/index.html +++ b/web/src/test/manual/web/ckeditor/index.html @@ -30,10 +30,9 @@ @@ -44,7 +43,7 @@ - +

KeymanWeb Sample Page - Test CKEditor Integration

Test CKEditor Integration

diff --git a/web/src/test/manual/web/ckeditor/inline.html b/web/src/test/manual/web/ckeditor/inline.html index 463ebf21388..06ec8ca0e5e 100644 --- a/web/src/test/manual/web/ckeditor/inline.html +++ b/web/src/test/manual/web/ckeditor/inline.html @@ -30,10 +30,9 @@ @@ -44,7 +43,7 @@ - +

KeymanWeb Sample Page - Test CKEditor Integration

Test CKEditor Integration

diff --git a/web/src/test/manual/web/ckeditor/utilities.js b/web/src/test/manual/web/ckeditor/utilities.js index 2ca0fd324c3..8c281e22239 100644 --- a/web/src/test/manual/web/ckeditor/utilities.js +++ b/web/src/test/manual/web/ckeditor/utilities.js @@ -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'); } \ No newline at end of file diff --git a/web/src/test/manual/web/commonHeader.js b/web/src/test/manual/web/commonHeader.js index 74cb22e1cdb..e80368b5567 100644 --- a/web/src/test/manual/web/commonHeader.js +++ b/web/src/test/manual/web/commonHeader.js @@ -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. - - 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'; - } + * Keyman is copyright (C) SIL Global. MIT License. + */ + +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'; } - 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)); - }); + return result; } - - function doAddKeyboardsForLanguage(data) { - return keyman.addKeyboardsForLanguage(data).catch(function(err) { - console.error('keyman.addKeyboardsForLanguage failed with '+errToString(err)+' for '+JSON.stringify(data)); - }); + if(err instanceof Error) { + return err.message; } - - 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; - } + 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)); + }); +} + +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; } - - // 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. } - // 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; - } + // 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) - function clickOnEnter(e,id) - { - e = e || window.event; - if(e.keyCode == 13) addKeyboard(id); +// 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); } +} diff --git a/web/src/test/manual/web/default-subkey/index.html b/web/src/test/manual/web/default-subkey/index.html index 728355c43b4..22f00ef3fef 100644 --- a/web/src/test/manual/web/default-subkey/index.html +++ b/web/src/test/manual/web/default-subkey/index.html @@ -36,11 +36,10 @@ - +

KeymanWeb Testing Page - Desktop UI module Testing

This page is designed to facilitate testing of various aspects of the Toolbar UI for KeymanWeb. Originally developed to help address issue #1275. diff --git a/web/src/test/manual/web/empty-row/index.html b/web/src/test/manual/web/empty-row/index.html index 02953493e91..ee6da66cca9 100644 --- a/web/src/test/manual/web/empty-row/index.html +++ b/web/src/test/manual/web/empty-row/index.html @@ -36,18 +36,16 @@ - - + + @@ -47,7 +49,7 @@ - +

KeymanWeb: 'Test case' for proper implementation of the removeKeyboards API function

diff --git a/web/src/test/manual/web/issue103/index.html b/web/src/test/manual/web/issue103/index.html index 35b006e3472..53c0447cbd1 100644 --- a/web/src/test/manual/web/issue103/index.html +++ b/web/src/test/manual/web/issue103/index.html @@ -36,10 +36,11 @@ @@ -48,7 +49,7 @@ - +

KeymanWeb Sample Page - Keyboard Registration Check

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

KeymanWeb Sample Page - Mixed Case of TTF font files

Previously, KMW was not applying .TTF fonts on mobile devices.

See issue #1332 diff --git a/web/src/test/manual/web/issue160/index.html b/web/src/test/manual/web/issue160/index.html index 33219e7fdf7..3808341b7d9 100644 --- a/web/src/test/manual/web/issue160/index.html +++ b/web/src/test/manual/web/issue160/index.html @@ -36,12 +36,12 @@ diff --git a/web/src/test/manual/web/issue266/index.html b/web/src/test/manual/web/issue266/index.html index 56e52016d76..adf03ffe706 100644 --- a/web/src/test/manual/web/issue266/index.html +++ b/web/src/test/manual/web/issue266/index.html @@ -36,12 +36,12 @@ diff --git a/web/src/test/manual/web/issue271/header.js b/web/src/test/manual/web/issue271/header.js index b0d52effe97..6fd6bec984d 100644 --- a/web/src/test/manual/web/issue271/header.js +++ b/web/src/test/manual/web/issue271/header.js @@ -1,7 +1,5 @@ function setActiveKeyboard() { - var kmw=window['keyman'] ? keyman : tavultesoft.keymanweb; - var sKbd = document.getElementById('kbd_id4').value; var sLng = document.getElementById('lang_id4').value; - var result = kmw.setActiveKeyboard("Keyboard_" + sKbd, sLng); + var result = keyman.setActiveKeyboard("Keyboard_" + sKbd, sLng); } \ No newline at end of file diff --git a/web/src/test/manual/web/issue271/index.html b/web/src/test/manual/web/issue271/index.html index fa969195a13..0bf96247798 100644 --- a/web/src/test/manual/web/issue271/index.html +++ b/web/src/test/manual/web/issue271/index.html @@ -36,9 +36,10 @@ @@ -49,7 +50,7 @@ - +

KeymanWeb: Test page for interactions between setActiveKeyboard and the toolbar UI

This version is used to test the interactions of SetActiveKeyboard with the Toolbar UI.

diff --git a/web/src/test/manual/web/issue29/index.html b/web/src/test/manual/web/issue29/index.html index 24a226326ca..3f007c9cb4b 100644 --- a/web/src/test/manual/web/issue29/index.html +++ b/web/src/test/manual/web/issue29/index.html @@ -36,8 +36,9 @@ @@ -47,7 +48,7 @@ - +

KeymanWeb: Test desktop MutationObserver functionality

diff --git a/web/src/test/manual/web/issue3701/index.html b/web/src/test/manual/web/issue3701/index.html index c27bba6c81f..6393f5fe7c8 100644 --- a/web/src/test/manual/web/issue3701/index.html +++ b/web/src/test/manual/web/issue3701/index.html @@ -36,11 +36,10 @@ diff --git a/web/src/test/manual/web/issue53/index.html b/web/src/test/manual/web/issue53/index.html index 4c730a3f392..d58dbcfdf48 100644 --- a/web/src/test/manual/web/issue53/index.html +++ b/web/src/test/manual/web/issue53/index.html @@ -36,9 +36,10 @@ @@ -48,7 +49,7 @@ - +

KeymanWeb: Tests layering transitions for keyboards with variable layer sizes.

This test should be run from mobile devices or with Chrome's mobile device emulation settings available within Developer Mode.

diff --git a/web/src/test/manual/web/issue53/issue53.js b/web/src/test/manual/web/issue53/issue53.js index 03d11758661..4cef9371cd5 100644 --- a/web/src/test/manual/web/issue53/issue53.js +++ b/web/src/test/manual/web/issue53/issue53.js @@ -1,9 +1,7 @@ function loadKeyboards() { - var kmw=keyman; - // Add a fully-specified, locally-sourced, keyboard. - kmw.addKeyboards({id:'layered_debug_keyboard',name:'Web_Layer_Debugging', + keyman.addKeyboards({id:'layered_debug_keyboard',name:'Web_Layer_Debugging', languages:{ id:'dbg',name:'Debug',region:'North America' }, @@ -14,28 +12,28 @@ function loadKeyboards() // Script to allow a user to add any keyboard to the keyboard menu function addKeyboard(n) { - var sKbd,kmw=keyman; + var sKbd; switch(n) { case 1: sKbd=document.getElementById('kbd_id1').value; - kmw.addKeyboards(sKbd); + keyman.addKeyboards(sKbd); break; case 2: sKbd=document.getElementById('kbd_id2').value.toLowerCase(); - kmw.addKeyboards('@'+sKbd); + keyman.addKeyboards('@'+sKbd); break; case 3: sKbd=document.getElementById('kbd_id3').value; - kmw.addKeyboardsForLanguage(sKbd); + keyman.addKeyboardsForLanguage(sKbd); break; } } function removeKeyboard(n) { - var sKbd=document.getElementById('kbd_id4').value, kmw=keyman; - kmw["removeKeyboards"](sKbd); + var sKbd=document.getElementById('kbd_id4').value; + keyman.removeKeyboards(sKbd); } // Add keyboard on Enter (as well as pressing button) diff --git a/web/src/test/manual/web/issue6005/index.html b/web/src/test/manual/web/issue6005/index.html index 7326f99c7db..9410bed5640 100644 --- a/web/src/test/manual/web/issue6005/index.html +++ b/web/src/test/manual/web/issue6005/index.html @@ -40,13 +40,11 @@ const alertType = (new URLSearchParams(window.location.search)).get("useAlerts") != "false"; var alertText = alertType ? 'enabled(default)' : 'disabled'; - var kmw=window.keyman; - - kmw.init({ + keyman.init({ 'attachType': 'auto' }).then(function() { // if mobile device, activate pred text. - if(kmw.util.isTouchDevice()) { + if(keyman.util.isTouchDevice()) { var pageRef = (window.location.protocol == 'file:') ? window.location.href.substr(0, window.location.href.lastIndexOf('/')+1) : window.location.href; @@ -60,24 +58,25 @@ languages: ['en'], path: (pageRef + "prediction-mtnt/nrc.en.mtnt.model.js") }; - kmw.addModel(modelStub1); + keyman.addModel(modelStub1); var modelStub2 = {'id': 'nrc.en.mtnt', languages: ['pny-latn'], // yep. Total cheat for the sake of testing. path: (pageRef + "prediction-mtnt/nrc.en.mtnt.model.js") }; - kmw.addModel(modelStub2); + keyman.addModel(modelStub2); } + + keyman.addKeyboards('sil_euro_latin@en', + 'galaxie_hebrew_positional@he', + 'sil_cameroon_qwerty@pny-latn'); + + keyman.addKeyboards({ + id:'unmatched_final_group_model_interactions_6005', + name:'"Unmatched Vowel Rule"', + languages:{id:'en',name:'English'}, + filename:('unmatched_final_group_model_interactions_6005.js') + }); }); - kmw.addKeyboards('sil_euro_latin@en', - 'galaxie_hebrew_positional@he', - 'sil_cameroon_qwerty@pny-latn'); - - kmw.addKeyboards({ - id:'unmatched_final_group_model_interactions_6005', - name:'"Unmatched Vowel Rule"', - languages:{id:'en',name:'English'}, - filename:('unmatched_final_group_model_interactions_6005.js') - }); diff --git a/web/src/test/manual/web/issue62/index.html b/web/src/test/manual/web/issue62/index.html index 23ac9889df3..c647b03e88a 100644 --- a/web/src/test/manual/web/issue62/index.html +++ b/web/src/test/manual/web/issue62/index.html @@ -36,9 +36,10 @@ @@ -49,7 +50,7 @@ - +

KeymanWeb: Light test for touch-based MutationObserver functionality

diff --git a/web/src/test/manual/web/issue63/index.html b/web/src/test/manual/web/issue63/index.html index 55cee63c979..acb2cdd40f1 100644 --- a/web/src/test/manual/web/issue63/index.html +++ b/web/src/test/manual/web/issue63/index.html @@ -36,9 +36,10 @@ @@ -49,7 +50,7 @@ - +

KeymanWeb: Test/stress-test touch-based MutationObserver functionality

diff --git a/web/src/test/manual/web/issue917-context-and-notany/index.html b/web/src/test/manual/web/issue917-context-and-notany/index.html index 251edb9142a..f7941b8a0d9 100644 --- a/web/src/test/manual/web/issue917-context-and-notany/index.html +++ b/web/src/test/manual/web/issue917-context-and-notany/index.html @@ -36,11 +36,10 @@ diff --git a/web/src/test/manual/web/issue920/index.html b/web/src/test/manual/web/issue920/index.html index b20f536906f..03265f30bf3 100644 --- a/web/src/test/manual/web/issue920/index.html +++ b/web/src/test/manual/web/issue920/index.html @@ -36,15 +36,10 @@ - +

KeymanWeb Sample Page - deadkeys and multiple groups Testing

See issue #920 for details on what this test is all about. Both ;e and .e should produce ə but ;e uses a deadkey and fails.

diff --git a/web/src/test/manual/web/issue9469/index.html b/web/src/test/manual/web/issue9469/index.html index 78c3a15f95e..5c7e33daa34 100644 --- a/web/src/test/manual/web/issue9469/index.html +++ b/web/src/test/manual/web/issue9469/index.html @@ -36,11 +36,10 @@ @@ -48,7 +49,7 @@ - +

KeymanWeb Sample Page - Error Testing page

Pick one of the 'debugging' keyboards to test whether the represented error is handled correctly.

diff --git a/web/src/test/manual/web/mnemonic/index.html b/web/src/test/manual/web/mnemonic/index.html index e0b0f0de79b..a77c8622199 100644 --- a/web/src/test/manual/web/mnemonic/index.html +++ b/web/src/test/manual/web/mnemonic/index.html @@ -33,24 +33,21 @@ - - diff --git a/web/src/test/manual/web/options-with-save/index.html b/web/src/test/manual/web/options-with-save/index.html index 9f607fd11f4..2e69318bbca 100644 --- a/web/src/test/manual/web/options-with-save/index.html +++ b/web/src/test/manual/web/options-with-save/index.html @@ -33,24 +33,21 @@ - - diff --git a/web/src/test/manual/web/osk-event-buttons/index.html b/web/src/test/manual/web/osk-event-buttons/index.html index 92dafcdb4a9..95ce7c77077 100644 --- a/web/src/test/manual/web/osk-event-buttons/index.html +++ b/web/src/test/manual/web/osk-event-buttons/index.html @@ -36,8 +36,7 @@ @@ -51,7 +52,7 @@ - +

KeymanWeb Sample Page - Platform Testing

This page is designed to test the Platform statement for consistency. Refer to PR #969.
diff --git a/web/src/test/manual/web/platform/utilities.js b/web/src/test/manual/web/platform/utilities.js index 5c351e03170..bb02bdd6a10 100644 --- a/web/src/test/manual/web/platform/utilities.js +++ b/web/src/test/manual/web/platform/utilities.js @@ -1,8 +1,6 @@ -function loadKeyboards() -{ - var kmw=keyman; - - kmw.addKeyboards({id:'platformtest',name:'Platform Testing', +function loadKeyboards() +{ + keyman.addKeyboards({id:'platformtest',name:'Platform Testing', languages:{ id:'en',name:'English',region:'North America' }, diff --git a/web/src/test/manual/web/pr10506/header.js b/web/src/test/manual/web/pr10506/header.js index 0107af35256..986cc004334 100644 --- a/web/src/test/manual/web/pr10506/header.js +++ b/web/src/test/manual/web/pr10506/header.js @@ -1,11 +1,10 @@ function loadKeyboards() { - var kmw=keyman; - kmw.addKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'}, + keyman.addKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'}, filename:('../us-1.0.js')}); // Add a fully-specified, locally-sourced, keyboard with custom font - kmw.addKeyboards({id:'dotty_keys',name:'Dotty Keys', + keyman.addKeyboards({id:'dotty_keys',name:'Dotty Keys', languages:{ id:'und',name:'Special',region:'World', font:{family:'DejaVu Dots',source:['./DejaVu-Dots.ttf']} diff --git a/web/src/test/manual/web/pr10506/index.html b/web/src/test/manual/web/pr10506/index.html index 06eae113623..84ff48f868a 100644 --- a/web/src/test/manual/web/pr10506/index.html +++ b/web/src/test/manual/web/pr10506/index.html @@ -36,9 +36,10 @@ @@ -49,7 +50,7 @@ - +

KeymanWeb Test Page - Key-cap scaling / font-load delay interactions

To run the test, simply change from the default (English) keyboard to the "Special" keyboard.

When things are working properly, the base keys on the "Special" keyboard should contain diff --git a/web/src/test/manual/web/prediction-mtnt/index.html b/web/src/test/manual/web/prediction-mtnt/index.html index 34f43f3c5b7..0e41b5b63a4 100644 --- a/web/src/test/manual/web/prediction-mtnt/index.html +++ b/web/src/test/manual/web/prediction-mtnt/index.html @@ -36,16 +36,15 @@ - +

@@ -72,8 +73,8 @@ e.value = e.value + event + ': w.iw='+window.innerWidth+' w.ih='+window.innerHeight+orientText+orient+'\n'; - if(event == 'kmw.ri' || event == 'kmw.rh' || event == 'kmw.re') { - e.value = e.value + ' osk.w=' + keyman.osk.computedWidth + ' osk.h=' + keyman.osk.computedHeight + ' kmw.vs=' + keyman.util.getViewportScale() + '\n'; + if(event == 'keyman.ri' || event == 'keyman.rh' || event == 'keyman.re') { + e.value = e.value + ' osk.w=' + keyman.osk.computedWidth + ' osk.h=' + keyman.osk.computedHeight + ' keyman.vs=' + keyman.util.getViewportScale() + '\n'; } e.scrollTop = e.scrollHeight; @@ -89,22 +90,22 @@ window.addEventListener('orientationchange', function() { doLog('orientationchange-b', false) }, false); window.addEventListener('orientationchange', function() { doLog('orientationchange-c', false) }, true); - var kmw_ri = com.keyman.RotationManager.prototype.initNewRotation; + var keyman_ri = com.keyman.RotationManager.prototype.initNewRotation; com.keyman.RotationManager.prototype.initNewRotation = function() { - doLog('kmw.ri'); - kmw_ri.call(this); + doLog('keyman.ri'); + keyman_ri.call(this); } - var kmw_rh = com.keyman.RotationManager.prototype.iOSEventHandler; + var keyman_rh = com.keyman.RotationManager.prototype.iOSEventHandler; com.keyman.RotationManager.prototype.iOSEventHandler = function() { - doLog('kmw.rh'); - kmw_rh.call(this); + doLog('keyman.rh'); + keyman_rh.call(this); } - var kmw_irh = com.keyman.RotationManager.prototype.resolve; + var keyman_irh = com.keyman.RotationManager.prototype.resolve; com.keyman.RotationManager.prototype.resolve = function() { - doLog('kmw.re'); - kmw_irh.call(this); + doLog('keyman.re'); + keyman_irh.call(this); }

Return to main index. diff --git a/web/src/test/manual/web/sentry-integration/errorhdr.js b/web/src/test/manual/web/sentry-integration/errorhdr.js index f5e4ec283fb..27b88695046 100644 --- a/web/src/test/manual/web/sentry-integration/errorhdr.js +++ b/web/src/test/manual/web/sentry-integration/errorhdr.js @@ -1,27 +1,25 @@ -// JavaScript Document samplehdr.js: Keyboard management for KeymanWeb demonstration pages - -/* - This script is designed to test KeymanWeb error message handling. +/* + * Keyman is copyright (C) SIL Global. MIT License. + * + * This script is designed to test KeymanWeb error message handling. */ -function loadKeyboards() { - var kmw=keyman; - +async function loadKeyboards() { // We start by adding a keyboard correctly. It's best to include a 'control' in our experiment. - kmw.addKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'}, + await keyman.addKeyboards({id:'us',name:'English',languages:{id:'en',name:'English'}, filename:'../us-1.0.js'}); - + // Insert a keyboard that cannot be found. - kmw.addKeyboards({id:'lao_2008_basic',name:'wrong-filename', + await keyman.addKeyboards({id:'lao_2008_basic',name:'wrong-filename', languages:{ id:'lo',name:'debugging',region:'Asia', font:{family:'LaoWeb',source:['../font/saysettha_web.ttf','../font/saysettha_web.woff','../font/saysettha_web.eot']} }, filename:'./missing_file.js' // Intentional error - the file doesn't exist, so the @@ -51,7 +52,7 @@ - +

KeymanWeb Sample Page - Shift Testing

See issue #978 for details on what this test is all about.

diff --git a/web/src/test/manual/web/test-updateLayer/utilities.js b/web/src/test/manual/web/test-updateLayer/utilities.js index 41704fa0daf..378f44c87e2 100644 --- a/web/src/test/manual/web/test-updateLayer/utilities.js +++ b/web/src/test/manual/web/test-updateLayer/utilities.js @@ -1,8 +1,6 @@ -function loadKeyboards() -{ - var kmw=keyman; - - kmw.addKeyboards({id:'rac_balti',name:'RAC Balti', +function loadKeyboards() +{ + keyman.addKeyboards({id:'rac_balti',name:'RAC Balti', languages:{ id:'en',name:'English',region:'North America' }, diff --git a/web/src/test/manual/web/text_selection_tests_9073/index.html b/web/src/test/manual/web/text_selection_tests_9073/index.html index 20bd3cda686..7851186892f 100644 --- a/web/src/test/manual/web/text_selection_tests_9073/index.html +++ b/web/src/test/manual/web/text_selection_tests_9073/index.html @@ -20,23 +20,21 @@ + }); + diff --git a/web/src/test/manual/web/unminified - manual.html b/web/src/test/manual/web/unminified - manual.html index dbe7cdae489..5ff0c686e3d 100644 --- a/web/src/test/manual/web/unminified - manual.html +++ b/web/src/test/manual/web/unminified - manual.html @@ -36,8 +36,7 @@ diff --git a/web/src/tools/testing/bulk_rendering/renderer_core.ts b/web/src/tools/testing/bulk_rendering/renderer_core.ts index fa90283838a..597ef36950d 100644 --- a/web/src/tools/testing/bulk_rendering/renderer_core.ts +++ b/web/src/tools/testing/bulk_rendering/renderer_core.ts @@ -326,5 +326,5 @@ export class BatchRenderer { // BatchRenderer's internal state stuff is static on the class and is not exposed with // the line below. // @ts-ignore - window['kmw_renderer'] = new BatchRenderer(); + window['keyman_renderer'] = new BatchRenderer(); })(); \ No newline at end of file diff --git a/web/src/tools/testing/recorder/index.html b/web/src/tools/testing/recorder/index.html index f4a68ba7690..a9af8e20296 100644 --- a/web/src/tools/testing/recorder/index.html +++ b/web/src/tools/testing/recorder/index.html @@ -57,6 +57,8 @@ keyman.init({ attachType: 'auto', keyboards: '' + }).then(function () { + loadKeyboards(); }); document.getElementById("body").focus(); @@ -67,7 +69,7 @@ - +
diff --git a/windows/docs/help/advanced/locale_edit.md b/windows/docs/help/advanced/locale_edit.md index 1db621bebff..d37c12d6ced 100644 --- a/windows/docs/help/advanced/locale_edit.md +++ b/windows/docs/help/advanced/locale_edit.md @@ -43,12 +43,7 @@ messages (these mostly start with SK) and with the Menu strings. First, edit the translation's language information - SKUILanguageName, SKUILanguageNameWithEnglish, and SKLanguageCode. The SKLanguageCode -should be the same as the language code you chose earlier. You will also -see a String with id SKDefaultLanguageCode. For Keyman for Windows, this -should remain "en" for all translations. When developing a custom -product using the Keyman Engine for Windows, you may change your -product's default language, and this would then entail changing -SKDefaultLanguageCode. +should be the same as the language code you chose earlier. **Note:** Any entries missing from the translation will be retrieved from the default file. diff --git a/windows/docs/help/basic/config/options.md b/windows/docs/help/basic/config/options.md index 3b4bbd86b0b..f4084f64650 100644 --- a/windows/docs/help/basic/config/options.md +++ b/windows/docs/help/basic/config/options.md @@ -180,6 +180,28 @@ To open the Options tab of Keyman Configuration: log on for short and isolated tests as specifically requested by Keyman Technical Support. +- Proxy Settings + + In some environments, you may need to configure proxy settings in + order to download keyboards or updates for Keyman. Check with your + network administrator. More information is available in the + [Proxy Configuration](../../advanced/proxy_config) documentation. + +- Keyman System Settings + + The Keyman System Settings dialog provides access to various low-level + Keyman configuration settings. Generally, these settings do not need + to be changed, unless you instructed to do so by Keyman support staff. + +- Base Keyboard + + The Base Keyboard dialog allows you to select the physical Latin-script + layout of your keyboard, which will allow certain Keyman keyboards to remap + themselves dynamically to match your key caps. + + [Base Keyboard dialog](../../context/base-keyboard) + + ## Related Topics - [Keyman Configuration](../config/) diff --git a/windows/src/desktop/branding/messages.txt b/windows/src/desktop/branding/messages.txt index 9fa93cc6de3..ac1ce1a5c90 100644 --- a/windows/src/desktop/branding/messages.txt +++ b/windows/src/desktop/branding/messages.txt @@ -40,14 +40,6 @@ object Messages: TStockMessages Comment = 'The language code for the current translation' Parameters = <> end - item - Name = 'SKDefaultLanguageCode' - DefStr = 'en' - Comment = - 'The default language code for this product. This should be the ' + - 'language that the product is created in' - Parameters = <> - end item Name = 'SKButtonUILanguageDownload' DefStr = 'More Languages Online...' diff --git a/windows/src/desktop/kmshell/locale/am-ET/strings.xml b/windows/src/desktop/kmshell/locale/am-ET/strings.xml index 7f5f4f8ba78..2f238923439 100644 --- a/windows/src/desktop/kmshell/locale/am-ET/strings.xml +++ b/windows/src/desktop/kmshell/locale/am-ET/strings.xml @@ -647,10 +647,6 @@ am - - - - en diff --git a/windows/src/desktop/kmshell/locale/az-AZ/strings.xml b/windows/src/desktop/kmshell/locale/az-AZ/strings.xml index 404e51c51ac..7e3b7d5691a 100644 --- a/windows/src/desktop/kmshell/locale/az-AZ/strings.xml +++ b/windows/src/desktop/kmshell/locale/az-AZ/strings.xml @@ -626,10 +626,6 @@ az - - - - en diff --git a/windows/src/desktop/kmshell/locale/bwr-NG/strings.xml b/windows/src/desktop/kmshell/locale/bwr-NG/strings.xml index 28cdb065aa4..503b941fdea 100644 --- a/windows/src/desktop/kmshell/locale/bwr-NG/strings.xml +++ b/windows/src/desktop/kmshell/locale/bwr-NG/strings.xml @@ -627,10 +627,6 @@ Keyboard natǝ ga ana hara kǝthlǝr kari akwa Windows. Keyboardyeri ar Keyman a en - - - - en diff --git a/windows/src/desktop/kmshell/locale/ckl-NG/strings.xml b/windows/src/desktop/kmshell/locale/ckl-NG/strings.xml index dd994697df0..779b0e05316 100644 --- a/windows/src/desktop/kmshell/locale/ckl-NG/strings.xml +++ b/windows/src/desktop/kmshell/locale/ckl-NG/strings.xml @@ -647,10 +647,6 @@ keyboard that you use in Windows. Keyman keyboards will adapt automatically to en - - - - en diff --git a/windows/src/desktop/kmshell/locale/cs-CZ/strings.xml b/windows/src/desktop/kmshell/locale/cs-CZ/strings.xml index 83adda0577e..6e217e3b1f8 100644 --- a/windows/src/desktop/kmshell/locale/cs-CZ/strings.xml +++ b/windows/src/desktop/kmshell/locale/cs-CZ/strings.xml @@ -714,10 +714,6 @@ klávesnici, kterou používáte v systému Windows. Klávesnice se automaticky cs-cs - - - - cs-cs diff --git a/windows/src/desktop/kmshell/locale/de/strings.xml b/windows/src/desktop/kmshell/locale/de/strings.xml index 9827426632f..2e2d3bffd87 100644 --- a/windows/src/desktop/kmshell/locale/de/strings.xml +++ b/windows/src/desktop/kmshell/locale/de/strings.xml @@ -713,10 +713,6 @@ de - - - - en diff --git a/windows/src/desktop/kmshell/locale/el-polyton/strings.xml b/windows/src/desktop/kmshell/locale/el-polyton/strings.xml index f9699f61de2..72d425903fd 100644 --- a/windows/src/desktop/kmshell/locale/el-polyton/strings.xml +++ b/windows/src/desktop/kmshell/locale/el-polyton/strings.xml @@ -693,10 +693,6 @@ el - - - - en diff --git a/windows/src/desktop/kmshell/locale/es-419/strings.xml b/windows/src/desktop/kmshell/locale/es-419/strings.xml index 61cf66cb46d..f09a4fd222f 100644 --- a/windows/src/desktop/kmshell/locale/es-419/strings.xml +++ b/windows/src/desktop/kmshell/locale/es-419/strings.xml @@ -713,10 +713,6 @@ es-419 - - - - en diff --git a/windows/src/desktop/kmshell/locale/es-ES/strings.xml b/windows/src/desktop/kmshell/locale/es-ES/strings.xml index 639debd6fd6..144777c6056 100644 --- a/windows/src/desktop/kmshell/locale/es-ES/strings.xml +++ b/windows/src/desktop/kmshell/locale/es-ES/strings.xml @@ -713,10 +713,6 @@ en - - - - en diff --git a/windows/src/desktop/kmshell/locale/ff-NG/strings.xml b/windows/src/desktop/kmshell/locale/ff-NG/strings.xml index ccb18cebfef..9c15b5bf159 100644 --- a/windows/src/desktop/kmshell/locale/ff-NG/strings.xml +++ b/windows/src/desktop/kmshell/locale/ff-NG/strings.xml @@ -647,10 +647,6 @@ Kibod ko a` naftirta dow na`ura. Kibod Keyman koosan bee hoore mum yanayu mawnug en - - - - en diff --git a/windows/src/desktop/kmshell/locale/ff-ZA/strings.xml b/windows/src/desktop/kmshell/locale/ff-ZA/strings.xml index 119a12f37b1..47b53ff10e3 100644 --- a/windows/src/desktop/kmshell/locale/ff-ZA/strings.xml +++ b/windows/src/desktop/kmshell/locale/ff-ZA/strings.xml @@ -651,10 +651,6 @@ Jokku\? ful - - - - en diff --git a/windows/src/desktop/kmshell/locale/fr/strings.xml b/windows/src/desktop/kmshell/locale/fr/strings.xml index 2ed3f704bd2..df1248f0332 100644 --- a/windows/src/desktop/kmshell/locale/fr/strings.xml +++ b/windows/src/desktop/kmshell/locale/fr/strings.xml @@ -646,10 +646,6 @@ fr - - - - en diff --git a/windows/src/desktop/kmshell/locale/ha-HG/strings.xml b/windows/src/desktop/kmshell/locale/ha-HG/strings.xml index f65e132d358..91a118a878b 100644 --- a/windows/src/desktop/kmshell/locale/ha-HG/strings.xml +++ b/windows/src/desktop/kmshell/locale/ha-HG/strings.xml @@ -626,10 +626,6 @@ en - - - - en diff --git a/windows/src/desktop/kmshell/locale/hia-NG/strings.xml b/windows/src/desktop/kmshell/locale/hia-NG/strings.xml index bb618801581..bb558c5b18b 100644 --- a/windows/src/desktop/kmshell/locale/hia-NG/strings.xml +++ b/windows/src/desktop/kmshell/locale/hia-NG/strings.xml @@ -627,10 +627,6 @@ keyboard unda da mogka thlina nda Windows. Keyman keyboards na da spat sosaya t en - - - - en diff --git a/windows/src/desktop/kmshell/locale/id-ID/strings.xml b/windows/src/desktop/kmshell/locale/id-ID/strings.xml index b99c489289e..bfc69558a31 100644 --- a/windows/src/desktop/kmshell/locale/id-ID/strings.xml +++ b/windows/src/desktop/kmshell/locale/id-ID/strings.xml @@ -626,10 +626,6 @@ id - - - - en diff --git a/windows/src/desktop/kmshell/locale/it-IT/strings.xml b/windows/src/desktop/kmshell/locale/it-IT/strings.xml index de66ee82e9f..a89759327e6 100644 --- a/windows/src/desktop/kmshell/locale/it-IT/strings.xml +++ b/windows/src/desktop/kmshell/locale/it-IT/strings.xml @@ -714,10 +714,6 @@ che usi in Windows. Le tastiere Keyman si adatteranno automaticamente al layout it - - - - it diff --git a/windows/src/desktop/kmshell/locale/km-KH/strings.xml b/windows/src/desktop/kmshell/locale/km-KH/strings.xml index 16deccb41df..4c32ffeb1bb 100644 --- a/windows/src/desktop/kmshell/locale/km-KH/strings.xml +++ b/windows/src/desktop/kmshell/locale/km-KH/strings.xml @@ -713,10 +713,6 @@ km - - - - en diff --git a/windows/src/desktop/kmshell/locale/kn/strings.xml b/windows/src/desktop/kmshell/locale/kn/strings.xml index 0425ab7a58b..4c985d98e0d 100644 --- a/windows/src/desktop/kmshell/locale/kn/strings.xml +++ b/windows/src/desktop/kmshell/locale/kn/strings.xml @@ -646,10 +646,6 @@ kn - - - - en diff --git a/windows/src/desktop/kmshell/locale/kr-NG/strings.xml b/windows/src/desktop/kmshell/locale/kr-NG/strings.xml index ce4aee3eddc..509f3e85441 100644 --- a/windows/src/desktop/kmshell/locale/kr-NG/strings.xml +++ b/windows/src/desktop/kmshell/locale/kr-NG/strings.xml @@ -627,10 +627,6 @@ keyboard do suro Windows ye dǝlan faidatǝmin ma. Keyman keyboards dǝye adai en - - - - en diff --git a/windows/src/desktop/kmshell/locale/mfi-NG/strings.xml b/windows/src/desktop/kmshell/locale/mfi-NG/strings.xml index 6f9c21529a1..7f3788a37cf 100644 --- a/windows/src/desktop/kmshell/locale/mfi-NG/strings.xml +++ b/windows/src/desktop/kmshell/locale/mfi-NG/strings.xml @@ -627,10 +627,6 @@ keyboard na ká ga slera anikwa na am Windows. Keyman keyboards anuga maga sler en - - - - en diff --git a/windows/src/desktop/kmshell/locale/mnw-MM/strings.xml b/windows/src/desktop/kmshell/locale/mnw-MM/strings.xml index a4c4ef6e924..63d921d4a9c 100644 --- a/windows/src/desktop/kmshell/locale/mnw-MM/strings.xml +++ b/windows/src/desktop/kmshell/locale/mnw-MM/strings.xml @@ -693,10 +693,6 @@ mnw - - - - mnw diff --git a/windows/src/desktop/kmshell/locale/mrt-NG/strings.xml b/windows/src/desktop/kmshell/locale/mrt-NG/strings.xml index 72b2a4f362a..896f67ddcbe 100644 --- a/windows/src/desktop/kmshell/locale/mrt-NG/strings.xml +++ b/windows/src/desktop/kmshell/locale/mrt-NG/strings.xml @@ -626,10 +626,6 @@ en - - - - en diff --git a/windows/src/desktop/kmshell/locale/my/strings.xml b/windows/src/desktop/kmshell/locale/my/strings.xml index 7830d9473a4..0e8576c65b4 100644 --- a/windows/src/desktop/kmshell/locale/my/strings.xml +++ b/windows/src/desktop/kmshell/locale/my/strings.xml @@ -225,7 +225,6 @@ ကီးဘုတ်ို အောင်မြင်စွာ ပရင့် ထုတ်လို့မရပါ။ ဝက်ဘ်စာမျက်နှာမှာသိမ်းဆည်းပြီး ထုတ်ယူပါ <ကီးဘုတ်ထိခိုက်ခြင်း> Keyman debug information will be stored in a text file called keymanlog\system.log on your desktop. You should exit Keyman before reading or deleting this file. WARNING: This file can grow large very quickly. Enabling debugging will slow down your system and should only be done if advised by Tavultesoft support. PRIVACY WARNING: Please note that the debug logfile records all keystrokes that you type. You should only turn on the debug log for the duration of a debugging or diagnostic session, and delete the [desktop]\keymanlog\system.log file as soon as possible - en သုံးစွဲသူအတွက်ဘာသာစကားကို ရွေးချယ်ပါ အွန်လိုင်းဘာသာပြန်မှုအသစ်ကို ဖန်တီးပါ ဖိုင်ကိုဒေါင်းလုဒ်ဆွဲနေသည် diff --git a/windows/src/desktop/kmshell/locale/nl-NL/strings.xml b/windows/src/desktop/kmshell/locale/nl-NL/strings.xml index c6e48c924ed..4edb9a4e6ca 100644 --- a/windows/src/desktop/kmshell/locale/nl-NL/strings.xml +++ b/windows/src/desktop/kmshell/locale/nl-NL/strings.xml @@ -627,10 +627,6 @@ dat u in Windows gebruikt. Keyman toetsenborden worden automatisch aangepast aan nl - - - - en diff --git a/windows/src/desktop/kmshell/locale/pl-PL/strings.xml b/windows/src/desktop/kmshell/locale/pl-PL/strings.xml index d6a75be6e9d..1702d5b4d1d 100644 --- a/windows/src/desktop/kmshell/locale/pl-PL/strings.xml +++ b/windows/src/desktop/kmshell/locale/pl-PL/strings.xml @@ -627,10 +627,6 @@ której używasz w systemie Windows. Klawiatury Keyman automatycznie dostosują pl - - - - en diff --git a/windows/src/desktop/kmshell/locale/pt-BR/strings.xml b/windows/src/desktop/kmshell/locale/pt-BR/strings.xml index d3dd5437c8d..d798b1b43dd 100644 --- a/windows/src/desktop/kmshell/locale/pt-BR/strings.xml +++ b/windows/src/desktop/kmshell/locale/pt-BR/strings.xml @@ -647,10 +647,6 @@ keyboard that you use in Windows. Keyman keyboards will adapt automatically to en - - - - en diff --git a/windows/src/desktop/kmshell/locale/pt-PT/strings.xml b/windows/src/desktop/kmshell/locale/pt-PT/strings.xml index 906b23c8589..6a1d0df5a48 100644 --- a/windows/src/desktop/kmshell/locale/pt-PT/strings.xml +++ b/windows/src/desktop/kmshell/locale/pt-PT/strings.xml @@ -542,7 +542,7 @@ - Selecione o teclado latino básico + Selecione o teclado latino básico que utiliza no Windows. O teclado Keyman irá se adaptar automaticamente ao seu layout preferido. @@ -714,10 +714,6 @@ por-PT - - - - por-PT diff --git a/windows/src/desktop/kmshell/locale/qqq/locale.xml b/windows/src/desktop/kmshell/locale/qqq/locale.xml deleted file mode 100644 index 0dfef8f9c79..00000000000 --- a/windows/src/desktop/kmshell/locale/qqq/locale.xml +++ /dev/null @@ -1,577 +0,0 @@ - - -]> - - - - -[!!Ķēɏmãŋ!!] - -Segoe UI -9 - - - - - - - -&Name; - - - - - -[!!Ÿĕŝ!!] -[!!Ňō!!] - -[!!&Ƴēš!!] -[!!&Ńŏ!!] - -[!!ƠĶ!!] -[!!Čãŋčȅļ!!] -[!!Ćļŏśȅ!!] -[!!Ãƥƥłƴ!!] -[!!Ʀȇmıŋď mĕ Ľàťēř!!] - -[!!ŐƘ!!] -[!!Ċāņċěł!!] - -[!!Ċłīċķ ţħĭš ĭċơŋ ťō şȇŀȅćţ ȁ ƙěɏƅŏáŗď!!] -&Name;[!! ıś ŝťıĺĺ ŕūʼnňīńġ. Ċŀĩćķ ţħıŝ įĉőň ťơ űśē ŷōūř ļāņğųãğě ƙȇƴƃŏǻřđ ąť āʼnɏ ŧĩmė!!] - - - - - -&Name;[!! Ćōʼnƒĩģųřȃţįőň!!] - - - 40 - 118 - 40 - - -[!!Ġĕţŧīńģ Ŝţářťěď!!] -[!!Ħēļƿ!!] - - - -[!!Ƙėƴɓŏäŗď Łǻŷơŭţš!!] [!!Ķ!!] -[!!Ōƿťĩőƞş!!] [!!Ŏ!!] -[!!Ĥōţƙȇȳş!!] [!!Ħ!!] -[!!Śűƥƥơřť!!] [!!Š!!] -[!!Ķēĕƥ īň Ťōŭċĥ!!] [!!Ŧ!!] - - - -[!!Ƒĭļėʼnȁmĕ:!!] - -[!!Ƥāčĸäġȅ vĕřšįơņ:!!] -[!!Ƙĕƴƅŏȃŕď vȇŕšįơņ:!!] -[!!Äųţĥōŗ:!!] -[!!Ŵēƀşīťę:!!] - -[!!Ƥáĉķąğȅ:!!] -[!!Ƒőƞţŝ:!!] -[!!Ƙěŷƀōäŗď Ĺāɏőūŧś:!!] - -[!!Ęńĉőďıʼnğś:!!] - -[!!Łáŷōũŧ Ŧŷƥȅ:!!] - [!!Ƒĭxěđ (Ƥőşıŧįơƞąŀ)!!] - [!!Mäƥƥȅď ŧơ Ŵıƞďőŵŝ ŀąƴőůŧ (Mƞēmŏʼnįč)!!] - -[!!Ɖȇŝĉřīƥťįơń:!!] - -[!!Įŋŝţăĺłȅđ ƒōŕ:!!] - [!!Āłĺ ŭšȇřş!!] - [!!Čūŕŗȅņţ űşęŗ!!] - -[!!Ĺǻƞĝůǻĝęŝ:!!] - [!!✕!!] - [!!Āđđ ļàʼnğũåĝē!!] - -[!!Őƞ Ŝĉŗȇȅň Ķȅƴƀŏȁřď:!!] - [!!Ĉŭŝťŏm!!] - [!!Įŋŝťăļļėđ!!] - [!!Ŋōŧ ĩņšťāłŀėđ!!] - -[!!Ďơĉũměŋťáťĩŏʼn:!!] - [!!İƞśţąļļĕď!!] - [!!Ɲőţ įńşťǻĺłȅđ!!] - -[!!Mȅşśäģę:!!] -[!!Čőƥȳŕīğĥť:!!] - - - -[!!Ƥąċƙäĝė ŏƿţīōʼnş!!] -[!!Őƥţĭơʼnş!!] -[!!Īňšŧąľľ ƙĕŷɓŏȃŕđ...!!] -[!!Ďŏŵƞļőȁđ ĸěƴɓōäŗď...!!] - - [!!Ƙēŷƀōăŕđ ōƿťĭőńš!!] -[!!Ūƞīŋšťāļŀ!!] -[!!Ŭňĭƞŝťąľĺ ƥåĉĸåĝĕ!!] -[!!Šħŏŵ ıʼnťŕơđūċŧŏŗŷ ĥȅłƥ!!] -[!!Čħąņģĕ ĥŏŧĸēƴ!!] -[!!Śęť ĥőťĸěɏ!!] -[!!Űńıňşťāľŀ Ơʼn Şčŗēȇń ƘȇƴƄơǻŕď!!] -[!!Īƞśŧăłŀ Ŏń Śċřęęŋ Ƙĕŷƀơáŗď...!!] - -[!!Ƴŏũ ďŏ ņōţ ħâvě àņƴ ķȅƴƀőāŗđś ĩņşŧăļłęď. Ćŀįĉķ ťĥē Ɗőŵńŀőȁď Ƙȅɏƀŏāřđ ƃŭţŧŏń ŧő ĭńŝţąĺĺ á ĸěƴƄŏąŗď ŀȃŷơŭŧ ƒŗōm ţħě Ƙěɏmăƞ ŵĕƀŝĩţȇ.!!] -[!!Ŷōų ĉáņ ŏƞľŷ ũʼnīňşŧȁĺŀ ŧħĕ ĸėɏƃōâŕđ '%0:s' ıƒ ŷőū ăŗę åŋ Âđmĩƞıśţřąţŏŗ!!] - - - -[!!Ğȅƞėřāĺ!!] -[!!Šťäřŧůƿ!!] -[!!Ŏń Śčŗȇĕň Ƙęŷƀơářď!!] -[!!Āďvăƞčĕď!!] - -[!!Ķĕɏɓơáŗď ħőţķȅŷş ŧōğģľė ƙēȳɓŏářđ áćŧīvǻŧĩōņ!!] -[!!Şīmũŀàťȇ ĄŀťĞř ŵīţĥ Ćţřľ+Āĺŧ!!] -[!!Ťŕėāţ ħȁŗđŵāřĕ ďěãđĸęŷş ǻş ƥľȃįŋ ĸēɏş!!] -[!!Ŝĥőŵ ĥĩʼnţ měššàĝȇś!!] -[!!Āűŧőmȁŧĩĉáĺłŷ ćħȅčķ ƒőŗ ūƿđȃťėŝ àńď đōŵʼnłőǻđ!!] - -[!!Šţářţ ŵĥēń Ŵīŋďơŵš ŝŧâřťš!!] -[!!Ŝĥơŵ ŝƥļąšħ šćŕęȅņ!!] -[!!Šħơŵ ŵěļĉōmė şĉŕĕěņ!!] -[!!Ţēśŧ ƒőŗ ćōńƒłĭćţıņġ ȃƥƿŀīčäťīōňş ŵĥēƞ !!]&Name;[!! şťǻřţš!!] - -[!!Ŕěŀęàšĕ Şĥıƒţ/Ĉţřŀ/Ȁľŧ ōň Őņ Ŝčŗėěń Ķēŷƃơâŕđ ȃƒţēŕ ćŀĭċĸīňģ å ĸȅȳ!!] -[!!Äľŵȁƴŝ ŝĥŏŵ Őƞ Šċŕėěń ĶĕƴƄōãřđ Ŵīƞďơŵ ŵĥėń Ķěɏmàƞ ķȇƴƃőáŗď ĩš śēļĕĉţěď!!] -[!!Šŵĭŧčĥ ŧơ āƥƿřőƥŕįáŧė Őŋ Šćŗěėń ƘȅƴƄőãŗđ/Ħȅĺƿ áųťơmăŧĭĉåłŀŷ ŵĥȅņ ȃ ķęȳƀōāŕđ ĭŝ śĕļȇćţȇđ!!] - -[!!ĎēƄŭģġįƞĝ!!] - -[!!Ůšēř ĩŋţĕŗƒāĉē łáņģůȃġė...!!] - -[!!Ɓâśȇ Ƙȇƴɓŏäŕđ...!!] - - - - - - - - - -[!!(ņōŋȅ)!!] -[!!Šŵĩťċĥ !!]&Name;[!! Ơƒƒ!!] -[!!Ōƥȇń Ķęƴɓőäŗď Měƞų!!] -[!!Śħōŵ Ŏƞ Ŝĉřęęƞ ĶėƴƂőäŗď Ƥâņȇ!!] -[!!Őƥȅņ Čŏƞƒıġŭřãŧĭőń!!] - -[!!Šĥōŵ Ƙȅɏƀŏâŗď Ũşāġė Ƥȁņě!!] -[!!Šħŏŵ Ƒơņŧ Ĥēľƿėř Ƥăňě!!] -[!!Šĥőŵ Ćħąŗâćţěŗ Mȁƥ Ƥȃņě!!] -[!!Ơƥĕʼn Ţěxţ Ęđĩţŏŕ!!] - -[!!Ŏƿĕʼn Łåƞġűãĝě Śŵīţċĥĕŕ!!] - -[!!Ğēņĕŕáŀ Ĥơťĸęŷŝ!!] -[!!Ƙȅŷƅŏȁřđ Łãƴơųţś!!] - - - -[!!Ĩňšťăłĺęđ Ŀāʼnĝŭāģė!!] -[!!Ŵıńďơŵš Ĺāɏőūŧ!!] -[!!Ƙěŷmąŋ ĶȅŷƄőāřď!!] - -[!!(ůšę Ŵīņđơŵş ĺáŷőůŧ)!!] - - - -[!!Ůƥģŗǻđȅ ňŏŵ!!!] -[!!Ŝēŋď Şūƥƿōŗŧ Ʀęǫűęšť!!] -[!!Ĉŏƞŧǻĉŧ Ķěɏmäń Şűƥƿŏřŧ!!] -[!!Ĭƒ ȳőŭ ĥȃvĕ ąŋɏ įśşųȅŝ űśĭňģ Ķėɏmāň, Ĵũšť ãšƙ ã ɋűȅšťıőƞ ŏņ ťħĕ Ķȇȳmȁń Ĉōmmūŋĭťŷ Ƒŏŗūm.!!] -[!!Ŏƿėŋ Ķȇȳmǻň Čőmmŭŋĭťɏ Ƒŏŕūm!!] -[!!Ĉōƿŷŕĭĝĥţ © ŜİĹ Īņţęřŋàťįơņåĺ. Ȃŀĺ Ŕĭğĥŧś Ŗěşĕŗvěď.!!] -[!!Vĕŕšĭōʼn!!] -[!!Ēƞģıňȅ Vęŗŝīőń!!] - -[!!Ũŝęƒųĺ Ľıŋķš!!] - -[!!Ơńŀĭŋě Ĥęĺƥ!!] -[!!Ɖĩȁģʼnőşťīċš!!] -[!!Čĥėĉķ ƒơŗ Ůƥđǻŧęŝ!!] -[!!Ƥŗŏxȳ Şęŧŧīʼnġŝ...!!] - -[!!Ďīāĝŋőşŧīĉş!!] -[!!Ĉħēĉĸ Ľȃŋĝūȃģȅ Śēţŧįŋġš!!] - -[!!Mīćřőŝơƒţ Ŵīňđőŵś ǻƥƿĕãŗś ťŏ Ƅĕ ćőŕŗēćţłŷ ċōŋƒįğűŗēđ ƒőř ɏőűř ľąŋğūȁģē(ś).!!] - - - - - - - 40 - - -[!!Đŏŵňľŏâď Ƙęŷƀőáŗđ Ƒŕŏm ƙėȳmâņ.čőm!!] -[!!Ɗōń'ŧ ĭʼnšŧȃļļ, Ĵūşť đōŵʼnłőǻđ!!] - - - - - - - - - -[!!Īňśťąľĺ Ķėȳƅőåřď/Ƥàčĸąĝė!!] - -[!!Ďȅťáıľś!!] -[!!Ŗěāďmē!!] - -[!!İňśťȁŀļ!!] -[!!Ĩŋšŧąĺŀ ƒŏŕ ãĺŀ űşęŗś!!] - - - - - - - -[!!Ƥřŏxɏ Śėŕvȅř Ĉőŋƒįğũŗåţįőň!!] - -[!!Šȇřvėŕ: !!] -[!!Ƥőŗŧ: !!] -[!!Űşēřńąmě: !!] -[!!Ƥàŝšŵōŗď: !!] - - - - - - - -[!!Šėţ ßǻśę Ķęȳɓơǻřđ!!] -[!!Ŝėłěċţ ťĥę Ƃãşȇ Ļǻťīʼn šċŗĭƥť -ƙėȳƄŏǻŗď ţħãţ ƴōű ůšę įŋ Ŵįŋďơŵŝ. Ķēŷmąň ĸēȳɓōȁřďś ŵĭŀļ ȁđǻƥť ǻųŧōmâŧĭĉäłļȳ ťō ȳőũŗ ƥŕĕƒėŗŕȅđ ŀàƴōũť.!!] - - - - - -[!!Ĉħåńğě Ħơťķėŷ!!] - -[!!&Ċłēǻŕ Ħŏťĸȅŷ!!] -[!!Šėľęćť ä šťȁňđáŕđ ħőŧƙȅƴ ơř ċĥơőşė 'Ćūşťōm' ăƞď ħŏļď Ćţŕŀ,Śĥįƒť åʼnđ/ōř Àļţ áƞđ ţŷƥė ŷőũŕ ďėšįŗěđ ĥőťĸēŷ ƒőŕ ĸęȳƄơȃŗď %0:s:!!] -[!!Ŝęļēċŧ à śŧáńďǻŗď ĥőţĸēɏ ōŕ ĉħőőšē 'Ćŭşţŏm' ǻňđ ħŏĺđ Ĉťŕŀ,Śħıƒŧ ȁʼnď/ŏŕ Àłţ åŋđ ťȳƿē ȳŏũŗ đĕŝĩŕęđ ĥōŧķęŷ ƒŏŗ ŀȃʼnğůăĝē %0:s:!!] -[!!Şēłēĉŧ ã šţàňđáŗď ħőţĸȇȳ ŏŗ ćħőőśȅ 'Ćűšťőm' ăńđ ĥōĺđ Čţŕł,Šħįƒť ăņđ/ơř Ȃŀŧ āʼnď ťȳƥȇ ŷơűŕ đęšĭŕěđ ħơţĸėɏ:!!] - -[!!Ţĥė ħŏţƙēȳ %0:s ŵįŀĺ čōńƒļĭċŧ ŵīŧħ ŋŏřmȁľ ĸęȳƅơǻřđ ŭşȇ. Ɏŏů śĥōųļđ ūşę áţ łėàŝţ Ĉţŕĺ ơŗ Áłť. Ɖő ɏőŭ ŵȁƞţ ţŏ čħäňğē ŧĥįş ńőŵ?!!] -[!!Ťĥē ħơťƙēɏ %0:s ċōŋƒŀĭćţş ŵĩŧĥ ťħę ĥőťĸěȳ şěłĕĉťěď ƒőŗ ƙȇɏƀōāŕď %1:s. Ĭƒ ŷōũ ĉőƞŧıʼnŭĕ, ťĥȇ ħŏŧƙęɏ ƒơŗ ƙȅƴƅōāŕď %1:s ŵīŀł ƃė ĉļēȁřěď. Čơʼnťįŋũę?!!] - - - - - -[!!Şȇłěčţ ƘęŷƂŏȁřďš ŧő Įňśţãļľ!!] -[!!Ÿőū ċȃƞņŏŧ įƞśťåłļ āŀŀ ŧħę ƙĕŷƂőǻŗďś įŋ ťħĩš ƥȁčķáĝē. Ƴŏũ ċãň īƞşťāĺľ à máxĩmūm őƒ %0:d àďđıŧıōʼnąļ ĸēɏƄōȃŕďš. Ƥļĕąšȇ šȅľĕċť ŧħē ĸȇƴƀơåŗđŝ ŷőū ŵįşħ ťő īʼnśŧăļĺ ƒŕŏm ťĥīś ƿàĉĸăģȅ.!!] - - - - - - - -[!!Ŭƥđăţĕď !!]&Name;[!! Ċơmƥơƞȅƞŧś Ãváīŀâɓļĕ!!] - -[!!Ůƥđäţęş ƒōŕ !!]&Name;[!! àŕė ŋơŵ ävàıłàƄľē!!] -[!!Ƥłęāşȅ šĕĺěčţ ŧħę űƥđáŧěš ȳőŭ ŵīŝħ ŧō ĭńšťąľŀ:!!] - -[!!Đō ȳőů ŵãʼnŧ ťŏ ďơŵńĺơăď ăńď ĭŋśţȁŀļ ťħīš ƿăŧċħ ƞōŵ?!!] -[!!Ŷŏů ċâƞ ďőŵŋłŏãđ ŧĥě ňȅŵ věŗşıŏƞ ƒŕŏm:!!] -[!!Đő ƴơų ŵǻňť ťŏ vīšįť ŧħę ŵȅƄŝįťĕ ʼnőŵ?!!] - -[!!Įʼnšŧąłļ Ɲōŵ!!] -[!!Čáńĉĕļ!!] - -[!!Ơļđ Vĕŗŝīőƞ!!] -[!!Ŭƿďãťěđ Čơmƥőŋȅňţ!!] -[!!Ŝĭźē!!] - -&Name;[!! %0:s!!] -[!!ĶȇɏƄơąŕď %0:s %1:s!!] - -[!!Ůńàƃŀĕ ŧő ćŏņŧāċŧ ƙēȳmāń.ċőm - ƿľěášē măķȇ śųŗȇ ŷōů ħåvė àƞ àćťįvę Ĭńťȅřʼnȇţ čőňŋęčťįơƞ àńď ţřŷ âĝàįń.!!] -[!!ŪŋȃƄŀĕ ţŏ ćŏņťȁćť ƙėƴmāʼn.ĉőm - ƿľěäśȇ mäķė šűŕě ȳōű ħåvȇ àƞ āĉťīvě Ĩƞŧēŕňȅŧ čŏņʼnĕćŧįŏʼn åʼnđ ţŗŷ āġàĭŋ. Ŧħȇ ĕŗřőř ŗėĉĕīvėđ ŵǻŝ: %0:s!!] - -&Name;[!! ųƿđåţȇş ăŕę âvăĩľáƂľė!!] -[!!Ċłıćķ ťħįŝ ĭĉơņ ţő đơŵʼnłōåđ åƞď ĭʼnŝťåĺľ űƿđàŧĕŝ!!] -[!!Vĭėŵ ȃʼnđ &ĩńŝţáĺľ !!]&Name;[!! ũƿďąţēş!!] -[!!Ė&xīť őŋľĩńē ŭƥđǻŧĕ ċħěĉƙ!!] - - - - - - - -&Name; -&Name; - -[!!Ŝŧăŕŧ Ƙęƴmâņ!!] -[!!Ĕxīţ!!] - -[!!Őţĥĕŗ ţäśķš!!] - [!!Čơʼnƒīĝůřȁťīőņ!!] - [!!Śĥőŵ ŧħıš šćŗęȇň ăť ŝţǻŗŧųƥ!!] - [!!Ĥĩďė ťħış ŝćŕēėň ąŧ śťǻŗŧŭƿ!!] - - - - - -[!!Đīŝƿľàƴ įʼn!!] -[!!Ƒĭņď őťĥēŗ ďīŝƿľãɏ łåƞğűåģęŝ ōƞłĭʼnė...!!] -[!!Ħȇłƿ ŧŗãňşļȁťĕ ţĥē ũśěŗ įńťēřƒàĉĕ...!!] -[!!Ěńġľĭŝħ!!] -[!!Ēŋġļıŝĥ!!] -[!!ȇƞ!!] -[!!ēŋ!!] - -&Name; - -[!!Šȅľęċţ Ūşěŕ Įƞţȇŕƒâċė Łâʼnĝũàğě!!] - -[!!Ĉĥőŏśė ťħȇ łåʼnġųăğę ţħäŧ ŷőū ŵâņţ ďĩşƿŀáƴȇď ĭŋ ţħĕ !!]&Name;[!! ĩņťěŕƒāčē.!!] -[!!&Mŏŕė łăňĝŭǻğęŝ őʼnľĭňē...!!] - -[!!Čŕėăŧē ā ŋĕŵ ŧŕăņŝłāŧīōń őƞľīʼnė!!] - - - - - - -[!!Ĺǻʼnģųâġē Ċőńƒığųŗȃťıŏƞ Ťāşƙś!!] - -[!!Ċōƞƒĩĝůŕę ƴơŭŗ ĉōmƿūŧȅŕ ťő ŵőŕĸ ŵĭŧĥ!!] - - -&Name;[!! ĥąś ďēŧēĉŧėď ťħąţ ŧħȅ ƒőľľőŵįŋģ čơńƒīģūřâţĭŏņ ťåśķś ƞęĕđ ţŏ Ƃę ĉŏmƿłēţěď įƞ ơŗďėŕ ƒőŗ!!] -[!!ŝĉŗĩƿť(š) ŧơ ŵŏŕĸ ċőŕřĕĉţĺƴ ơƞ ɏōűŕ ċơmƥųţȅŗ:!!] - -[!!Mĩĉŕőŝōƒţ Ŏƒƒįčě Ļǻŋĝűăĝě Ŝũƿƥơŕť!!] - -[!!Mōŗĕ Īƞƒōŗmàŧįőʼn...!!] - -[!!Ɖő ȳơů ŵăňţ !!]&Name;[!! ŧő čơmƿłȅŧę ŧĥēŝę ŧäşķş ņơŵ?!!] - - -[!!Ɖơń'ť åŝƙ mė ăƂơūţ ťħīś àģåĩƞ!!] - - - - - - - 40 - 40 - - -[!!Ŗěšěţ Ĥĭņťš!!] -[!!Áļļ ħĩńť mĕŝšăĝȇš ħävȇ Ƃȅēʼn ŕȇşĕţ ȃņď ŵĩĺļ Ƅė đışƥļȁȳēđ āĝáıŋ.!!] - -[!!Ȅxıŧ !!]&Name;[!!?!!] -[!!Ãŕę ƴŏū şŭŗē ɏōų ŵȁņť ŧō ēxīŧ !!]&Name;[!!? Ÿơũŗ Ƙȅŷmǻń ĸȅŷƄőǻŕďš ŵıĺļ ŝťıľľ ƀę ŀıŝŧȅđ įň ťħě Ŵĩŋđŏŵš ŀȃņĝŭāģēŝ, ƃŭť ŵĭĺĺ ņơţ ƀĕ ƒůƞčŧĩŏňȃĺ ũŋťĩł ȳőů řęšţäŕţ !!]&Name;[!!.!!] - -[!!Ŏņ Šċŗěȇʼn Ƙȅɏƀơâřď Ĉĺőŝęđ!!] -[!!Ÿơų ħāvę čļơśȇđ ťĥē Őň Ŝćřěēʼn Ķēɏƃơâřď. !!]&Name;[!! ış şťıłł ŗŭŋƞĭŋģ. Ƴŏų čãń ōƿēŋ ţĥė Ŏň Şćŕęēƞ Ƙěɏƀōȁŗď åţ åņȳ ťĭmė ƃƴ čĺĭĉĸĭņğ ōń ŧĥĕ !!]&Name;[!! Ĩĉōņ ȃňď şĕļȅćŧīŋĝ "Ōņ Śćŗĕȇʼn Ƙȇȳƃơäŕđ"!!] - -[!!Đōň'ţ ŝĥŏŵ ŧĥĩš ĥıņť âĝăĩƞ!!] - - - - - - -&Name;[!! Ĥęļƥ!!] -[!!Ġėŧ Şŧáŗţěđ!!] -[!!Ĥěłƿ Čơʼnťēƞŧš!!] - -[!!Ħėŀƥ ōņ "!!] -[!!" ƙęŷƃőáŕď!!] - -[!!Vīěŵ Őŋ Şčŗęȇņ Ƙȅƴƅŏȃřď!!] -[!!Vįĕŵ Ƙȇɏƅơãŗď Ŭśȁĝě!!] -[!!Vĩėŵ Ƒōňţ Ħēĺƥěŗ!!] -[!!Vīěŵ Ċħåřāčŧĕř Máƿ!!] -[!!Ŝŵĭŧčħ !!]&Name;[!! Ōƒƒ!!] -[!!Ơƥĕʼn !!]&Name;[!! Ċơƞƒĩġũřáţīőń!!] -[!!Ơƿĕň Ĥȅłƿ!!] -[!!Ćļŏŝȇ Ŏň Şĉŗȅēʼn Ķēƴƃōäŗď!!] - - - - - -[!!Şŵıţčħ !!]&Name;[!! &Ōƒƒ!!] -[!!Ōń Ŝćŗĕėň &ĶȇȳƂőářď!!] -[!!Ķȇƴƃơăřď &Ůšăĝė!!] -[!!&Ƒōńť Ħȇŀƥěŕ!!] -[!!Ĉĥãřāĉťęř &Mâƥ!!] -[!!&Ťēxť Ȇďĩťŏř...!!] -[!!&Ċơņƒığūŕåťĭơʼn...!!] - -[!!&Ħęłƥ...!!] -[!!Ĕ&xĩŧ!!] - -[!!Ňơ ƙěƴɓőȃřď ŀāƴőůťš āŕĕ ıŋşţāłłěď. -Ĉĥơơšȇ "Čőʼnƒįğűŗäťīŏƞ" ŧơ īňŝŧǻłľ á -ķȅɏƂőäŗđ ļąɏōůţ ƒőř ɏŏūŕ ĺáňġųáĝę.!!] - -[!!Ƒȁďȅ Ŵĥęń Ĭʼnàčţĭvē!!] -[!!Ŝĥŏŵ Ťōōĺƀåř!!] -[!!Śǻvĕ äš ŴěƄ Ƥąģę...!!] -[!!Ƥŕıňŧ...!!] - - - - - -[!!Ƙȅŷƅőǻřď Ħēłƥ!!] -[!!Őņ Ŝćŗėėń ĶȇɏƄōâŕď!!] -[!!Ģȅŧ Šťàŗťĕď!!] -&Name;[!! Ĥėŀƥ!!] -&Name;[!! Ċơŋƒĩġũřăţıơņ!!] -[!!Ťħĩŝ ƙȅŷƄőäŗđ ĺàŷŏũţ ĥáś ʼnō Ĝěťťīņģ Śťăŗţęď ơř Ũŝȁģē ďơċũměņťȁţĩŏƞ. Ƥľȅǻšě ĉħėċĸ ŧħē Šťàřţ Měńů őŗ ĉőŋţáĉť ŧħę ƙȇȳƂŏåŗď ďȇvēľőƥȅŗ ƒōŗ mőŗē ıňƒŏŗmȃŧĩőň ōƞ ĥơŵ ťō ųŝę ŧĥĭş ķěȳƅőářđ ŀâŷōųţ.!!] -[!!Čļīćƙ ťĥȅ ƅŭťŧŏňś ƃȅłơŵ ťơ vıęŵ åďďįŧĩōņāľ ďŏĉųmȅņŧāŧīơƞ ąʼnđ/őř Őń Şčřȇęʼn Ƙȇƴƃŏȁŗď.!!] -[!!Ňơ ƙęƴƃŏàŕđ ĺȁƴơųŧ ĭś ćũřŗęńţľȳ ŝȇľěćŧĕđ ĩņ !!]&Name;[!!. Ĉŀĩĉƙ őƞ àŋɏ őƒ ŧĥē ƙěŷƀőăřď ıćőņš ıƞ ŧħĕ ŧŏơŀƀąŗ åƄơvȇ ťơ ćħőŏşȅ ã ĸȅɏƀơáŕď ĺãŷōūţ.!!] -[!!Ŋō ķĕŷƄőàřđ ľãƴŏųŧŝ ąřė ċůŕŕěŋťĺŷ įƞšťàļľĕď ơŗ ŀơȃđȅđ. Ŭşē !!]&Name;[!! Čōʼnƒīĝűŗāťĩőň ŧơ ıƞšŧȃľļ à ķȅɏƂơâřď ĺãɏōũť.!!] - - - - - - - - - -&Name; -[!!Vĕŕŝĩŏņ %0:s!!] - -[!!Ĥėļƥ!!] -[!!&Ƥřıƞţ...!!] -[!!&Ɗŏŵʼnłōȁď...!!] - - - -[!!Ą ƿäĉĸâģĕ ŵįťħ ţĥę ňȃmȅ %0:s ĩš ǻłŕȅáďƴ īŋśŧåĺĺȅď. Ďơ ŷőũ ŵąņŧ ţō űņĩńŝţǻŀŀ ıŧ ǻňđ įńŝťåŀĺ ťħē ņēŵ ōʼnė?!!] -[!!Ă ĸėȳƀơáŗđ ŵįťħ ŧħė ƞàmę '%0:s' ĩş ȁļŕȇäđȳ ĭʼnšţǻĺłȇđ. ݃ ŷơű ĉơņţīŋűȇ, ĩŧ ŵįļļ ɓę ŭʼnīņŝŧāľŀęđ Ƅėƒơřȅ ťħę ʼnȅŵ ōņȇ īś ĭʼnşŧáłłęđ. Ċōƞťįƞųȇ?!!] -[!!Ŧħē ƙĕŷƄŏäŗď '%0:s' īŝ ƿǻŗť ōƒ ƥâĉĸàģě '%1:s'. Ɏōū mūśţ ũňĩňŝŧäĺĺ ŧĥę ėƞťįřė ƿąĉĸäğȇ. Čŏʼnŧĭņųĕ?!!] -[!!Ɏơũ ċâň ōʼnŀŷ ĭƞšŧâĺĺ ŏř ůŋīņŝťàļł ķȇɏƃőąřďş àś Ȃđmįŋıšťřâťŏŗ. Ůşě Ćōņŧřŏļ Ƥâŋęļ ţō ŝēļȇčŧ ŀáƞģůàĝęś ƒơŕ ųśȅ ãš ą ƞơŕmǻŀ ųšȅŗ.!!] -[!!Ĉħāņģĭńğ ťĥĕ śĕţŧĭŋğ ōƒ Šůŕŗőğȁŧĕś ŕęǫūĩřėŝ Ŵĭňďơŵś ţő ƀě ŗȇśŧȁŗŧěď. Đō ƴơŭ ŵàʼnŧ ŧő ŕȇşţąřŧ Ŵįņđőŵš ňŏŵ?!!] -[!!Ţĥė ƿàċƙȃĝě ơŕ ƙěŷƂơäřď '%0:s' ďơȇś ƞŏţ ĭńčłŭđė äƞƴ ĭƞŧřŏđůċţōŗȳ ħēľƥ.!!] -[!!Ţħĭş ōƥȇŕȃťīʼnĝ şȳšţȅm ıŝ ƞőť śűƥƥōŕŧēď.!!] -[!!Ÿōŭŗ ļįċęŋĉě ħǻş ɓėȇƞ śūċĉėşŝƒŭłľȳ àćţĩvåťȅđ.!!] -[!!ĶėƴmȁņĎēşƙŧōƿ.čĥm!!] -[!!Ţĥĕ ĥĕľƿ ƒįŀě $(ŞƘŎʼnľıŋěĤēļƥƑĭļę) čŏūļđ ƞōŧ ƅė ƒōűʼnď.!!] -[!!<Ďȃmàĝěď ķěɏƄőāřđ>!!] -[!!Ćōđȅƥăġė!!] -[!!Ūņıčŏďĕ!!] -[!!Ďŏŵʼnľŏąđįńġ Ƒĩłē!!] - - - - -[!!Ąřĕ ȳőŭ ŝŭŗȅ ɏŏů ŵåňţ ŧő ŗěmōvȅ ŧĥĕ ơƞ śćŕēęƞ ķēȳƀőȁŗď ĭņŝťäľļėď ƒŏŗ %0:s?!!] -[!!Ăŕȅ ƴőŭ şųŗē ƴơŭ ŵāʼnť ţő ųƞįňšţăļł ĸĕƴƄőȁŗď %0:s?!!] -[!!Åŕė ɏōų śűŕĕ ŷŏū ŵȁńť ŧő űňıňşŧāŀĺ ƿáĉƙåĝė %0:s? - -%1:s!!] -[!!Ţħė ƒơĺĺŏŵįƞĝ ƒōʼnŧś ŵīłĺ ãłšō ɓę ũʼnıƞśťâľŀȅď: %0:s.!!] -[!!Đŏ ƴŏũ ŵäńť ţơ àċţıvǻŧė $(ŞĶŞħőřŧĂƿƿľįĉāŧĭŏńŢıŧŀě) ŏńļīńė ńơŵ ŵĭţĥ ľıčėŋčě ŋŭmƄěŕ %0:s? ݃ ȳŏũ ĉŀĩčƙ Ņō, ȳōŭ ċȃň čħőōšĕ ƒřŏm ăĺŧęřƞāŧē ǻčŧĩváŧįơń mȇŧħơďŝ.!!] -[!!Ŧħė Ćĥàŕǻċťěŗ Mȁƿ ĥáş ȃ ďãťäƀáŝę ơƒ ćħǻŕāćţȇŗš ŧħáţ ňěĕďś ŧő ƅȅ ɓũĩŀť ƀȅƒőřȇ ĩŧ ĉâņ ƀȅ ųşȅđ. ʙůıĺď ĭť ńŏŵ?!!] - - - -[!!Ŧĥē Ůńĭĉŏďė Ċĥąŗáċţėř ďȁťăƅāśĕ ĉŏůļđ ńŏť ƀȅ đęłȅŧȅď ƒőŕ å řėƀųīĺđ. Ĕŗŕōŕ đėŧăĩĺŝ ȁŗȅ: %0:s!!] -[!!Ťħė Ūņīĉőđĕ Ćħáŗȁċťęŗ ďąťáɓȃśȇ ċŏŭĺď ŋơţ Ƅĕ ćřęãŧėđ ăŋđ ħàş Ƃĕĕň đĩŝàƂŀēđ. Ěŗŕōŗ đȅţāįŀŝ äřě: %0:s!!] -[!!Ťĥȇ Űňĭċŏďĕ ĉħäŗąčţȅř đȁţăƅåŝě đįđ ňōť łōăď šűććėŝšƒűŀłɏ (%0:s). Ʀȅƃūīĺď īŧ ƞőŵ?!!] - - -[!!Ċàńňŏŧ åĉťĩvâŧė ƀŕŏŵśĕř ŏř ěmáīľ ƥŕơģŗǻm ƒōŕ ţĥĭŝ ŪƦĽ.!!] -[!!Ŧħĕ ķȅȳƀŏäřđ ċơŭłď ƞŏť ƃě ƥŕĭʼnţȅđ šũććĕŝŝƒŭļļɏ. Ƥľĕáşē ŝâvė ťő ā ŵěƀ ƿåğȅ áńď ƿŕįňť mâņũąĺľɏ.!!] -&Name;[!! ƒȁīĺȅđ ťō śťȃŗŧ. Ƥĺȅǻšĕ ćĥȇčƙ ƴŏųŕ śěćũŕĭţƴ şĕťťıƞĝś ŧơ màķȇ şųŕę ŧħâŧ ţĥĕ ƿŕơġŕām ƙęɏmâƞ.ȅxė įś äľľŏŵęď ťŏ şţãřţ ƃęƒơŗĕ čōʼnŧĩńůıŋĝ. Ŧĥė ȇřřōŗ řěŧŭŗŋēđ ŵãŝ: - -"%0:s" - -Đơ ƴŏū ŵáņť ţő ţŗƴ ǻʼnď śţàŕŧ !!]&Name;[!! ǻĝãĭņ ňōŵ?!!] - - - -&Name;[!! đēƂůĝ ıńƒŏŕmàŧıőņ ŵıĺľ Ƃė ŝŧŏŗȅď ĭń ă ţȇxť ƒīłȇ ćãĺŀȇď ķėŷmåńŀőģ\śŷşťēm.łōģ ơņ ƴőũŕ ďėśƙťŏƥ. Ȳŏū šħơůľđ ėxıť !!]&Name;[!! ƅĕƒŏřȇ řȇåđīʼnĝ ơŕ ďĕŀėŧīŋĝ ťħıŝ ƒĩłě. - -ŴÃŖŊĪŅĢ: Ťħĭš ƒıŀȇ čãň ĝřŏŵ łářģȅ vėřȳ ǫųĩćƙŀɏ. ĒƞâƂļıņģ ďėƄŭġģīʼnğ ŵĭłļ ŝŀơŵ ďōŵň ŷōŭŕ šɏšťěm åňď śħőųļď ŏņĺŷ ɓę ďōƞė ıƒ äďvĭśȅď ɓŷ ŧēćĥʼnįċȃĺ šųƿƿơŗť. - -ƤŘĬVÅĊŸ ŴÁŔŃĨŅĞ: Ƥŀēâšȇ ňơŧē ţĥäť ţĥě đęƄũğ ŀőğƒĭĺȅ ŕĕčőŕďş āĺĺ ĸĕŷşťřőķěś ťĥäŧ ƴơů ţȳƿȇ. Ƴŏų śħơūļđ ơņĺŷ ťűŕń ơʼn ţħė ďėƃũġ ľōģ ƒōř ţĥē ďųřàţĭőń őƒ ȃ ďēƀŭğģīƞģ ŏŗ ďıăĝƞōşŧįĉ šėşŝīơƞ, âŋď đĕĺȇţē ŧħȅ [đȅŝķŧơƥ]\ƙȇȳmåńłŏģ\ŝȳşťȅm.ŀōģ ƒıĺě ãŝ šōōņ áš ƿơśśıɓļĕ.!!] - - - -[!!Äďđ %0:s ēđįŧīƞģ şųƿƥơŕť ťŏ %1:s!!] -[!!Mĭčŗơšōƒť Ōƒƒįčě 2000!!] -[!!Mįćřōśơƒť Ơƒƒįċĕ XƤ!!] -[!!Mıċŗơşőƒŧ Ōƒƒıćĕ 2003!!] -[!!Mįĉŗōšơƒŧ Ōƒƒįćē 2007!!] -[!!Mįćŗőşōƒŧ Ōƒƒīčě 2010!!] - - -[!!Ļįŋƙ ŀâʼnĝűąğę %0:s ŧō Ķȅŷmāň ƙęŷƄŏäřđ %1:s!!] -[!!Ĩňŝŧąŀĺ ļăʼnģųåğē %0:s ąʼnď łĩňƙ ţŏ Ƙėƴmàʼn ķĕɏƂōäřđ %1:s!!] - - - -[!!Ƥłęäśȇ ŵàįŧ ŵħĭĺę !!]&Name;[!! ƒıńďŝ ŧħě ƒōňţŝ ŧĥåţ ŵĩĺľ ŵŏřķ ŵĭţħ!!] -[!!ķȅɏɓơăŗď.!!] - - -[!!įŝ ňőţ á Ůʼnįčơďĕ ĸěȳƄőãŗđ. Ƒōņţš ćȁʼnņơť ƅȅ âŭťŏmãťĭćáļľɏ ĺōĉäţȇđ. Ƥłĕǻšė ćħēćķ ťħě ĸěȳƀőāŗď ďōćůmȇňŧàťĭŏŋ ƒơŗ ƒŏņť ĭƞƒơŕmâţīőń.!!] - -[!!Ťĥȇ ƒŏĺĺőŵıňģ ƒơŋŧś ŵơřĸ ŵıŧħ!!] -[!!ķĕɏƀŏāřđ:!!] - -[!!Ťħĕ ƒōŀŀōŵĭʼnğ ƒơņťš màŷ ãłşő ŵŏřƙ:!!] - -[!!Ŧħę ƒōľļơŵįʼnģ ƒơňŧś mǻƴ ŵŏŕķ ŵįŧĥ!!] -[!!ķęŷƅŏǻŕď:!!] - -[!!Ńő ƒŏŋŧş ŵĕřě ƒōũʼnđ ơņ ȳơŭŗ ŝƴśťȅm ťĥȃţ ŵōŕķ ŵīţĥ !!] -[!!. Ƥľȇāšē !!][!!čħēċƙ ťħė ďŏĉūmęņţǻţīŏņ!!][!! ţơ ƒıńď ƒơňţś ƒōŕ ťĥış ķȇɏƀōãřď.!!] - -[!!Ńŏ ĸęƴƅơāŗđ ŀåƴōůŧś äŕȅ ćųŗŕęņťĺȳ īņśťâłłēđ ōŕ łőäđĕď. Ŭŝě !!]&Name;[!! Ĉőŋƒįğűřąŧīŏņ ŧơ ıʼnŝťȃĺľ ä ƙĕɏƃōąŗđ łâƴơŭŧ.!!] -[!!Ƥľęǻśĕ ćħōŏšě å !!]&Name;[!! ķēƴƄŏȃřđ ţő ƒīƞď ţħė ƒőƞŧş ŧĥáŧ ŵįĺĺ ŵőŗĸ ŵıŧħ īŧ.!!] - - - -[!!Ħīʼnť:!!] -[!!Řěšıżȇ ŧħȅ Őň Şċŕĕĕň Ƙěŷƅơǻřđ ƃŷ ćŀĭćĸīňģ äńď đŕäĝġıńĝ ŏʼn ŧĥę ŵįʼnďơŵ ƅơŗďėŕ!!] -[!!Mővȅ ţĥȅ Ơň Şčřėěŋ ƘėŷƄơäřď Ƃɏ čŀīċĸĩʼnĝ ąʼnđ ďřâģğıŋĝ ōń ťĥȅ "!!]&Name;[!!" ŧīťłē!!] -[!!Ĩņśȅřť ąŋƴ Ůńĩćōđē ćħăŗǻċŧěř ĩņťō ƴơũŕ đōčŭměňŧ ŵıŧĥ ŧħȅ!!] -[!!Čĥǻŗąĉŧėř Mäƥ Ťŏŏŀ!!] -[!!Ƒĩńď őűť ŵĥıćĥ ƒơņţş ŵĭŀł ŵōřƙ ŵıţħ ŷōŭŕ ŀåņğūȃĝė ŵĭťĥ ťĥȇ!!] -[!!Ƒơʼnţ Ħęŀƥȅŕ Ťōōļ!!] -[!!Ğēţ mơŕē ħěŀƥ ōŋ ţħė ĸȅȳƄơäřđ Ƃȳ ċļįčķįŋğ ţħě!!] -[!!Ƙěɏƀơåřď Ůśâģȇ Ƅűŧţơņ!!] - - - - - -[!!Ţęxŧ Ęďĭŧŏŗ - !!]&Name; - - diff --git a/windows/src/desktop/kmshell/locale/qqq/strings.xml b/windows/src/desktop/kmshell/locale/qqq/strings.xml index 86fbece5842..23e31b333b7 100644 --- a/windows/src/desktop/kmshell/locale/qqq/strings.xml +++ b/windows/src/desktop/kmshell/locale/qqq/strings.xml @@ -234,7 +234,6 @@ Create a user interface translation for Keyman Desktop at http://www.tavultesoft [!!Ěńġľĭŝħ!!] [!!Ēŋġļıŝĥ!!] [!!ȇƞ!!] - [!!ēŋ!!] Keyman [!!Šȅľęċţ Ūşěŕ Įƞţȇŕƒâċė Łâʼnĝũàğě!!] [!!Ĉĥőŏśė ťħȇ łåʼnġųăğę ţħäŧ ŷőū ŵâņţ ďĩşƿŀáƴȇď ĭŋ ţħĕ !!]Keyman[!! ĩņťěŕƒāčē.!!] diff --git a/windows/src/desktop/kmshell/locale/ru-RU/strings.xml b/windows/src/desktop/kmshell/locale/ru-RU/strings.xml index 4fdf78818a3..b0729d8b54c 100644 --- a/windows/src/desktop/kmshell/locale/ru-RU/strings.xml +++ b/windows/src/desktop/kmshell/locale/ru-RU/strings.xml @@ -647,10 +647,6 @@ ru - - - - ru diff --git a/windows/src/desktop/kmshell/locale/shu-latn/strings.xml b/windows/src/desktop/kmshell/locale/shu-latn/strings.xml index 06c95b9f6de..ebf8995a12c 100644 --- a/windows/src/desktop/kmshell/locale/shu-latn/strings.xml +++ b/windows/src/desktop/kmshell/locale/shu-latn/strings.xml @@ -646,10 +646,6 @@ en - - - - en diff --git a/windows/src/desktop/kmshell/locale/sv-SE/strings.xml b/windows/src/desktop/kmshell/locale/sv-SE/strings.xml index bec64002587..7bba7833fe5 100644 --- a/windows/src/desktop/kmshell/locale/sv-SE/strings.xml +++ b/windows/src/desktop/kmshell/locale/sv-SE/strings.xml @@ -647,10 +647,6 @@ tangentbord som du använder i Windows. Keyman tangentbord kommer att anpassas a sv - - - - sv diff --git a/windows/src/desktop/kmshell/locale/ta/strings.xml b/windows/src/desktop/kmshell/locale/ta/strings.xml index 63214c2104f..38a91f66656 100644 --- a/windows/src/desktop/kmshell/locale/ta/strings.xml +++ b/windows/src/desktop/kmshell/locale/ta/strings.xml @@ -155,9 +155,6 @@ Entries not in this file will be sourced from the default locale.xml file in the Tamil - - '%0:s' தட்டசு பலகை ஏற்கனவே நிறுவப்பட்டுள்ளது. தொடர்ந்தால் ஏற்கனவே உள்ளதை நீக்கி விட்டு, புதியதை நிறுவவா? diff --git a/windows/src/desktop/kmshell/locale/tr/strings.xml b/windows/src/desktop/kmshell/locale/tr/strings.xml index 688bc45dcb3..e32f2a09b56 100644 --- a/windows/src/desktop/kmshell/locale/tr/strings.xml +++ b/windows/src/desktop/kmshell/locale/tr/strings.xml @@ -228,7 +228,6 @@ Klavye başarıyla yazdırılamadı. Lütfen bir web sayfasına kaydedin ve manuel olarak yazdırın. <Klavye zarar görmüş> Keyman hata ayıklama bilgileri masaüstünüzde keymanlog\system.log adlı metin dosyasında saklanır. Bu dosyayı okumadan ve silmeden önce Keyman çıkış yapmalısınız. UYARI: Bu dosya çok hızlı büyür. Hata ayıklamayı etkinleştirmek sisteminizi yavaşlatır ve yalnızca Tavultesoft destek birimi tarafından tavsiye edilirse yapılmalıdır. GİZLİLİK UYARISI: Hata ayıklama günlük dosyası yazdığınız tuş vuruşlarının tamamını kaydeder. Hata ayıklama günlüğünü sadece hata ayıklama veya tanılama oturumunun süresi için açmalısınız ve [masaüstü]\keymanlog\system.log dosyanızı en kısa sürede silmeniz gerekir. - en Kullanıcı Arabirimi Dili Seçin Çevrimiçi olarak yeni bir çeviri hazırla Dosya İndiriyor diff --git a/windows/src/desktop/kmshell/locale/uk-UA/strings.xml b/windows/src/desktop/kmshell/locale/uk-UA/strings.xml index 6e54690f54e..6d2e9e092b1 100644 --- a/windows/src/desktop/kmshell/locale/uk-UA/strings.xml +++ b/windows/src/desktop/kmshell/locale/uk-UA/strings.xml @@ -647,10 +647,6 @@ uk-UA - - - - uk-UA diff --git a/windows/src/desktop/kmshell/locale/vec/strings.xml b/windows/src/desktop/kmshell/locale/vec/strings.xml index 2b2dcc477df..47c1a96cc7f 100644 --- a/windows/src/desktop/kmshell/locale/vec/strings.xml +++ b/windows/src/desktop/kmshell/locale/vec/strings.xml @@ -161,7 +161,6 @@ No se pol stanpar ƚa tastièra. Prègo, salva inte na pàxena web e stanpa manualmente. <Tastièra danexada> Ƚa informasion de debug Keyman ƚa vegnarà salvada inte un file de tèsto de nòme keymanlog\system.log so 'l to desktop. Te gà da nar fora da Keyman inanso de lèxar o scanseƚar sto file. ATENSION: sto file el pol deventar grando pasto in poco tenpo. Ativando el debugging se ralentarà el to sistèma e se gà da farlo nòma che se consejà da 'l supòrto de Tavultesoft. VERTIMENTO DE PRIVACY: tien conto che el logfile de debug el rejistra tute ƚe batùe de tastièra che te fà. Te gà da inpisar el log de debug nòma che durante na sesion de debugging o diagnòstega, e scanseƚar el file \keymanlog\system.log da 'l desktop pì presto che se pol. - en Desernisi lengua de ƚa interfacia utente Descargo de 'l file No se pol ativar el navegador o el programa de email par sto URL. diff --git a/windows/src/desktop/kmshell/locale/vi-VN/strings.xml b/windows/src/desktop/kmshell/locale/vi-VN/strings.xml index 8e75db2fb0e..2cf66d5d5a3 100644 --- a/windows/src/desktop/kmshell/locale/vi-VN/strings.xml +++ b/windows/src/desktop/kmshell/locale/vi-VN/strings.xml @@ -713,10 +713,6 @@ vi - - - - vi diff --git a/windows/src/desktop/kmshell/locale/zh-CN/strings.xml b/windows/src/desktop/kmshell/locale/zh-CN/strings.xml index ae99513afb6..c9049cd2a97 100644 --- a/windows/src/desktop/kmshell/locale/zh-CN/strings.xml +++ b/windows/src/desktop/kmshell/locale/zh-CN/strings.xml @@ -647,10 +647,6 @@ 英语 - - - - 英语 diff --git a/windows/src/desktop/kmshell/main/Keyman.Configuration.System.KeymanUILanguageManager.pas b/windows/src/desktop/kmshell/main/Keyman.Configuration.System.KeymanUILanguageManager.pas index 8a303747de7..b1b3c8c5d5a 100644 --- a/windows/src/desktop/kmshell/main/Keyman.Configuration.System.KeymanUILanguageManager.pas +++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.System.KeymanUILanguageManager.pas @@ -48,7 +48,6 @@ class function TKeymanUILanguageManager.GetKeymanUILanguages: TStrings; begin Result := TStringList.Create; Result.Text := LowerCase(KeymanCustomisation.CustMessages.GetAvailableLanguages); - Result.Insert(0, LowerCase(KeymanCustomisation.CustMessages.MessageFromID('SKDefaultLanguageCode'))); end; end. diff --git a/windows/src/desktop/kmshell/util/UILanguages.pas b/windows/src/desktop/kmshell/util/UILanguages.pas index 83519c23d08..2cbe5dfc833 100644 --- a/windows/src/desktop/kmshell/util/UILanguages.pas +++ b/windows/src/desktop/kmshell/util/UILanguages.pas @@ -108,7 +108,7 @@ procedure TUILanguages.Refresh; begin with kmint.KeymanCustomisation.CustMessages do begin - FLanguagesAvailableString := MsgFromId(SKDefaultLanguageCode)+#13#10+GetAvailableLanguages; + FLanguagesAvailableString := GetAvailableLanguages; FLanguages.Text := FLanguagesAvailableString; for i := 0 to FLanguages.Count - 1 do diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml index b414fd891c2..49287614482 100644 --- a/windows/src/desktop/kmshell/xml/strings.xml +++ b/windows/src/desktop/kmshell/xml/strings.xml @@ -919,11 +919,6 @@ keyboard that you use in Windows. Keyman keyboards will adapt automatically to en - - - - en - diff --git a/windows/src/global/delphi/cust/CustomisationMessages.pas b/windows/src/global/delphi/cust/CustomisationMessages.pas index d79e9b32640..46c5e6fcd9c 100644 --- a/windows/src/global/delphi/cust/CustomisationMessages.pas +++ b/windows/src/global/delphi/cust/CustomisationMessages.pas @@ -79,11 +79,18 @@ implementation uses DebugPaths, Keyman.System.AndroidStringToKeymanLocaleString, + KeymanPaths, + KeymanVersion, KLog, ErrorControlledRegistry, MessageIdentifierConsts, RegistryKeys; +const + S_DefaultLocale = 'en'; + S_DefaultLocaleName = 'English'; + S_TestLocale = 'qqq'; + { TCustomisationMessageManager } constructor TCustomisationMessageManager.Create(ACustStorageFilename: string; ALoadLocale: Boolean = True); @@ -127,11 +134,28 @@ function TCustomisationMessageManager.GetAvailableLanguages: string; Result := True; end; + procedure AddDefaultLanguage; + var + locale: TCustomisationLocale; + begin + locale := TCustomisationLocale.Create; + locale.ID := S_DefaultLocale; + locale.Name := S_DefaultLocaleName; + locale.NameWithEnglish := S_DefaultLocaleName; + FLanguages.Add(locale.ID, locale); + end; + begin - Result := ''; - FLocalePath := ExtractFilePath(FCustStorageFileName)+'locale\'; + FLocalePath := TKeymanPaths.KeymanLocalePath; FLanguages.Clear; + // Add default locale + + AddDefaultLanguage; + Result := S_DefaultLocale + #13#10; + + // Load other defined locales + try FLocaleIndexDoc := CoDomDocument.Create; FLocaleIndexDoc.preserveWhiteSpace := True; @@ -152,6 +176,9 @@ function TCustomisationMessageManager.GetAvailableLanguages: string; locale := TCustomisationLocale.Create; if not LoadLocaleData(node, locale) then locale.Free + else if (locale.ID = S_TestLocale) and (CKeymanVersionInfo.Environment <> ENVIRONMENT_LOCAL) and (CKeymanVersionInfo.Environment <> ENVIRONMENT_TEST) then + // Exclude test UI except when running a test build + locale.Free else begin FLanguages.Add(locale.ID, locale); @@ -163,7 +190,9 @@ function TCustomisationMessageManager.GetAvailableLanguages: string; except // If any locale items are invalid, we will have only English // Currently, we cannot report errors to Sentry from kmcomapi - Result := ''; + FLanguages.Clear; + AddDefaultLanguage; + Result := S_DefaultLocale; end; end; @@ -200,7 +229,7 @@ function TCustomisationMessageManager.GetLocalePathForLocale( locale: TCustomisationLocale; begin if LocaleName = '' then - Result := ExtractFilePath(FCustStorageFileName)+'locale\' + Result := TKeymanPaths.KeymanLocalePath else if not FLanguages.TryGetValue(LocaleName, locale) then Result := '' else diff --git a/windows/src/test/manual-tests/test_i2605/locale-tam.xml b/windows/src/test/manual-tests/test_i2605/locale-tam.xml index 3154776c3c7..2bcf262d626 100644 --- a/windows/src/test/manual-tests/test_i2605/locale-tam.xml +++ b/windows/src/test/manual-tests/test_i2605/locale-tam.xml @@ -236,9 +236,6 @@ Entries not in this file will be sourced from the default locale.xml file in the Tamil - - '%0:s' தட்டசு பலகை ஏற்கனவே நிறுவப்பட்டுள்ளது. தொடர்ந்தால் ஏற்கனவே உள்ளதை நீக்கி விட்டு, புதியதை நிறுவவா?