diff --git a/HISTORY.md b/HISTORY.md
index 164a5a93ba..c660322647 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,5 +1,37 @@
# Keyman Version History
+## 14.0.181 alpha 2020-11-13
+
+* fix(common/core/web): fixes no-output logic check, arrow keys (#3848)
+* fix(common/core/web): adds missing null-check (#3859)
+
+## 14.0.180 alpha 2020-11-12
+
+* fix(windows): makefile format error (#3854)
+
+## 14.0.179 alpha 2020-11-12
+
+* fix(windows): Import OSK wrong for European layouts (#3830)
+* fix(windows): SizeOfImage header was wrong for dbg (#3833)
+* chore(windows): symbol server support (#3834)
+* fix(windows): sporadic 8087 control word corruption (#3842)
+
+## 14.0.178 alpha 2020-11-10
+
+* fix(developer): Remove IE dependency from Developer Setup (#3839)
+
+## 14.0.177 alpha 2020-11-07
+
+* fix(developer): support for notany() and context() (#3816)
+* fix(web): support for notany() and context() (#3817)
+* fix(developer): debug window inherits editor font (#3829)
+
+## 14.0.176 alpha 2020-11-05
+
+* fix(developer): remove obsolete NRSIAllTags (#3819)
+* fix(developer): Incorrect script:language map (#3818)
+* fix(android/app): Remove network check on "Get Started" menu (#3823)
+
## 14.0.175 alpha 2020-11-04
* feat(android/engine): Add check for associated model on ModelPickerActivity (#3808)
diff --git a/VERSION.md b/VERSION.md
index 64bddfe3d6..d82cce3e52 100644
--- a/VERSION.md
+++ b/VERSION.md
@@ -1 +1 @@
-14.0.176
\ No newline at end of file
+14.0.182
\ No newline at end of file
diff --git a/common/core/web/input-processor/package-lock.json b/common/core/web/input-processor/package-lock.json
index 4ce637c80e..38960e20a5 100644
--- a/common/core/web/input-processor/package-lock.json
+++ b/common/core/web/input-processor/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/input-processor",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/common/core/web/input-processor/package.json b/common/core/web/input-processor/package.json
index 4304320487..a36a826e45 100644
--- a/common/core/web/input-processor/package.json
+++ b/common/core/web/input-processor/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/input-processor",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "The core text and prediction processing engine for KeymanWeb",
"repository": {
"type": "git",
@@ -18,7 +18,7 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
- "@keymanapp/resources-gosh": "^14.0.176",
+ "@keymanapp/resources-gosh": "^14.0.182",
"chai": "^4.2.0",
"mocha": "^5.2.0",
"typescript": "^3.8.3"
@@ -29,11 +29,11 @@
"test": "gosh ./unit_tests/test.sh"
},
"dependencies": {
- "@keymanapp/keyboard-processor": "^14.0.176",
- "@keymanapp/lexical-model-layer": "^14.0.176",
- "@keymanapp/models-types": "^14.0.176",
- "@keymanapp/web-environment": "^14.0.176",
- "@keymanapp/web-utils": "^14.0.176",
+ "@keymanapp/keyboard-processor": "^14.0.182",
+ "@keymanapp/lexical-model-layer": "^14.0.182",
+ "@keymanapp/models-types": "^14.0.182",
+ "@keymanapp/web-environment": "^14.0.182",
+ "@keymanapp/web-utils": "^14.0.182",
"@types/node": "^11.9.4",
"eventemitter3": "^4.0.0",
"ts-node": "^8.0.2"
diff --git a/common/core/web/keyboard-processor/package-lock.json b/common/core/web/keyboard-processor/package-lock.json
index ec3c7d218d..05ed2ba2d2 100644
--- a/common/core/web/keyboard-processor/package-lock.json
+++ b/common/core/web/keyboard-processor/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/keyboard-processor",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/common/core/web/keyboard-processor/package.json b/common/core/web/keyboard-processor/package.json
index e13012dbca..70b475f84a 100644
--- a/common/core/web/keyboard-processor/package.json
+++ b/common/core/web/keyboard-processor/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/keyboard-processor",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Core module for Keyman keyboard support in KeymanWeb.",
"repository": {
"type": "git",
@@ -18,7 +18,7 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
- "@keymanapp/resources-gosh": "^14.0.176",
+ "@keymanapp/resources-gosh": "^14.0.182",
"chai": "^4.2.0",
"mocha": "^5.2.0",
"mocha-teamcity-reporter": "^3.0.0",
@@ -31,9 +31,9 @@
"mocha": "mocha"
},
"dependencies": {
- "@keymanapp/models-types": "^14.0.176",
- "@keymanapp/web-environment": "^14.0.176",
- "@keymanapp/web-utils": "^14.0.176",
+ "@keymanapp/models-types": "^14.0.182",
+ "@keymanapp/web-environment": "^14.0.182",
+ "@keymanapp/web-utils": "^14.0.182",
"@types/node": "^11.9.4",
"ts-node": "^8.0.2"
}
diff --git a/common/core/web/keyboard-processor/src/text/defaultOutput.ts b/common/core/web/keyboard-processor/src/text/defaultOutput.ts
index f0f917fa7d..0bea0dfd45 100644
--- a/common/core/web/keyboard-processor/src/text/defaultOutput.ts
+++ b/common/core/web/keyboard-processor/src/text/defaultOutput.ts
@@ -48,7 +48,7 @@ namespace com.keyman.text {
// case Codes.keyCodes['K_TABFWD']:
// return '\t';
default:
- return '';
+ return null;
}
}
}
diff --git a/common/core/web/keyboard-processor/src/text/kbdInterface.ts b/common/core/web/keyboard-processor/src/text/kbdInterface.ts
index 20bce0e22e..fd134d34d0 100644
--- a/common/core/web/keyboard-processor/src/text/kbdInterface.ts
+++ b/common/core/web/keyboard-processor/src/text/kbdInterface.ts
@@ -26,7 +26,7 @@ namespace com.keyman.text {
type PlainKeyboardStore = string;
export type KeyboardStoreElement = (string|StoreNonCharEntry);
- export type ComplexKeyboardStore = KeyboardStoreElement[];
+ export type ComplexKeyboardStore = KeyboardStoreElement[];
type KeyboardStore = PlainKeyboardStore | ComplexKeyboardStore;
@@ -65,12 +65,12 @@ namespace com.keyman.text {
/** Discriminant field - 'i' for `index()`.
*/
['t']: 'i';
-
+
/**
* Value: the Store from which to output
*/
['i']: KeyboardStore;
-
+
/**
* Offset: the offset in context for the corresponding `any()`.
*/
@@ -81,7 +81,7 @@ namespace com.keyman.text {
/** Discriminant field - 'c' for `context()`.
*/
['t']: 'c';
-
+
/**
* Value: The offset into the current rule's context to be matched.
*/
@@ -108,16 +108,16 @@ namespace com.keyman.text {
/**
* Cache of context storing and retrieving return values from KC
* Must be reset prior to each keystroke and after any text changes
- * MCD 3/1/14
- **/
+ * MCD 3/1/14
+ **/
class CachedContext {
_cache: string[][];
-
- reset(): void {
- this._cache = [];
+
+ reset(): void {
+ this._cache = [];
}
- get(n: number, ln: number): string {
+ get(n: number, ln: number): string {
// return null; // uncomment this line to disable context caching
if(typeof this._cache[n] == 'undefined') {
return null;
@@ -127,22 +127,22 @@ namespace com.keyman.text {
return this._cache[n][ln];
}
- set(n: number, ln: number, val: string): void {
- if(typeof this._cache[n] == 'undefined') {
- this._cache[n] = [];
- }
- this._cache[n][ln] = val;
+ set(n: number, ln: number, val: string): void {
+ if(typeof this._cache[n] == 'undefined') {
+ this._cache[n] = [];
+ }
+ this._cache[n][ln] = val;
}
};
type CachedExEntry = {valContext: (string|number)[], deadContext: text.Deadkey[]};
- /**
- * An extended version of cached context storing designed to work with
+ /**
+ * An extended version of cached context storing designed to work with
* `fullContextMatch` and its helper functions.
*/
class CachedContextEx {
_cache: CachedExEntry[][];
-
+
reset(): void {
this._cache = [];
}
@@ -157,11 +157,17 @@ namespace com.keyman.text {
return this._cache[n][ln];
}
- set(n: number, ln: number, val: CachedExEntry): void {
- if(typeof this._cache[n] == 'undefined') {
- this._cache[n] = [];
- }
- this._cache[n][ln] = val;
+ set(n: number, ln: number, val: CachedExEntry): void {
+ if(typeof this._cache[n] == 'undefined') {
+ this._cache[n] = [];
+ }
+ this._cache[n][ln] = val;
+ }
+
+ clone(): CachedContextEx {
+ let r = new CachedContextEx();
+ r._cache = this._cache;
+ return r;
}
};
@@ -172,6 +178,7 @@ namespace com.keyman.text {
cachedContext: CachedContext = new CachedContext();
cachedContextEx: CachedContextEx = new CachedContextEx();
+ ruleContextEx: CachedContextEx;
activeTargetOutput: OutputTarget;
ruleBehavior: RuleBehavior;
@@ -191,7 +198,7 @@ namespace com.keyman.text {
constructor(variableStoreSerializer: VariableStoreSerializer = null) {
this.systemStores = {};
-
+
this.systemStores[KeyboardInterface.TSS_PLATFORM] = new PlatformSystemStore(this);
this.systemStores[KeyboardInterface.TSS_LAYER] = new MutableSystemStore(KeyboardInterface.TSS_LAYER, 'default');
@@ -201,61 +208,61 @@ namespace com.keyman.text {
/**
* Function KSF
* Scope Public
- *
+ *
* Saves the document's current focus settings on behalf of the keyboard. Often paired with insertText.
- */
+ */
saveFocus(): void { }
/**
* A text-insertion method used by custom OSKs for helpHTML interaction, like with sil_euro_latin.
- *
+ *
* This function currently bypasses web-core's standard text handling control path and all predictive text processing.
* It also has DOM-dependencies that help ensure KMW's active OutputTarget retains focus during use.
*/
insertText?: (Ptext: string, PdeadKey: number) => boolean;
-
+
/**
- * Function registerKeyboard KR
+ * Function registerKeyboard KR
* Scope Public
* @param {Object} Pk Keyboard object
* Description Registers a keyboard with KeymanWeb once its script has fully loaded.
- *
+ *
* In web-core, this also activates the keyboard; in other modules, this method
* may be replaced with other implementations.
- */
+ */
registerKeyboard(Pk): void {
- // NOTE: This implementation is web-core specific and is intentionally replaced, whole-sale,
+ // NOTE: This implementation is web-core specific and is intentionally replaced, whole-sale,
// by DOM-aware code.
let keyboard = new keyboards.Keyboard(Pk);
this.activeKeyboard = keyboard;
}
/**
- * Used by DOM-aware KeymanWeb to add keyboard stubs, used by the `KeyboardManager` type
+ * Used by DOM-aware KeymanWeb to add keyboard stubs, used by the `KeyboardManager` type
* to optimize resource use.
- */
+ */
registerStub?: (Pstub) => number;
/**
* Get *cached or uncached* keyboard context for a specified range, relative to caret
- *
+ *
* @param {number} n Number of characters to move back from caret
* @param {number} ln Number of characters to return
* @param {Object} Pelem Element to work with (must be currently focused element)
- * @return {string} Context string
- *
+ * @return {string} Context string
+ *
* Example [abcdef|ghi] as INPUT, with the caret position marked by |:
* KC(2,1,Pelem) == "e"
* KC(3,3,Pelem) == "def"
* KC(10,10,Pelem) == "abcdef" i.e. return as much as possible of the requested string
- */
-
+ */
+
context(n: number, ln: number, outputTarget: OutputTarget): string {
var v = this.cachedContext.get(n, ln);
if(v !== null) {
return v;
}
-
+
var r = this.KC_(n, ln, outputTarget);
this.cachedContext.set(n, ln, r);
return r;
@@ -263,17 +270,17 @@ namespace com.keyman.text {
/**
* Get (uncached) keyboard context for a specified range, relative to caret
- *
+ *
* @param {number} n Number of characters to move back from caret
* @param {number} ln Number of characters to return
* @param {Object} Pelem Element to work with (must be currently focused element)
- * @return {string} Context string
- *
+ * @return {string} Context string
+ *
* Example [abcdef|ghi] as INPUT, with the caret position marked by |:
* KC(2,1,Pelem) == "e"
* KC(3,3,Pelem) == "def"
* KC(10,10,Pelem) == "XXXXabcdef" i.e. return as much as possible of the requested string, where X = \uFFFE
- */
+ */
private KC_(n: number, ln: number, outputTarget: OutputTarget): string {
var tempContext = '';
@@ -285,29 +292,29 @@ namespace com.keyman.text {
return tempContext._kmwSubstr(-n)._kmwSubstr(0,ln);
}
-
+
/**
- * Function nul KN
+ * Function nul KN
* Scope Public
* @param {number} n Length of context to check
* @param {Object} Ptarg Element to work with (must be currently focused element)
* @return {boolean} True if length of context is less than or equal to n
* Description Test length of context, return true if the length of the context is less than or equal to n
- *
+ *
* Example [abc|def] as INPUT, with the caret position marked by |:
* KN(3,Pelem) == TRUE
* KN(2,Pelem) == FALSE
* KN(4,Pelem) == TRUE
- */
+ */
nul(n: number, outputTarget: OutputTarget): boolean {
var cx=this.context(n+1, 1, outputTarget);
-
+
// With #31, the result will be a replacement character if context is empty.
return cx === "\uFFFE";
}
/**
- * Function contextMatch KCM
+ * Function contextMatch KCM
* Scope Public
* @param {number} n Number of characters to move back from caret
* @param {Object} Ptarg Focused element
@@ -315,7 +322,7 @@ namespace com.keyman.text {
* @param {number} ln Number of characters to return
* @return {boolean} True if selected context matches val
* Description Test keyboard context for match
- */
+ */
contextMatch(n: number, outputTarget: OutputTarget, val: string, ln: number): boolean {
var cx=this.context(n, ln, outputTarget);
if(cx === val) {
@@ -327,14 +334,14 @@ namespace com.keyman.text {
/**
* Builds the *cached or uncached* keyboard context for a specified range, relative to caret
- *
+ *
* @param {number} n Number of characters to move back from caret
* @param {number} ln Number of characters to return
* @param {Object} Pelem Element to work with (must be currently focused element)
- * @return {Array} Context array (of strings and numbers)
+ * @return {Array} Context array (of strings and numbers)
*/
private _BuildExtendedContext(n: number, ln: number, outputTarget: OutputTarget): CachedExEntry {
- var cache: CachedExEntry = this.cachedContextEx.get(n, ln);
+ var cache: CachedExEntry = this.cachedContextEx.get(n, ln);
if(cache !== null) {
return cache;
} else {
@@ -398,13 +405,14 @@ namespace com.keyman.text {
* @param {Object} Ptarg Focused element
* @param {Array} rule An array of ContextEntries to match.
* @return {boolean} True if the fully-specified rule context matches the current KMW state.
- *
+ *
* A KMW 10+ function designed to bring KMW closer to Keyman Desktop functionality,
* near-directly modeling (externally) the compiled form of Desktop rules' context section.
*/
fullContextMatch(n: number, outputTarget: OutputTarget, rule: ContextEntry[]): boolean {
// Stage one: build the context index map.
var fullContext = this._BuildExtendedContext(n, rule.length, outputTarget);
+ this.ruleContextEx = this.cachedContextEx.clone();
var context = fullContext.valContext;
var deadContext = fullContext.deadContext;
@@ -431,7 +439,7 @@ namespace com.keyman.text {
var r = rule[i] as ContextNonCharEntry;
switch(r.t) {
case 'd':
- // We still need to set a flag here;
+ // We still need to set a flag here;
if(r['d'] !== context[i]) {
mismatch = true;
} else {
@@ -457,7 +465,7 @@ namespace com.keyman.text {
deadContext[i].set();
}
// 'n' for 'notany'. If we actually match or if we have nul context (\uFFFE), notany fails.
- } else if(r.n && (result || context[i] !== NUL_CONTEXT)) {
+ } else if(r.n && (result || context[i] === NUL_CONTEXT)) {
mismatch = true;
}
break;
@@ -471,7 +479,7 @@ namespace com.keyman.text {
deadContext[i].set();
}
break;
- case 'c':
+ case 'c':
if(context[r.c - 1] !== context[i]) {
mismatch = true;
} else if(deadContext[i] !== undefined) {
@@ -500,12 +508,12 @@ namespace com.keyman.text {
}
/**
- * Function KIK
+ * Function KIK
* Scope Public
* @param {Object} e keystroke event
* @return {boolean} true if keypress event
* Description Test if event as a keypress event
- */
+ */
isKeypress(e: KeyEvent): boolean {
if(this.activeKeyboard.isMnemonic) { // I1380 - support KIK for positional layouts
return !e.LisVirtualKey; // will now return true for U_xxxx keys, but not for T_xxxx keys
@@ -513,16 +521,16 @@ namespace com.keyman.text {
return KeyMapping._USKeyCodeToCharCode(e) ? true : false; // I1380 - support KIK for positional layouts
}
}
-
+
/**
- * Function keyMatch KKM
+ * Function keyMatch KKM
* Scope Public
* @param {Object} e keystroke event
* @param {number} Lruleshift
* @param {number} Lrulekey
* @return {boolean} True if key matches rule
* Description Test keystroke with modifiers against rule
- */
+ */
keyMatch(e: KeyEvent, Lruleshift:number, Lrulekey:number): boolean {
var retVal = false; // I3318
var keyCode = (e.Lcode == 173 ? 189 : e.Lcode); //I3555 (Firefox hyphen issue)
@@ -535,7 +543,7 @@ namespace com.keyman.text {
if(e.vkCode > 255) {
keyCode = e.vkCode; // added to support extended (touch-hold) keys for mnemonic layouts
}
-
+
if(e.LisVirtualKey || keyCode > 255) {
if((Lruleshift & 0x4000) == 0x4000 || (keyCode > 255)) { // added keyCode test to support extended keys
retVal = ((Lrulekey == keyCode) && ((Lruleshift & modifierBitmask) == e.Lmodifiers)); //I3318, I3555
@@ -554,7 +562,7 @@ namespace com.keyman.text {
* Function stateMatch KSM
* Scope Public
* @param {Object} e keystroke event
- * @param {number} Lstate
+ * @param {number} Lstate
* Description Test keystroke against state key rules
*/
stateMatch(e: KeyEvent, Lstate: number) {
@@ -567,7 +575,7 @@ namespace com.keyman.text {
* @param {Object} e
* @return {Object} Object with event's virtual key flag, key code, and modifiers
* Description Get object with extended key event information
- */
+ */
keyInformation(e: KeyEvent): KeyInformation {
var ei = new KeyInformation();
ei['vk'] = e.LisVirtualKey;
@@ -577,24 +585,24 @@ namespace com.keyman.text {
};
/**
- * Function deadkeyMatch KDM
+ * Function deadkeyMatch KDM
* Scope Public
* @param {number} n offset from current cursor position
* @param {Object} Ptarg target element
* @param {number} d deadkey
* @return {boolean} True if deadkey found selected context matches val
* Description Match deadkey at current cursor position
- */
+ */
deadkeyMatch(n: number, outputTarget: OutputTarget, d: number): boolean {
return outputTarget.hasDeadkeyMatch(n, d);
}
-
+
/**
- * Function beep KB
+ * Function beep KB
* Scope Public
* @param {Object} Pelem element to flash
* Description Flash body as substitute for audible beep; notify embedded device to vibrate
- */
+ */
beep(outputTarget: OutputTarget): void {
this.resetContextCache();
@@ -624,21 +632,21 @@ namespace com.keyman.text {
return store;
}
}
-
+
/**
- * Function any KA
+ * Function any KA
* Scope Public
- * @param {number} n character position (index)
+ * @param {number} n character position (index)
* @param {string} ch character to find in string
- * @param {string} s 'any' string
+ * @param {string} s 'any' string
* @return {boolean} True if character found in 'any' string, sets index accordingly
* Description Test for character matching
- */
+ */
any(n: number, ch: KeyboardStoreElement, s: KeyboardStore): boolean {
if(ch == '') {
return false;
}
-
+
s = this._ExplodeStore(s);
var Lix = -1;
for(var i=0; i < s.length; i++) {
@@ -658,18 +666,18 @@ namespace com.keyman.text {
/**
* Function _Index
- * Scope Public
+ * Scope Public
* @param {string} Ps string
* @param {number} Pn index
* Description Returns the character from a store string according to the offset in the index array
*/
- _Index(Ps: KeyboardStore, Pn: number): KeyboardStoreElement {
+ _Index(Ps: KeyboardStore, Pn: number): KeyboardStoreElement {
Ps = this._ExplodeStore(Ps);
if(this._AnyIndices[Pn-1] < Ps.length) { //I3319
return Ps[this._AnyIndices[Pn-1]];
} else {
- /* Should not be possible for a compiled keyboard, but may arise
+ /* Should not be possible for a compiled keyboard, but may arise
* during the development of handwritten keyboards.
*/
console.warn("Unmatched contextual index() statement detected in rule with index " + Pn + "!");
@@ -714,16 +722,16 @@ namespace com.keyman.text {
} else { // For keyboards developed during 10.0's alpha phase - t:'d' was assumed.
this.deadkeyOutput(Pdn, outputTarget, indexChar['d']);
}
- }
+ }
}
-
-
+
+
/**
- * Function deleteContext KDC
+ * Function deleteContext KDC
* Scope Public
* @param {number} dn number of context entries to overwrite
- * @param {Object} Pelem element to output to
- * @param {string} s string to output
+ * @param {Object} Pelem element to output to
+ * @param {string} s string to output
* Description Keyboard output
*/
deleteContext(dn: number, outputTarget: OutputTarget): void {
@@ -765,11 +773,11 @@ namespace com.keyman.text {
}
/**
- * Function output KO
+ * Function output KO
* Scope Public
* @param {number} dn number of characters to overwrite
- * @param {Object} Pelem element to output to
- * @param {string} s string to output
+ * @param {Object} Pelem element to output to
+ * @param {string} s string to output
* Description Keyboard output
*/
output(dn: number, outputTarget: OutputTarget, s:string): void {
@@ -786,16 +794,45 @@ namespace com.keyman.text {
outputTarget.insertTextBeforeCaret(s);
outputTarget.restoreProperties();
}
-
-
+
/**
- * Function deadkeyOutput KDO
+ * `contextExOutput` function emits the character or object at `contextOffset` from the
+ * current matched rule's context. Introduced in Keyman 14.0, in order to resolve a
+ * gap between desktop and web core functionality for context(n) matching on notany().
+ * See #917 for additional detail.
+ * @alias KCXO
+ * @public
+ * @param {number} Pdn number of characters to delete left of cursor
+ * @param {OutputTarget} outputTarget target to output to
+ * @param {number} contextLength length of current rule context to retrieve
+ * @param {number} contextOffset offset from start of current rule context, 1-based
+ */
+ contextExOutput(Pdn: number, outputTarget: OutputTarget, contextLength: number, contextOffset: number): void {
+ this.resetContextCache();
+
+ if(Pdn >= 0) {
+ this.output(Pdn, outputTarget, "");
+ }
+
+ const context = this.ruleContextEx.get(contextLength, contextLength);
+ const dk = context.deadContext[contextOffset-1], vc = context.valContext[contextOffset-1];
+ if(dk) {
+ outputTarget.insertDeadkeyBeforeCaret(dk.d);
+ } else if(typeof vc == 'string') {
+ this.output(-1, outputTarget, vc);
+ } else {
+ throw new Error("contextExOutput: should never be a numeric valContext with no corresponding deadContext");
+ }
+ }
+
+ /**
+ * Function deadkeyOutput KDO
* Scope Public
- * @param {number} Pdn no of character to overwrite (delete)
- * @param {Object} Pelem element to output to
+ * @param {number} Pdn no of character to overwrite (delete)
+ * @param {Object} Pelem element to output to
* @param {number} Pd deadkey id
* Description Record a deadkey at current cursor position, deleting Pdn characters first
- */
+ */
deadkeyOutput(Pdn: number, outputTarget: OutputTarget, Pd: number): void {
this.resetContextCache();
@@ -806,36 +843,36 @@ namespace com.keyman.text {
outputTarget.insertDeadkeyBeforeCaret(Pd);
// _DebugDeadKeys(Pelem, 'KDeadKeyOutput: dn='+Pdn+'; deadKey='+Pd);
}
-
+
/**
- * KIFS compares the content of a system store with a string value
- *
+ * KIFS compares the content of a system store with a string value
+ *
* @param {number} systemId ID of the system store to test (only TSS_LAYER currently supported)
* @param {string} strValue String value to compare to
- * @param {Object} Pelem Currently active element (may be needed by future tests)
- * @return {boolean} True if the test succeeds
- */
+ * @param {Object} Pelem Currently active element (may be needed by future tests)
+ * @return {boolean} True if the test succeeds
+ */
ifStore(systemId: number, strValue: string, outputTarget: OutputTarget): boolean {
var result=true;
let store = this.systemStores[systemId];
if(store) {
result = store.matches(strValue);
}
- return result; //Moved from previous line, now supports layer selection, Build 350
+ return result; //Moved from previous line, now supports layer selection, Build 350
}
/**
- * KSETS sets the value of a system store to a string
- *
+ * KSETS sets the value of a system store to a string
+ *
* @param {number} systemId ID of the system store to set (only TSS_LAYER currently supported)
- * @param {string} strValue String to set as the system store content
- * @param {Object} Pelem Currently active element (may be needed in future tests)
+ * @param {string} strValue String to set as the system store content
+ * @param {Object} Pelem Currently active element (may be needed in future tests)
* @return {boolean} True if command succeeds
* (i.e. for TSS_LAYER, if the layer is successfully selected)
- *
+ *
* Note that option/variable stores are instead set within keyboard script code, as they only
* affect keyboard behavior.
- */
+ */
setStore(systemId: number, strValue: string, outputTarget: OutputTarget): boolean {
this.resetContextCache();
if(systemId == KeyboardInterface.TSS_LAYER) {
@@ -848,15 +885,15 @@ namespace com.keyman.text {
/**
* Load an option store value from a cookie or default value
- *
+ *
* @param {string} kbdName keyboard internal name
* @param {string} storeName store (option) name, embedded in cookie name
* @param {string} dfltValue default value
- * @return {string} current or default option value
- *
+ * @return {string} current or default option value
+ *
* This will only ever be called when the keyboard is loaded, as it is used by keyboards
* to initialize a store value on the keyboard's script object.
- */
+ */
loadStore(kbdName: string, storeName:string, dfltValue:string): string {
this.resetContextCache();
if(this.variableStoreSerializer) {
@@ -868,16 +905,16 @@ namespace com.keyman.text {
}
/**
- * Save an option store value to a cookie
- *
+ * Save an option store value to a cookie
+ *
* @param {string} storeName store (option) name, embedded in cookie name
* @param {string} optValue option value to save
* @return {boolean} true if save successful
- *
+ *
* Note that a keyboard will freely manipulate the value of its variable stores on the
* script object within its own code. This function's use is merely to _persist_ that
* value across sessions, providing a custom user default for later uses of the keyboard.
- */
+ */
saveStore(storeName:string, optValue:string): boolean {
this.resetContextCache();
var kbd=this.activeKeyboard;
@@ -927,12 +964,12 @@ namespace com.keyman.text {
outputTarget.invalidateSelection();
- outputTarget.deadkeys().resetMatched(); // I3318
+ outputTarget.deadkeys().resetMatched(); // I3318
this.resetContextCache();
// Capture the initial state of the OutputTarget before any rules are matched.
let preInput = Mock.from(outputTarget);
-
+
// Establishes the results object, allowing corresponding commands to set values here as appropriate.
this.ruleBehavior = new RuleBehavior();
@@ -963,7 +1000,7 @@ namespace com.keyman.text {
* Publishes the KeyboardInterface's shorthand API names. As this assigns the current functions
* held by the longform versions, note that this should be called after replacing any of them via
* JS method extension.
- *
+ *
* DOM-aware KeymanWeb should call this after its domKbdInterface.ts code is loaded, as it replaces
* a few. (This is currently done within its kmwapi.ts.)
*/
@@ -994,6 +1031,7 @@ namespace com.keyman.text {
exportKBCallback('KDC', 'deleteContext');
exportKBCallback('KO', 'output');
exportKBCallback('KDO', 'deadkeyOutput');
+ exportKBCallback('KCXO', 'contextExOutput');
exportKBCallback('KIO', 'indexOutput');
exportKBCallback('KIFS', 'ifStore');
exportKBCallback('KSETS', 'setStore');
diff --git a/common/core/web/keyboard-processor/src/text/keyboardProcessor.ts b/common/core/web/keyboard-processor/src/text/keyboardProcessor.ts
index 1226c889c1..8f30d25101 100644
--- a/common/core/web/keyboard-processor/src/text/keyboardProcessor.ts
+++ b/common/core/web/keyboard-processor/src/text/keyboardProcessor.ts
@@ -186,7 +186,7 @@ namespace com.keyman.text {
Lkc.Lstates |= this.stateKeys['K_SCROLL'] ? Codes.modifierCodes['SCROLL_LOCK'] : Codes.modifierCodes['NO_SCROLL_LOCK'];
// Set LisVirtualKey to false to ensure that nomatch rule does fire for U_xxxx keys
- if(Lkc.kName.substr(0,2) == 'U_') {
+ if(Lkc.kName && Lkc.kName.substr(0,2) == 'U_') {
Lkc.LisVirtualKey=false;
}
diff --git a/common/core/web/keyboard-processor/tests/cases/engine/notany_context.js b/common/core/web/keyboard-processor/tests/cases/engine/notany_context.js
new file mode 100644
index 0000000000..1b502fa99b
--- /dev/null
+++ b/common/core/web/keyboard-processor/tests/cases/engine/notany_context.js
@@ -0,0 +1,112 @@
+const assert = require('chai').assert;
+const fs = require('fs');
+const vm = require('vm');
+
+const KeyboardProcessor = require('../../../dist');
+const KMWRecorder = require('../../../../tools/recorder/dist/nodeProctor');
+
+// Required initialization setup.
+global.com = KeyboardProcessor.com; // exports all keyboard-processor namespacing.
+global.keyman = {}; // So that keyboard-based checks against the global `keyman` succeed.
+ // 10.0+ dependent keyboards, like khmer_angkor, will otherwise fail to load.
+
+// Initialize supplementary plane string extensions
+String.kmwEnableSupplementaryPlane(false);
+
+const device = {
+ formFactor: 'desktop',
+ OS: 'windows',
+ browser: 'native'
+}
+
+let keyboard;
+
+function runEngineRuleSet(ruleSet) {
+ for(let ruleDef of ruleSet) {
+ // Prepare the context!
+ const ruleSeq = new KMWRecorder.RecordedKeystrokeSequence(ruleDef);
+ const proctor = new KMWRecorder.NodeProctor(keyboard, device, assert.equal);
+ const target = new com.keyman.text.Mock();
+ ruleSeq.test(proctor, target);
+ }
+}
+
+/**
+ * Wrapper to simplify running tests -- supports either virtual key codes as string (e.g. 'A' is VK_A)
+ * or an array of integers. Does not currently support modifiers (not needed here).
+ * @param {String|Array} input Virtual key codes of each character (as string or array)
+ * @param {String} output Expected output
+ */
+
+function runStringRuleSet(input, output) {
+ const rule = {
+ "inputs":
+ typeof input == 'string'
+ ? input.split("").map(ch =>
+ { return { "type": "key", "keyCode": ch.charCodeAt(0), "states": 10752, "modifiers": 0, "modifierChanged": false, "isVirtualKey": true } })
+ : input.map(ch =>
+ { return { "type": "key", "keyCode": ch, "states": 10752, "modifiers": 0, "modifierChanged": false, "isVirtualKey": true } }),
+ "output": output
+ };
+ return runEngineRuleSet([rule]);
+}
+
+// -----------
+
+describe('Engine - notany() and context()', function() {
+ before(function() {
+ const kp = new KeyboardProcessor();
+
+ // These two lines will load a keyboard from its file; headless-mode `registerKeyboard` will
+ // automatically set the keyboard as active.
+ const script = new vm.Script(fs.readFileSync('../tests/resources/keyboards/test_917.js'));
+ script.runInThisContext();
+
+ keyboard = kp.activeKeyboard;
+ });
+
+ /*
+ * Isolated rule tests for validity of `context()` and `notany()` statements.
+ */
+
+ describe('First set -- notany() in first context position', function() {
+ it("baseline: rule with nomatch is not executed", function() {
+ runStringRuleSet('ABC', 'abc');
+ });
+
+ it("rule with nomatch is executed with a plain char matched", function() {
+ runStringRuleSet('XBC', 'xBC');
+ });
+
+ it("rule with nomatch is executed with a deadkey matched", function() {
+ runStringRuleSet('DBC1', 'pass!');
+ });
+ });
+
+ describe('Second set -- notany() in second context position', function() {
+ it("rule with nomatch is executed with a plain char matched", function() {
+ runStringRuleSet('AXCD1', 'axXX1');
+ });
+
+ it("rule with nomatch is executed with a deadkey matched", function() {
+ runStringRuleSet('ADCD1', 'pass!');
+ });
+ });
+
+ describe('Third set -- notany() in third context position with initial if() testing AdjustIndex', function() {
+ it("rule with nomatch is executed with a plain char matched", function() {
+ runStringRuleSet('FGHI1', 'fgXX1');
+ });
+
+ it("rule with nomatch is executed with a deadkey matched", function() {
+ runStringRuleSet('FDHI1', 'pass!');
+ });
+ });
+
+ // This is actually a separate issue that was corrected in the same PR
+ // so we placed the test here for conciseness.
+
+ it('verify notany comparison flip correction from #3817', function() {
+ runStringRuleSet('YZ', 'pass!');
+ });
+});
\ No newline at end of file
diff --git a/common/core/web/tests/resources/keyboards/.gitignore b/common/core/web/tests/resources/keyboards/.gitignore
new file mode 100644
index 0000000000..2bf1ef8c0e
--- /dev/null
+++ b/common/core/web/tests/resources/keyboards/.gitignore
@@ -0,0 +1 @@
+debug.log
\ No newline at end of file
diff --git a/common/core/web/tests/resources/keyboards/test_917.js b/common/core/web/tests/resources/keyboards/test_917.js
new file mode 100644
index 0000000000..6bf1e2f108
--- /dev/null
+++ b/common/core/web/tests/resources/keyboards/test_917.js
@@ -0,0 +1 @@
+if(typeof keyman === 'undefined') {console.log('Keyboard requires KeymanWeb 10.0 or later');if(typeof tavultesoft !== 'undefined') tavultesoft.keymanweb.util.alert("This keyboard requires KeymanWeb 10.0 or later");} else {KeymanWeb.KR(new Keyboard_test_917());}function Keyboard_test_917(){this.KI="Keyboard_test_917";this.KN="Test #917 - notany() and context()";this.KMINVER="14.0";this.KV=null;this.KDU=0;this.KH='';this.KM=0;this.KBVER="1.0";this.KMBM=0x0000;this.s8="aeiou";this.s10="web";this.s11="native";this.KVER="13.0.9999.0";this.gs=function(t,e) {return this.g0(t,e);};this.g0=function(t,e) {var k=KeymanWeb,r=0,m=0;if(k.KKM(e,16384,49)) {if(k.KFCM(4,t,['a',{t:'d',d:0},'X','X'])){r=m=1;k.KDC(4,t);k.KO(-1,t,"pass!");}else if(k.KFCM(4,t,['f',{t:'d',d:0},'X','X'])){r=m=1;k.KDC(4,t);k.KO(-1,t,"pass!");}else if(k.KFCM(3,t,[{t:'d',d:0},'B','C'])){r=m=1;k.KDC(3,t);k.KO(-1,t,"pass!");}}else if(k.KKM(e,16384,67)) {if(k.KFCM(2,t,[{t:'a',a:this.s8,n:1},'b'])){r=m=1;k.KDC(2,t);k.KCXO(-1,t,2,1);k.KO(-1,t,"BC");}}else if(k.KKM(e,16384,68)) {if(k.KFCM(3,t,['a',{t:'a',a:this.s8,n:1},'c'])){r=m=1;k.KDC(3,t);k.KO(-1,t,"a");k.KCXO(-1,t,3,2);k.KO(-1,t,"XX");}else if(1){r=m=1;k.KDC(0,t);k.KDO(-1,t,0);}}else if(k.KKM(e,16384,73)) {if(k.KFCM(3,t,['f',{t:'a',a:this.s8,n:1},'h'])&&k.KIFS(31,this.s10,t)){r=m=1;k.KDC(3,t);k.KO(-1,t,"f");k.KCXO(-1,t,3,2);k.KO(-1,t,"XX");}else if(k.KFCM(3,t,['f',{t:'a',a:this.s8,n:1},'h'])&&k.KIFS(31,this.s11,t)){r=m=1;k.KDC(3,t);k.KO(-1,t,"f");k.KCXO(-1,t,3,2);k.KO(-1,t,"XX");}}else if(k.KKM(e,16384,90)) {if(k.KFCM(1,t,[{t:'a',a:this.s8,n:1}])){r=m=1;k.KDC(1,t);k.KO(-1,t,"pass!");}}return r;};}
\ No newline at end of file
diff --git a/common/core/web/tests/resources/keyboards/test_917.kmn b/common/core/web/tests/resources/keyboards/test_917.kmn
new file mode 100644
index 0000000000..f261c9064a
--- /dev/null
+++ b/common/core/web/tests/resources/keyboards/test_917.kmn
@@ -0,0 +1,46 @@
+c test_917 generated from template at 2020-11-04 06:59:27
+c with name "test_917"
+c store(&VERSION) '14.0'
+store(&NAME) 'Test #917 - notany() and context()'
+store(©RIGHT) '©'
+store(&KEYBOARDVERSION) '1.0'
+store(&TARGETS) 'web'
+c store(&VISUALKEYBOARD) 'test_917.kvks'
+c store(&LAYOUTFILE) 'test_917.keyman-touch-layout'
+
+begin Unicode > use(main)
+
+group(main) using keys
+
+c type: 'abc' > 'abc'
+c type: 'xbc' > 'xBC'
+c type: 'dbc' > DK(1) 'BC'
+c type: 'dbc1' > 'pass!'
+c
+store(vow) 'aeiou'
+notany(vow) 'b' + 'c' > context(1) 'BC'
++ 'd' > dk(1)
+dk(1) 'BC' + '1' > 'pass!'
+
+c type: 'adcd' > 'a' DK(1) 'XX'
+c type: 'adcd1' > 'pass!'
+c type: 'axcd' > 'axXX'
+c type: 'axcd1' > 'axXX1'
+'a' notany(vow) 'c' + 'd' > context(1) context(2) 'XX'
+'a' dk(1) 'XX' + '1' > 'pass!'
+
+c Test for AdjustIndex correctness
+c type: 'fghi' > 'fgXX'
+c type: 'fghi1' > 'fgXX1'
+c type: 'fdhi' > 'f' DK(1) 'XX'
+c type: 'fdhi1' > 'pass!'
+if(&platform = 'web') 'f' notany(vow) 'h' + 'i' > context(2) context(3) 'XX'
+
+c this platform matches for node-based tests ('web' doesn't!)
+if(&platform = 'native') 'f' notany(vow) 'h' + 'i' > context(2) context(3) 'XX'
+'f' dk(1) 'XX' + '1' > 'pass!'
+
+
+c Test notany issue also resolved by #3817
+c type: 'yz' > 'pass!'
+notany(vow) + 'z' > 'pass!'
diff --git a/common/core/web/tests/resources/keyboards/unit_test_keyboards.kpj b/common/core/web/tests/resources/keyboards/unit_test_keyboards.kpj
index 73278cb469..202767259c 100644
--- a/common/core/web/tests/resources/keyboards/unit_test_keyboards.kpj
+++ b/common/core/web/tests/resources/keyboards/unit_test_keyboards.kpj
@@ -4,6 +4,8 @@
False
True
+ False
+ keyboard
@@ -39,5 +41,16 @@
© 2018 SIL International
+
+ id_1bccf5634ecd6605fe682279eed62c73
+ test_917.kmn
+ test_917.kmn
+ 1.0
+ .kmn
+
+ Test #917 - notany() and context()
+ ©
+
+
diff --git a/common/core/web/tools/recorder/package-lock.json b/common/core/web/tools/recorder/package-lock.json
index 5b1167548a..06cdeede6b 100644
--- a/common/core/web/tools/recorder/package-lock.json
+++ b/common/core/web/tools/recorder/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/recorder-core",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/common/core/web/tools/recorder/package.json b/common/core/web/tools/recorder/package.json
index fa43b2a50f..bdeead0290 100644
--- a/common/core/web/tools/recorder/package.json
+++ b/common/core/web/tools/recorder/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/recorder-core",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Core classes used to develop KeymanWeb test cases based on keystrokes",
"main": "index.js",
"scripts": {
@@ -17,10 +17,10 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"dependencies": {
- "@keymanapp/keyboard-processor": "^14.0.176",
- "@keymanapp/models-types": "^14.0.176",
- "@keymanapp/web-environment": "^14.0.176",
- "@keymanapp/web-utils": "^14.0.176"
+ "@keymanapp/keyboard-processor": "^14.0.182",
+ "@keymanapp/models-types": "^14.0.182",
+ "@keymanapp/web-environment": "^14.0.182",
+ "@keymanapp/web-utils": "^14.0.182"
},
"devDependencies": {
"typescript": "^3.8.3"
diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json
index 8efe388333..3910fe01a1 100644
--- a/common/core/web/tools/sentry-manager/package-lock.json
+++ b/common/core/web/tools/sentry-manager/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-sentry-manager",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json
index 3f8c582f54..4d45469809 100644
--- a/common/core/web/tools/sentry-manager/package.json
+++ b/common/core/web/tools/sentry-manager/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-sentry-manager",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Common setup for KeymanWeb's Sentry-based error reporting",
"main": "dist/index.js",
"scripts": {
@@ -20,7 +20,7 @@
"typescript": "^3.8.3"
},
"dependencies": {
- "@keymanapp/web-environment": "^14.0.176",
+ "@keymanapp/web-environment": "^14.0.182",
"@sentry/browser": "^5.15.5"
}
}
diff --git a/common/core/web/utils/package-lock.json b/common/core/web/utils/package-lock.json
index 5412724cda..fc72d97cc2 100644
--- a/common/core/web/utils/package-lock.json
+++ b/common/core/web/utils/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-utils",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/common/core/web/utils/package.json b/common/core/web/utils/package.json
index 4a329eb053..ffb54d542c 100644
--- a/common/core/web/utils/package.json
+++ b/common/core/web/utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-utils",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Common utility functions used throughout other Keyman packages",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -20,8 +20,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
- "@keymanapp/resources-gosh": "^14.0.176",
- "@keymanapp/web-environment": "^14.0.176",
+ "@keymanapp/resources-gosh": "^14.0.182",
+ "@keymanapp/web-environment": "^14.0.182",
"@types/node": "^14.0.5",
"typescript": "^3.8.3"
}
diff --git a/common/models/templates/package-lock.json b/common/models/templates/package-lock.json
index 1f5b01f3da..76c5f83a80 100644
--- a/common/models/templates/package-lock.json
+++ b/common/models/templates/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-templates",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/common/models/templates/package.json b/common/models/templates/package.json
index 2feae9d088..3c649ca4cf 100644
--- a/common/models/templates/package.json
+++ b/common/models/templates/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-templates",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Backing model implementations (templates) for Keyman's modelling layer",
"keywords": [
"lm",
@@ -42,8 +42,8 @@
"url": "https://github.com/keymanapp/keyman/issues"
},
"devDependencies": {
- "@keymanapp/models-types": "^14.0.176",
- "@keymanapp/web-utils": "^14.0.176",
+ "@keymanapp/models-types": "^14.0.182",
+ "@keymanapp/web-utils": "^14.0.182",
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
"@types/node": "^14.0.4",
@@ -52,6 +52,6 @@
"typescript": "^3.8.3"
},
"dependencies": {
- "@keymanapp/models-wordbreakers": "^14.0.176"
+ "@keymanapp/models-wordbreakers": "^14.0.182"
}
}
diff --git a/common/models/types/package-lock.json b/common/models/types/package-lock.json
index 122e58b66f..92453a967d 100644
--- a/common/models/types/package-lock.json
+++ b/common/models/types/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-types",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/common/models/types/package.json b/common/models/types/package.json
index 1349c5342f..22d2d3bec7 100644
--- a/common/models/types/package.json
+++ b/common/models/types/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-types",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.",
"types": "./index.d.ts",
"scripts": {
diff --git a/common/models/wordbreakers/package-lock.json b/common/models/wordbreakers/package-lock.json
index cc65905565..04558ca02e 100644
--- a/common/models/wordbreakers/package-lock.json
+++ b/common/models/wordbreakers/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-wordbreakers",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/common/models/wordbreakers/package.json b/common/models/wordbreakers/package.json
index f9cf69a098..966ddd9ae6 100644
--- a/common/models/wordbreakers/package.json
+++ b/common/models/wordbreakers/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/models-wordbreakers",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Word breakers bundled in the modeling layer",
"keywords": [
"lexical model",
@@ -40,7 +40,7 @@
"url": "https://github.com/keymanapp/keyman/issues"
},
"devDependencies": {
- "@keymanapp/models-types": "^14.0.176",
+ "@keymanapp/models-types": "^14.0.182",
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
"chai": "^4.2.0",
diff --git a/common/predictive-text/package-lock.json b/common/predictive-text/package-lock.json
index 01eaa752f5..e166fda6b2 100644
--- a/common/predictive-text/package-lock.json
+++ b/common/predictive-text/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-layer",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/common/predictive-text/package.json b/common/predictive-text/package.json
index 9daa50eed6..a4fc5b4112 100644
--- a/common/predictive-text/package.json
+++ b/common/predictive-text/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-layer",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Keyman/Predictive Text integration layer",
"main": "build/index.js",
"scripts": {
@@ -21,8 +21,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
- "@keymanapp/models-types": "^14.0.176",
- "@keymanapp/resources-gosh": "^14.0.176",
+ "@keymanapp/models-types": "^14.0.182",
+ "@keymanapp/resources-gosh": "^14.0.182",
"@types/node": "^10.17.21",
"chai": "^4.2.0",
"karma": "^4.2.0",
@@ -46,10 +46,10 @@
"typescript": "^3.8.3"
},
"dependencies": {
- "@keymanapp/models-templates": "^14.0.176",
- "@keymanapp/models-wordbreakers": "^14.0.176",
- "@keymanapp/web-environment": "^14.0.176",
- "@keymanapp/web-utils": "^14.0.176",
+ "@keymanapp/models-templates": "^14.0.182",
+ "@keymanapp/models-wordbreakers": "^14.0.182",
+ "@keymanapp/web-environment": "^14.0.182",
+ "@keymanapp/web-utils": "^14.0.182",
"es6-shim": "^0.35.5",
"string.prototype.codepointat": "^0.2.1",
"string.prototype.startswith": "^0.2.0"
diff --git a/developer/js/package-lock.json b/developer/js/package-lock.json
index 083a77fe63..4c58a70370 100644
--- a/developer/js/package-lock.json
+++ b/developer/js/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-compiler",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/developer/js/package.json b/developer/js/package.json
index d87de5bd6b..9395665db0 100644
--- a/developer/js/package.json
+++ b/developer/js/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/lexical-model-compiler",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Keyman Developer lexical model compiler",
"keywords": [
"keyboard",
@@ -37,14 +37,14 @@
"node": ">=12.0.0"
},
"dependencies": {
- "@keymanapp/models-types": "^14.0.176",
+ "@keymanapp/models-types": "^14.0.182",
"commander": "^3.0.0",
"typescript": "^3.8.3",
"xml2js": "^0.4.19"
},
"devDependencies": {
- "@keymanapp/models-templates": "^14.0.176",
- "@keymanapp/models-wordbreakers": "^14.0.176",
+ "@keymanapp/models-templates": "^14.0.182",
+ "@keymanapp/models-wordbreakers": "^14.0.182",
"@types/chai": "^4.1.7",
"@types/mocha": "^5.2.7",
"@types/node": "^10.14.6",
diff --git a/lerna.json b/lerna.json
index 54db9be65c..0a2e98949c 100644
--- a/lerna.json
+++ b/lerna.json
@@ -16,5 +16,5 @@
"lerna",
"typescript"
],
- "version": "14.0.176"
+ "version": "14.0.182"
}
diff --git a/resources/gosh/package-lock.json b/resources/gosh/package-lock.json
index 3a0d2a320d..5461400719 100644
--- a/resources/gosh/package-lock.json
+++ b/resources/gosh/package-lock.json
@@ -1,5 +1,5 @@
{
"name": "@keymanapp/resources-gosh",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1
}
diff --git a/resources/gosh/package.json b/resources/gosh/package.json
index ec4f7adeba..023413547b 100644
--- a/resources/gosh/package.json
+++ b/resources/gosh/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/resources-gosh",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Good Ol' Shell",
"author": "Marc Durdin (https://github.com/mcdurdin)",
"contributors": [
diff --git a/resources/web-environment/package-lock.json b/resources/web-environment/package-lock.json
index f25b71e89d..f06053eb4e 100644
--- a/resources/web-environment/package-lock.json
+++ b/resources/web-environment/package-lock.json
@@ -1,5 +1,5 @@
{
"name": "@keymanapp/web-environment",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1
}
diff --git a/resources/web-environment/package.json b/resources/web-environment/package.json
index 4ee8e40030..4130442837 100644
--- a/resources/web-environment/package.json
+++ b/resources/web-environment/package.json
@@ -1,6 +1,6 @@
{
"name": "@keymanapp/web-environment",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "A compilation utility for passing select environment variables into our TS builds",
"author": "Joshua Horton (https://github.com/jahorton)",
"homepage": "https://github.com/keymanapp/keyman",
@@ -17,6 +17,6 @@
"url": "https://github.com/keymanapp/keyman/issues"
},
"devDependencies": {
- "@keymanapp/resources-gosh": "^14.0.176"
+ "@keymanapp/resources-gosh": "^14.0.182"
}
}
diff --git a/web/package-lock.json b/web/package-lock.json
index db7c17dc0a..169ad43706 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "keyman",
- "version": "14.0.176",
+ "version": "14.0.182",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/web/package.json b/web/package.json
index c67a91b716..ff653df3ba 100644
--- a/web/package.json
+++ b/web/package.json
@@ -1,6 +1,6 @@
{
"name": "keyman",
- "version": "14.0.176",
+ "version": "14.0.182",
"description": "Facilitates text input in any language.",
"repository": {
"type": "git",
@@ -18,8 +18,8 @@
},
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
- "@keymanapp/resources-gosh": "^14.0.176",
- "@keymanapp/web-sentry-manager": "^14.0.176",
+ "@keymanapp/resources-gosh": "^14.0.182",
+ "@keymanapp/web-sentry-manager": "^14.0.182",
"@sentry/cli": "^1.52.3",
"chai": "^4.2.0",
"google-closure-compiler-java": "^20200224.0.0",
@@ -52,13 +52,13 @@
"sentry-cli": "sentry-cli"
},
"dependencies": {
- "@keymanapp/input-processor": "^14.0.176",
- "@keymanapp/keyboard-processor": "^14.0.176",
- "@keymanapp/lexical-model-layer": "^14.0.176",
- "@keymanapp/models-types": "^14.0.176",
- "@keymanapp/recorder-core": "^14.0.176",
- "@keymanapp/web-environment": "^14.0.176",
- "@keymanapp/web-utils": "^14.0.176",
+ "@keymanapp/input-processor": "^14.0.182",
+ "@keymanapp/keyboard-processor": "^14.0.182",
+ "@keymanapp/lexical-model-layer": "^14.0.182",
+ "@keymanapp/models-types": "^14.0.182",
+ "@keymanapp/recorder-core": "^14.0.182",
+ "@keymanapp/web-environment": "^14.0.182",
+ "@keymanapp/web-utils": "^14.0.182",
"@types/node": "^11.9.4",
"es6-shim": "^0.35.5",
"eventemitter3": "^4.0.0",
diff --git a/web/source/osk/visualKeyboard.ts b/web/source/osk/visualKeyboard.ts
index 282f19f7b3..e3beefa17f 100644
--- a/web/source/osk/visualKeyboard.ts
+++ b/web/source/osk/visualKeyboard.ts
@@ -1664,11 +1664,20 @@ namespace com.keyman.osk {
var classes=key.className, cs = ' kmw-key-touched';
// For phones, use key preview rather than highlighting the key,
- // except for space, bksp, enter, shift and popup keys
- var usePreview = ((this.keytip != null)
- && (classes.indexOf('kmw-key-shift') < 0)
- && (classes.indexOf('kmw-spacebar') < 0)
- && (key.id.indexOf('popup') < 0 ));
+ var usePreview = ((this.keytip != null) && (key.id.indexOf('popup') < 0 ));
+
+ if(usePreview) {
+ // Previews are not permitted for keys using any of the following CSS styles.
+ var excludedClasses = ['kmw-key-shift', // special keys
+ 'kmw-key-shift-on', // active special keys (shift, when in shift layer
+ 'kmw-spacebar', // space
+ 'kmw-key-blank', // Keys that are only used for layout control
+ 'kmw-key-hidden'];
+
+ for(let c=0; c < excludedClasses.length; c++) {
+ usePreview = usePreview && (classes.indexOf(excludedClasses[c]) < 0);
+ }
+ }
if(usePreview) {
this.showKeyTip(key,on);
diff --git a/web/testing/index.html b/web/testing/index.html
index 8dade79279..37b21675a9 100644
--- a/web/testing/index.html
+++ b/web/testing/index.html
@@ -51,6 +51,7 @@
+
Return to main index.