{
- root: HTMLInputElement;
-
- /**
- * Tracks the most recently-cached selection start index.
- */
- private _cachedSelectionStart: number
-
- /**
- * Tracks the most recently processed, extended-string-based selection start index.
- * When the element's selectionStart value changes, this should be invalidated.
- */
- private processedSelectionStart: number;
-
- /**
- * Tracks the most recently processed, extended-string-based selection end index.
- * When the element's selectionEnd value changes, this should be invalidated.
- */
- private processedSelectionEnd: number;
-
- /**
- * Set, then unset within the `forceScroll` method in order to facilitate the
- * `isForcingScroll` flag.
- */
- private _activeForcedScroll: boolean;
-
- constructor(ele: HTMLInputElement) {
- super();
-
- this.root = ele;
- this._cachedSelectionStart = -1;
- }
-
- get isSynthetic(): boolean {
- return false;
- }
-
- static isSupportedType(type: string): boolean {
- return type == 'email' || type == 'search' || type == 'text' || type == 'url';
- }
-
- getElement(): HTMLInputElement {
- return this.root;
- }
-
- clearSelection(): void {
- // Processes our codepoint-based variants of selectionStart and selectionEnd.
- this.getCaret(); // updates processedSelectionStart if required
- this.root.value = KMWString.substr(this.root.value, 0, this.processedSelectionStart) + KMWString.substr(this.root.value, this.processedSelectionEnd); //I3319
-
- this.setCaret(this.processedSelectionStart);
- }
-
- isSelectionEmpty(): boolean {
- return this.root.selectionStart == this.root.selectionEnd;
- }
-
- hasSelection(): boolean {
- return true;
- }
-
- invalidateSelection() {
- // Since .selectionStart will never return this value, we use it to indicate
- // the need to refresh our processed indices.
- this._cachedSelectionStart = -1;
- }
-
- getCaret(): number {
- if(this.root.selectionStart != this._cachedSelectionStart) {
- this._cachedSelectionStart = this.root.selectionStart; // KMW-1
- this.processedSelectionStart = KMWString.codeUnitToCodePoint(this.root.value, this.root.selectionStart); // I3319
- this.processedSelectionEnd = KMWString.codeUnitToCodePoint(this.root.value, this.root.selectionEnd); // I3319
- }
- return this.root.selectionDirection == 'forward' ? this.processedSelectionEnd : this.processedSelectionStart;
- }
-
- getDeadkeyCaret(): number {
- return this.getCaret();
- }
-
- setCaret(caret: number) {
- this.setSelection(caret, caret, "none");
- }
-
- setSelection(start: number, end: number, direction: "forward" | "backward" | "none") {
- const domStart = KMWString.codePointToCodeUnit(this.root.value, start);
- const domEnd = KMWString.codePointToCodeUnit(this.root.value, end);
- this.root.setSelectionRange(domStart, domEnd, direction);
-
- this.processedSelectionStart = start;
- this.processedSelectionEnd = end;
-
- this.forceScroll();
-
- this.root.setSelectionRange(domStart, domEnd, direction);
- }
-
- forceScroll() {
- // Only executes when com.keyman.DOMEventHandlers is defined.
- //
- // We bypass this whenever operating in the embedded format.
- const element = this.getElement();
-
- const selectionStart = element.selectionStart;
- const selectionEnd = element.selectionEnd;
-
- this._activeForcedScroll = true;
-
- try {
- //Forces scrolling; the re-focus triggers the scroll, at least.
- element.blur();
- element.focus();
- } finally {
- // On Edge, it appears that the blur/focus combination will reset the caret position
- // under certain scenarios during unit tests. So, we re-set it afterward.
- element.selectionStart = selectionStart;
- element.selectionEnd = selectionEnd;
- this._activeForcedScroll = false;
- }
- }
-
- isForcingScroll(): boolean {
- return this._activeForcedScroll;
- }
-
- getSelectionDirection(): "forward" | "backward" | "none" {
- return this.root.selectionDirection;
- }
-
- getTextBeforeCaret(): string {
- this.getCaret();
- return KMWString.substring(this.getText(), 0, this.processedSelectionStart);
- }
-
- getSelectedText(): string {
- this.getCaret();
- return KMWString.substring(this.getText(), this.processedSelectionStart, this.processedSelectionEnd);
- }
-
- setTextBeforeCaret(text: string) {
- this.getCaret();
- const selectionLength = this.processedSelectionEnd - this.processedSelectionStart;
- const direction = this.getSelectionDirection();
- const newCaret = KMWString.length(text);
- this.root.value = text + KMWString.substring(this.getText(), this.processedSelectionStart);
-
- this.setSelection(newCaret, newCaret + selectionLength, direction);
- }
-
- protected setTextAfterCaret(s: string) {
- const direction = this.getSelectionDirection();
-
- this.root.value = this.getTextBeforeCaret() + s;
- this.setSelection(this.processedSelectionStart, this.processedSelectionEnd, direction);
- }
-
- getTextAfterCaret(): string {
- this.getCaret();
- return KMWString.substring(this.getText(), this.processedSelectionEnd);
- }
-
- getText(): string {
- return this.root.value;
- }
-
- deleteCharsBeforeCaret(dn: number) {
- if(dn > 0) {
- const curText = this.getTextBeforeCaret();
- const caret = this.processedSelectionStart;
-
- if(dn > caret) {
- dn = caret;
- }
-
- this.adjustDeadkeys(-dn);
- this.setTextBeforeCaret(KMWString.substring(curText, 0, caret - dn));
- this.setCaret(caret - dn);
- }
- }
-
- insertTextBeforeCaret(s: string) {
- if(!s) {
- return;
- }
-
- const caret = this.getCaret();
- const front = this.getTextBeforeCaret();
- const back = KMWString.substring(this.getText(), this.processedSelectionStart);
-
- this.adjustDeadkeys(KMWString.length(s));
- this.root.value = front + s + back;
- this.setCaret(caret + KMWString.length(s));
- }
-
- handleNewlineAtCaret(): void {
- const inputEle = this.root;
- // Can't occur for Mocks - just Input types.
- if (inputEle && (inputEle.type == 'search' || inputEle.type == 'submit')) {
- inputEle.disabled=false;
- inputEle.form.submit();
- } else {
- this.events.emit('unhandlednewline', inputEle);
- }
- }
-
- doInputEvent() {
- this.dispatchInputEventOn(this.root);
- }
-}
\ No newline at end of file
diff --git a/web/src/engine/element-text-stores/readme.md b/web/src/engine/element-text-stores/readme.md
deleted file mode 100644
index 4e40b46178..0000000000
--- a/web/src/engine/element-text-stores/readme.md
+++ /dev/null
@@ -1,9 +0,0 @@
-## engine/element-wrappers
-
-This submodule provides a subset of the main engine's Web-oriented code that's used to 'wrap' webpage
-elements as part of KMW attachment and interface the element with the `keyboard` submodule.
-
-
-Please keep any code in this folder / namespace as free as possible from dependencies on other parts of KMW. Some of our unit tests wish to run against these types without requiring KMW to be active.
-
-Note that with a little work, we _could_ completely spin this into its own separate module - this could be useful for development and testing purposes, especially now that we've dropped the old `TouchAlias` type that was a bit entangled with main engine code.
\ No newline at end of file
diff --git a/web/src/engine/element-text-stores/textAreaElementTextStore.ts b/web/src/engine/element-text-stores/textAreaElementTextStore.ts
deleted file mode 100644
index 5418ef1c77..0000000000
--- a/web/src/engine/element-text-stores/textAreaElementTextStore.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-import { AbstractElementTextStore } from './abstractElementTextStore.js';
-import { KMWString } from 'keyman/common/web-utils';
-
-export class TextAreaElementTextStore extends AbstractElementTextStore<{}> {
- root: HTMLTextAreaElement;
-
- /**
- * Tracks the most recently-cached selection start index.
- */
- private _cachedSelectionStart: number
-
- /**
- * Tracks the most recently processed, extended-string-based selection start index.
- * When the element's selectionStart value changes, this should be invalidated.
- */
- private processedSelectionStart: number;
-
- /**
- * Tracks the most recently processed, extended-string-based selection end index.
- * When the element's selectionEnd value changes, this should be invalidated.
- */
- private processedSelectionEnd: number;
-
- /**
- * Set, then unset within the `forceScroll` method in order to facilitate the
- * `isForcingScroll` flag.
- */
- private _activeForcedScroll: boolean;
-
- constructor(ele: HTMLTextAreaElement) {
- super();
-
- this.root = ele;
- this._cachedSelectionStart = -1;
- }
-
- get isSynthetic(): boolean {
- return false;
- }
-
- getElement(): HTMLTextAreaElement {
- return this.root;
- }
-
- clearSelection(): void {
- // Processes our codepoint-based variants of selectionStart and selectionEnd.
- this.getCaret(); // updates processedSelectionStart if required
- this.root.value = KMWString.substr(this.root.value, 0, this.processedSelectionStart) + KMWString.substr(this.root.value, this.processedSelectionEnd); //I3319
-
- this.setCaret(this.processedSelectionStart);
- }
-
- isSelectionEmpty(): boolean {
- return this.root.selectionStart == this.root.selectionEnd;
- }
-
- hasSelection(): boolean {
- return true;
- }
-
- invalidateSelection() {
- // Since .selectionStart will never return this value, we use it to indicate
- // the need to refresh our processed indices.
- this._cachedSelectionStart = -1;
- }
-
- getCaret(): number {
- if(this.root.selectionStart != this._cachedSelectionStart) {
- this._cachedSelectionStart = this.root.selectionStart; // KMW-1
- this.processedSelectionStart = KMWString.codeUnitToCodePoint(this.root.value, this.root.selectionStart); // I3319
- this.processedSelectionEnd = KMWString.codeUnitToCodePoint(this.root.value, this.root.selectionEnd); // I3319
- }
- return this.root.selectionDirection == 'forward' ? this.processedSelectionEnd : this.processedSelectionStart;
- }
-
- getDeadkeyCaret(): number {
- return this.getCaret();
- }
-
- setCaret(caret: number) {
- this.setSelection(caret, caret, "none");
- }
-
- setSelection(start: number, end: number, direction: "forward" | "backward" | "none") {
- const domStart = KMWString.codePointToCodeUnit(this.root.value, start);
- const domEnd = KMWString.codePointToCodeUnit(this.root.value, end);
- this.root.setSelectionRange(domStart, domEnd, direction);
-
- this.processedSelectionStart = start;
- this.processedSelectionEnd = end;
-
- this.forceScroll();
-
- this.root.setSelectionRange(domStart, domEnd, direction);
- }
-
- forceScroll() {
- // Only executes when com.keyman.DOMEventHandlers is defined.
- //
- // We bypass this whenever operating in the embedded format.
- const element = this.getElement();
-
- const selectionStart = element.selectionStart;
- const selectionEnd = element.selectionEnd;
-
- this._activeForcedScroll = true;
-
- try {
- //Forces scrolling; the re-focus triggers the scroll, at least.
- element.blur();
- element.focus();
- } finally {
- // On Edge, it appears that the blur/focus combination will reset the caret position
- // under certain scenarios during unit tests. So, we re-set it afterward.
- element.selectionStart = selectionStart;
- element.selectionEnd = selectionEnd;
- this._activeForcedScroll = false;
- }
- }
-
- isForcingScroll(): boolean {
- return this._activeForcedScroll;
- }
-
- getSelectionDirection(): "forward" | "backward" | "none" {
- return this.root.selectionDirection;
- }
-
- getTextBeforeCaret(): string {
- this.getCaret();
- return KMWString.substring(this.getText(), 0, this.processedSelectionStart);
- }
-
- setTextBeforeCaret(text: string) {
- this.getCaret();
- const selectionLength = this.processedSelectionEnd - this.processedSelectionStart;
- const direction = this.getSelectionDirection();
- const newCaret = KMWString.length(text);
- this.root.value = text + KMWString.substring(this.getText(), this.processedSelectionStart);
-
- this.setSelection(newCaret, newCaret + selectionLength, direction);
- }
-
- protected setTextAfterCaret(s: string) {
- const direction = this.getSelectionDirection();
-
- this.root.value = this.getTextBeforeCaret() + s;
- this.setSelection(this.processedSelectionStart, this.processedSelectionEnd, direction);
- }
-
- getTextAfterCaret(): string {
- this.getCaret();
- return KMWString.substring(this.getText(), this.processedSelectionEnd);
- }
-
- getSelectedText(): string {
- this.getCaret();
- return KMWString.substring(this.getText(), this.processedSelectionStart, this.processedSelectionEnd);
- }
-
- getText(): string {
- return this.root.value;
- }
-
- deleteCharsBeforeCaret(dn: number) {
- if(dn > 0) {
- const curText = this.getTextBeforeCaret();
- const caret = this.processedSelectionStart;
-
- if(dn > caret) {
- dn = caret;
- }
-
- this.adjustDeadkeys(-dn);
- this.setTextBeforeCaret(KMWString.substr(curText, 0, caret - dn));
- this.setCaret(caret - dn);
- }
- }
-
- insertTextBeforeCaret(s: string) {
- if(!s) {
- return;
- }
-
- const caret = this.getCaret();
- const front = this.getTextBeforeCaret();
- const back = KMWString.substring(this.getText(), this.processedSelectionStart);
-
- this.adjustDeadkeys(KMWString.length(s));
- this.root.value = front + s + back;
- this.setCaret(caret + KMWString.length(s));
- }
-
- handleNewlineAtCaret(): void {
- this.insertTextBeforeCaret('\n');
- }
-
- doInputEvent() {
- this.dispatchInputEventOn(this.root);
- }
-}
\ No newline at end of file
diff --git a/web/src/engine/element-text-stores/utils.ts b/web/src/engine/element-text-stores/utils.ts
deleted file mode 100644
index 739f11a982..0000000000
--- a/web/src/engine/element-text-stores/utils.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Checks the type of an input DOM-related object while ensuring that it is checked against the correct prototype,
- * as class prototypes are (by specification) scoped upon the owning Window.
- *
- * See https://stackoverflow.com/questions/43587286/why-does-instanceof-return-false-on-chrome-safari-and-edge-and-true-on-firefox
- * for more details.
- *
- * @param {EventTarget} Pelem An element of the web page or one of its IFrame-based subdocuments.
- * @param {string} className The plain-text name of the expected Element type.
- * @return {boolean}
- */
-export function nestedInstanceOf(Pelem: EventTarget, className: string): boolean {
- let scopedClass;
-
- if(!Pelem) {
- // If we're bothering to check something's type, null references don't match
- // what we're looking for.
- return false;
- }
- // @ts-ignore
- if (Pelem['Window']) { // Window objects contain the class definitions for types held within them. So, we can check for those.
- return className == 'Window';
- // @ts-ignore
- } else if (Pelem['defaultView']) { // Covers Document.
- // @ts-ignore
- scopedClass = (Pelem as Document)['defaultView'][className];
- // @ts-ignore
- } else if(Pelem['ownerDocument']) {
- // @ts-ignore
- scopedClass = (Pelem as Node).ownerDocument.defaultView[className];
- }
-
- if(scopedClass) {
- return Pelem instanceof scopedClass;
- } else {
- return false;
- }
-}
\ No newline at end of file
diff --git a/web/src/engine/src/element-text-stores/readme.md b/web/src/engine/src/element-text-stores/readme.md
index 4e40b46178..d4a5e94da3 100644
--- a/web/src/engine/src/element-text-stores/readme.md
+++ b/web/src/engine/src/element-text-stores/readme.md
@@ -1,4 +1,4 @@
-## engine/element-wrappers
+## engine/element-text-stores
This submodule provides a subset of the main engine's Web-oriented code that's used to 'wrap' webpage
elements as part of KMW attachment and interface the element with the `keyboard` submodule.
diff --git a/web/src/engine/src/main/keymanEngineBase.ts b/web/src/engine/src/main/keymanEngineBase.ts
index 9dd31fe964..4446250eca 100644
--- a/web/src/engine/src/main/keymanEngineBase.ts
+++ b/web/src/engine/src/main/keymanEngineBase.ts
@@ -1,4 +1,4 @@
-import { type KeyEvent, JSKeyboard, Keyboard, KeyboardProperties, KeyboardKeymanGlobal, ProcessorAction } from "keyman/engine/keyboard";
+import { type KeyEvent, JSKeyboard, Keyboard, KeyboardProperties, KeyboardKeymanGlobal, ProcessorAction, KMXKeyboard } from "keyman/engine/keyboard";
import { ProcessorInitOptions } from 'keyman/engine/js-processor';
// TODO-web-core: remove alias
import { DOMKeyboardLoader as KeyboardLoader } from "keyman/engine/keyboard";
@@ -566,7 +566,7 @@ export class KeymanEngineBase<
const kbdObj = this.keyboardRequisitioner.cache.getKeyboard(k0);
if (!kbdObj) {
throw new Error(`Keyboard '${k0}' has not been loaded.`);
- } else if (!(kbdObj instanceof JSKeyboard)) {
+ } else if (kbdObj instanceof KMXKeyboard) {
return false; // TODO-web-core: implement for KMX keyboards
} else {
k0 = kbdObj;
diff --git a/web/src/test/manual/build.sh b/web/src/test/manual/build.sh
index 1d12fb56e4..34a839bdd6 100755
--- a/web/src/test/manual/build.sh
+++ b/web/src/test/manual/build.sh
@@ -24,11 +24,16 @@ builder_describe_outputs \
#### Build action definitions ####
function do_copy() {
- mkdir -p "$KEYMAN_ROOT/$DEST"
+ mkdir -p "$KEYMAN_ROOT/$DEST/keyboards"
# The next two lines are needed for the sentry-integration manual test page.
cp "$KEYMAN_ROOT/common/web/sentry-manager/build/lib/index.js" "$KEYMAN_ROOT/$DEST/sentry-manager.js"
cp "$KEYMAN_ROOT/common/web/sentry-manager/build/lib/index.js.map" "$KEYMAN_ROOT/$DEST/sentry-manager.js.map"
+
+ # copy common test (resources) keyboards
+ cp -f "$KEYMAN_ROOT/common/test/keyboards/platform-rules/platformtest.js" "$KEYMAN_ROOT/$DEST/keyboards/"
+ cp -f "$KEYMAN_ROOT/common/test/keyboards/test9469/build/test9469.js" "$KEYMAN_ROOT/$DEST/keyboards/"
+ cp -f "$KEYMAN_ROOT/common/test/resources/keyboards/"*.js "$KEYMAN_ROOT/$DEST/keyboards/"
}
builder_run_action clean rm -rf "$KEYMAN_ROOT/$DEST"
diff --git a/web/src/test/manual/web/chirality/utilities.js b/web/src/test/manual/web/chirality/utilities.js
index 158a05c08e..16c9caaf02 100644
--- a/web/src/test/manual/web/chirality/utilities.js
+++ b/web/src/test/manual/web/chirality/utilities.js
@@ -7,7 +7,7 @@ function loadKeyboards()
languages:{
id:'en',name:'English',region:'North America'
},
- filename:'chirality.js'
+ filename:'../../../../../build/test-resources/keyboards/chirality.js'
});
// A testing keyboard using 10.0 format and the KLS layout specifier
diff --git a/web/src/test/manual/web/issue9469/index.html b/web/src/test/manual/web/issue9469/index.html
index 5db68244a2..78c3a15f95 100644
--- a/web/src/test/manual/web/issue9469/index.html
+++ b/web/src/test/manual/web/issue9469/index.html
@@ -40,7 +40,8 @@
kmw.init({
attachType:'auto'
}).then(function() {
- kmw.addKeyboards({id:'test9469',name:'test9469',languages:{id:'en',name:'English'}, filename:'test9469/build/test9469.js'});
+ kmw.addKeyboards({id:'test9469',name:'test9469',languages:{id:'en',name:'English'},
+ filename:'../../../../../build/test-resources/keyboards/test9469.js'});
var pageRef = (window.location.protocol == 'file:')
? window.location.href.substr(0, window.location.href.lastIndexOf('/')+1)
diff --git a/web/src/test/manual/web/platform/index.html b/web/src/test/manual/web/platform/index.html
index c528862ead..7f007561fd 100644
--- a/web/src/test/manual/web/platform/index.html
+++ b/web/src/test/manual/web/platform/index.html
@@ -53,12 +53,12 @@
KeymanWeb Sample Page - Platform Testing
- This page is designed to test the Platform statement for consistency.
- Refer to PR #969.
- See also /windows/src/test/manual-tests/platform-rules/platform-results.xlsx.
+
This page is designed to test the Platform statement for consistency.
+ Refer to PR #969.
+ See also /common/test/keyboards/platform-rules/platform-results.xlsx.
To run the test, just press [a]. This can be pasted into the above spreadsheet.
- Be sure to reference the developer console for additional feedback.
-
+ Be sure to reference the developer console for additional feedback.
+