From 8d74c8cc9531c9a472bf080f9bf852d0500a137c Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Fri, 2 Mar 2018 13:47:03 +0700 Subject: [PATCH] Implements basic support + testing for the 'input' event. --- web/source/kmwcallback.ts | 34 +++++++++++++++- web/source/kmwdomevents.ts | 8 ++-- web/source/kmwosk.ts | 4 +- web/unit_tests/cases/events.js | 72 ++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 7 deletions(-) diff --git a/web/source/kmwcallback.ts b/web/source/kmwcallback.ts index c1e89eddfc..774ae15c6d 100644 --- a/web/source/kmwcallback.ts +++ b/web/source/kmwcallback.ts @@ -1195,6 +1195,32 @@ class KeyboardInterface { } // I3318 - deadkey changes END + doInputEvent(_target: HTMLElement|Document) { + var event: Event; + // TypeScript doesn't yet recognize InputEvent as a type! + if(typeof window['InputEvent'] == 'function') { + event = new window['InputEvent']('input', {"bubbles": true, "cancelable": false}); + } // No else - there is no supported version in some browsers. + + // Ensure that touch-aliased elements fire as if from the aliased element. + if(_target['base'] && _target['base']['kmw_ip']) { + _target = _target['base']; + } + + if(_target && event) { + _target.dispatchEvent(event); + } + } + + defaultBackspace(Pelem?: HTMLElement|Document) { + if(!Pelem) { + Pelem = this.keymanweb.domManager.getLastActiveElement(); + } + + this.output(1, this.keymanweb.domManager.getLastActiveElement(), ""); + this.doInputEvent(Pelem); + } + /** * Function processKeystroke * Scope Private @@ -1215,7 +1241,13 @@ class KeyboardInterface { this.keymanweb.util.activeDevice = device; // Calls the start-group of the active keyboard. - return this.keymanweb.keyboardManager.activeKeyboard['gs'](element, keystroke); + var matched = this.keymanweb.keyboardManager.activeKeyboard['gs'](element, keystroke); + + if(matched) { + this.doInputEvent(element); + } + + return matched; } /** diff --git a/web/source/kmwdomevents.ts b/web/source/kmwdomevents.ts index 130cbb8a60..ee0642061a 100644 --- a/web/source/kmwdomevents.ts +++ b/web/source/kmwdomevents.ts @@ -334,10 +334,10 @@ class DOMEventHandlers { if(DOMEventHandlers.states.changed) { var event: Event; if(typeof Event == 'function') { - event = new Event("change", {"bubbles": true, "cancelable": false}); + event = new Event('change', {"bubbles": true, "cancelable": false}); } else { // IE path event = document.createEvent("HTMLEvents"); - event.initEvent("change", true, false); + event.initEvent('change', true, false); } // Ensure that touch-aliased elements fire as if from the aliased element. @@ -723,14 +723,14 @@ class DOMEventHandlers { // Support backspace in simulated input DIV from physical keyboard where not matched in rule I3363 (Build 301) if(Levent.Lcode == 8 && !LeventMatched && Levent.Ltarg.className != null && Levent.Ltarg.className.indexOf('keymanweb-input') >= 0) { - kbdInterface.output(1, DOMEventHandlers.states.lastActiveElement, ""); + this.keyman.interface.defaultBackspace(); } } else { // Mnemonic layout if(Levent.Lcode == 8) { // I1595 - Backspace for mnemonic DOMEventHandlers.states._KeyPressToSwallow = 1; if(!kbdInterface.processKeystroke(util.physicalDevice,Levent.Ltarg,Levent)) { - kbdInterface.output(1, DOMEventHandlers.states.lastActiveElement, ""); // I3363 (Build 301) + this.keyman.interface.defaultBackspace(); } return false; //added 16/3/13 to fix double backspace on mnemonic layouts on desktop } diff --git a/web/source/kmwosk.ts b/web/source/kmwosk.ts index c37c44d71e..feb35eacd4 100644 --- a/web/source/kmwosk.ts +++ b/web/source/kmwosk.ts @@ -915,7 +915,7 @@ if(!window['keyman']['initialized']) { switch(code) { case osk.keyCodes['K_BKSP']: //Only desktop UI, not touch devices. TODO: add repeat while mouse down for desktop UI - kbdInterface.output(1, keymanweb.domManager.getLastActiveElement(), ""); + this.keyman.interface.defaultBackspace(); break; case osk.keyCodes['K_TAB']: keymanweb.domManager.moveToNext(keyShiftState); @@ -973,7 +973,7 @@ if(!window['keyman']['initialized']) { // // Failed to move right - there's nothing to delete. // break; // } - // kbdInterface.output(1, keymanweb.domManager.getLastActiveElement(), ""); + // this.keyman.interface.defaultBackspace(); // } } } diff --git a/web/unit_tests/cases/events.js b/web/unit_tests/cases/events.js index 1d26321a67..5c28680101 100644 --- a/web/unit_tests/cases/events.js +++ b/web/unit_tests/cases/events.js @@ -27,6 +27,7 @@ describe('Event Management', function() { var aliasing = false; ele.onchange = function() { + ele.onchange = null; done(); } @@ -62,6 +63,7 @@ describe('Event Management', function() { var aliasing = false; ele.onchange = function() { + ele.onchange = null; done(); } @@ -88,4 +90,74 @@ describe('Event Management', function() { if(focusEvent) ele.dispatchEvent(focusEvent); }); + + it('Keystroke-based onInput event generation', function(done) { + // Not all browsers support InputEvent. Bypass the test for these. + if(typeof InputEvent != 'function') { + console.log("InputEvent not supported."); + done(); + } + + var simple_A = {"type":"key","key":"a","code":"KeyA","keyCode":65,"modifierSet":0,"location":0}; + var event = new KMWRecorder.PhysicalInputEvent(simple_A); + + var ele = document.getElementById("input"); + var aliasing = false; + + var counterObj = {i:0}; + var fin = 3; + + if(typeof InputEvent == 'function') { + ele.addEventListener("input", function() { + counterObj.i++; + if(counterObj.i == fin) { + done(); + } + }); + } + + if(ele['kmw_ip']) { + ele = ele['kmw_ip']; + aliasing = true; + } + + event.simulateEventOn(ele); + event.simulateEventOn(ele); + event.simulateEventOn(ele); + }); + + it('OSK-based onInput event generation', function(done) { + // Not all browsers support InputEvent. Bypass the test for these. + if(typeof InputEvent != 'function') { + console.log("InputEvent not supported."); + done(); + } + + var simple_A = {"type":"osk","keyID":"default-K_A"}; + var event = new KMWRecorder.OSKInputEvent(simple_A); + + var ele = document.getElementById("input"); + var aliasing = false; + + var counterObj = {i:0}; + var fin = 3; + + if(typeof InputEvent == 'function') { + ele.addEventListener("input", function() { + counterObj.i++; + if(counterObj.i == fin) { + done(); + } + }); + } + + if(ele['kmw_ip']) { + ele = ele['kmw_ip']; + aliasing = true; + } + + event.simulateEventOn(ele); + event.simulateEventOn(ele); + event.simulateEventOn(ele); + }); }); \ No newline at end of file