mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-06 08:55:34 +00:00
Merge pull request #1432 from keymanapp/web-osk-bulk-renderer
[Web] OSK bulk renderer
This commit is contained in:
commit
7d4a67caa5
12 changed files with 482 additions and 33 deletions
54
web/bulk_rendering/build.sh
Normal file
54
web/bulk_rendering/build.sh
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#! /bin/bash
|
||||
#
|
||||
# Compile the KeymanWeb bulk-renderer module for use with developing/running engine tests.
|
||||
#
|
||||
|
||||
# Fails the build if a specified file does not exist.
|
||||
assert ( ) {
|
||||
if ! [ -f $1 ]; then
|
||||
fail "Build failed."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
fail() {
|
||||
FAILURE_MSG="$1"
|
||||
if [[ "$FAILURE_MSG" == "" ]]; then
|
||||
FAILURE_MSG="Unknown failure"
|
||||
fi
|
||||
echo "${ERROR_RED}$FAILURE_MSG${NORMAL}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ensure the dependencies are downloaded. --no-optional should help block fsevents warnings.
|
||||
echo "Node.js + dependencies check"
|
||||
npm install --no-optional
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
fail "Build environment setup error detected! Please ensure Node.js is installed!"
|
||||
fi
|
||||
|
||||
# Definition of global compile constants
|
||||
COMPILED_FILE="bulk_render.js"
|
||||
OUTPUT="../release/renderer"
|
||||
NODE_SOURCE="bulk_rendering"
|
||||
#ENGINE_TEST_OUTPUT="../unit_tests/"
|
||||
|
||||
readonly OUTPUT
|
||||
readonly NODE_SOURCE
|
||||
#readonly ENGINE_TEST_OUTPUT
|
||||
|
||||
# Ensures that we rely first upon the local npm-based install of Typescript.
|
||||
# (Facilitates automated setup for build agents.)
|
||||
PATH="../../node_modules/.bin:$PATH"
|
||||
|
||||
compiler="npm run tsc --"
|
||||
compilecmd="$compiler"
|
||||
|
||||
$compilecmd -p $NODE_SOURCE/tsconfig.json
|
||||
if [ $? -ne 0 ]; then
|
||||
fail "Typescript compilation failed."
|
||||
fi
|
||||
|
||||
#cp $OUTPUT/$COMPILED_FILE $ENGINE_TEST_OUTPUT
|
||||
#cp $OUTPUT/$COMPILED_FILE.map $ENGINE_TEST_OUTPUT
|
||||
57
web/bulk_rendering/index.html
Normal file
57
web/bulk_rendering/index.html
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
|
||||
<!-- Set the viewport width to match phone and tablet device widths -->
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no" />
|
||||
|
||||
<!-- Allow KeymanWeb to be saved to the iPhone home screen -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
|
||||
<!-- Enable IE9 Standards mode -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
|
||||
<title>KeymanWeb - On-Screen Keyboard Renders</title>
|
||||
|
||||
<!-- Your page CSS -->
|
||||
<style type='text/css'>
|
||||
body {font-family: Tahoma,helvetica; margin-left: 0px}
|
||||
h3 {font-size: 1em;font-weight:normal;color: darkred; margin-bottom: 4px}
|
||||
</style>
|
||||
|
||||
<!-- Insert unminified KeymanWeb source scripts -->
|
||||
<script src="../release/unminified/web/keymanweb.js" type="application/javascript"></script>
|
||||
<script src="../release/renderer/bulk_render.js" type="application/javascript"></script>
|
||||
|
||||
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
|
||||
<script>
|
||||
var kmw=window.keyman;
|
||||
kmw.init({
|
||||
attachType:'auto'
|
||||
}).then(function() {
|
||||
var renderer_poll = function() {
|
||||
if(kmw.getKeyboards().length == 0) {
|
||||
window.setTimeout(function() {
|
||||
renderer_poll();
|
||||
}, 1000);
|
||||
} else {
|
||||
kmw_renderer.run();
|
||||
}
|
||||
}
|
||||
|
||||
renderer_poll();
|
||||
});
|
||||
kmw.addKeyboards();
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<!-- Sample page HTML -->
|
||||
<body>
|
||||
<h1>KeymanWeb - Bulk On-Screen Keyboard Rendering</h1>
|
||||
<div id='deviceNotes'></div>
|
||||
<div id='renderList'></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
242
web/bulk_rendering/renderer_core.ts
Normal file
242
web/bulk_rendering/renderer_core.ts
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// Includes KeymanWeb's Device class, as it's quite a useful resource for KMW-related projects.
|
||||
/// <reference path="../source/kmwdevice.ts" />
|
||||
// Needed for OSK rendering to image files.
|
||||
/// <reference path="../node_modules/html2canvas/dist/html2canvas.js" />
|
||||
// Ensure that Promises are within scope.
|
||||
/// <reference path="../node_modules/promise-polyfill/lib/polyfill.js" />
|
||||
|
||||
type KeyboardMap = {[id: string]: any};
|
||||
|
||||
namespace com.keyman.renderer {
|
||||
export class BatchRenderer {
|
||||
static divMaster: HTMLDivElement;
|
||||
static dummy: HTMLInputElement;
|
||||
|
||||
// Filters the keyboard array to ensure only a single entry remains, rather than an entry per language.
|
||||
private filterKeyboards(): KeyboardMap {
|
||||
let keyman = window['keyman'];
|
||||
|
||||
let kbds = keyman['getKeyboards']();
|
||||
|
||||
let keyboardMap = [];
|
||||
|
||||
for(var i = 0; i < kbds.length; i++) {
|
||||
let id: string = kbds[i]['InternalName'];
|
||||
if(keyboardMap[id]) {
|
||||
continue;
|
||||
} else {
|
||||
keyboardMap[id] = kbds[i];
|
||||
}
|
||||
}
|
||||
|
||||
return keyboardMap;
|
||||
}
|
||||
|
||||
private render(ele: HTMLElement, isMobile?: boolean): Promise<HTMLImageElement> {
|
||||
let html2canvas = window['html2canvas'];
|
||||
|
||||
let imgOut = document.createElement('img');
|
||||
|
||||
let canvasParams = {
|
||||
'logging': false,
|
||||
'scale': 1,
|
||||
'width': window.innerWidth // Good for mobile, less-so for desktop.
|
||||
}
|
||||
|
||||
// So, if it's desktop, we set more reasonable values.
|
||||
if(!isMobile) {
|
||||
canvasParams['width'] = 500;
|
||||
ele.style.width = '500px';
|
||||
}
|
||||
|
||||
return html2canvas(ele, canvasParams).then(function(canvas) {
|
||||
imgOut.src = canvas.toDataURL();
|
||||
return imgOut;
|
||||
});
|
||||
}
|
||||
|
||||
createKeyboardHeader(kbd, loaded: boolean): HTMLDivElement {
|
||||
let divHeader = document.createElement('div');
|
||||
let eleName = document.createElement('h2');
|
||||
|
||||
eleName.textContent = 'ID: ' + kbd['InternalName'];
|
||||
divHeader.appendChild(eleName);
|
||||
|
||||
let eleDescription = document.createElement('p');
|
||||
|
||||
if(loaded) {
|
||||
|
||||
eleDescription.appendChild(document.createTextNode('Name: ' + kbd['Name']));
|
||||
eleDescription.appendChild(document.createElement('br'));
|
||||
eleDescription.appendChild(document.createTextNode('Font: ' + window['keyman'].keyboardManager.activeKeyboard.KV.F));
|
||||
|
||||
} else {
|
||||
eleDescription.appendChild(document.createTextNode('Unable to load this keyboard!'));
|
||||
}
|
||||
|
||||
divHeader.appendChild(eleDescription);
|
||||
|
||||
return divHeader;
|
||||
}
|
||||
|
||||
private processKeyboard(kbd) {
|
||||
let keyman = window['keyman'];
|
||||
let p: Promise<void> = keyman.setActiveKeyboard(kbd['InternalName']);
|
||||
let isMobile = keyman.util.device.formFactor != 'desktop';
|
||||
|
||||
// Establish common keyboard header info.
|
||||
let divSummary = document.createElement('div');
|
||||
// Establishes a linkable target for this keyboard's data.
|
||||
divSummary.id = "summary-" + kbd['InternalName'];
|
||||
|
||||
BatchRenderer.divMaster.insertAdjacentElement('afterbegin', divSummary);
|
||||
|
||||
// A nice, closure-friendly reference for use in our callbacks.
|
||||
let renderer = this;
|
||||
|
||||
// Once the keyboard's loaded, we can really get started.
|
||||
return p.then(function() {
|
||||
let box: HTMLDivElement = keyman.osk._Box;
|
||||
|
||||
divSummary.appendChild(renderer.createKeyboardHeader(kbd, true));
|
||||
|
||||
let divRenders = document.createElement('div');
|
||||
divSummary.appendChild(divRenders);
|
||||
|
||||
// Uses 'private' APIs that may be subject to change in the future. Keep it updated!
|
||||
var layers;
|
||||
if(isMobile) {
|
||||
layers = keyman.osk.layers;
|
||||
} else {
|
||||
// The desktop OSK will be overpopulated, with a number of blank layers to display in most cases.
|
||||
// We instead rely upon the KLS definition to ensure we keep the renders sparse.
|
||||
layers = keyman.keyboardManager.activeKeyboard.KV.KLS;
|
||||
}
|
||||
|
||||
let renderLayer = function(i: number) {
|
||||
return new Promise(function(resolve) {
|
||||
// (Private API) Directly sets the keyboard layer within KMW, then uses .show to force-display it.
|
||||
if(isMobile) {
|
||||
keyman.osk.layerId = layers[i].id;
|
||||
} else {
|
||||
keyman.osk.layerId = Object.keys(layers)[i];
|
||||
}
|
||||
// Make sure the active element's still set!
|
||||
renderer.setActiveDummy();
|
||||
keyman.osk.show(true);
|
||||
|
||||
renderer.render(box, isMobile).then(function(imgEle: HTMLImageElement) {
|
||||
let eleLayer = document.createElement('div');
|
||||
let eleLayerId = document.createElement('p');
|
||||
eleLayerId.textContent = 'Layer ID: ' + (isMobile ? keyman.osk.layers[i].id : Object.keys(layers)[i]);
|
||||
|
||||
eleLayer.appendChild(eleLayerId);
|
||||
eleLayer.appendChild(imgEle);
|
||||
eleLayer.appendChild(document.createElement('br'));
|
||||
|
||||
divRenders.appendChild(eleLayer);
|
||||
resolve(i);
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
// The resulting Promise will only call it's `.then()` once all of this keyboard's renders have been completed.
|
||||
return renderer.arrayPromiseIteration(renderLayer, isMobile ? keyman.osk.layers.length : Object.keys(layers).length);
|
||||
}).catch(function() {
|
||||
console.log("Failed to load the \"" + kbd['InternalName'] + "\" keyboard for rendering!");
|
||||
divSummary.appendChild(renderer.createKeyboardHeader(kbd, false));
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
// Synchronously performs asynchronous operations across a loop, one at a time.
|
||||
// Necessary due to the nature of KMW OSK rendering.
|
||||
private arrayPromiseIteration(promiseGenerator: (i: number) => Promise<any>, length: number): Promise<any> {
|
||||
let iteration = function(index: number): Promise<any> {
|
||||
if(index < length) {
|
||||
var promise = promiseGenerator(index);
|
||||
return promise.then(function(index: number) {
|
||||
return iteration(++index);
|
||||
})
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
return iteration(0);
|
||||
}
|
||||
|
||||
fillDeviceNotes() {
|
||||
let description = document.createElement('p');
|
||||
let device = new com.keyman.Device();
|
||||
device.detect();
|
||||
|
||||
description.appendChild(document.createTextNode('Browser: ' + device.browser));
|
||||
description.appendChild(document.createElement('br'));
|
||||
description.appendChild(document.createTextNode('OS: ' + device.OS));
|
||||
description.appendChild(document.createElement('br'));
|
||||
description.appendChild(document.createTextNode('Form factor: ' + device.formFactor));
|
||||
description.appendChild(document.createElement('br'));
|
||||
description.appendChild(document.createTextNode('Touchable: ' + device.touchable));
|
||||
|
||||
document.getElementById('deviceNotes').appendChild(description);
|
||||
}
|
||||
|
||||
setActiveDummy() {
|
||||
com.keyman['DOMEventHandlers'].states.activeElement = BatchRenderer.dummy;
|
||||
}
|
||||
|
||||
run() {
|
||||
if(window['keyman']) {
|
||||
let keyman = window['keyman'];
|
||||
|
||||
// Establish a 'dummy' element to bypass the 'nothing's active' check KMW usualy uses.
|
||||
BatchRenderer.dummy = document.createElement('input');
|
||||
this.setActiveDummy();
|
||||
|
||||
BatchRenderer.divMaster = <HTMLDivElement> document.getElementById('renderList');
|
||||
if(BatchRenderer.divMaster.childElementCount > 0) {
|
||||
console.log("Prior bulk-renderer run detected. Terminating execution.");
|
||||
return;
|
||||
}
|
||||
|
||||
// We want the renderer to control where the keyboard is displayed.
|
||||
// Also bypasses another 'fun' OSK complication.
|
||||
if(keyman.util.device.formFactor == 'desktop') {
|
||||
keyman.osk.userPositioned = true;
|
||||
}
|
||||
|
||||
// Assumes that the keyboards have been preloaded for us.
|
||||
let kbds = this.filterKeyboards();
|
||||
|
||||
console.log("Unique keyboard ids detected: " + Object.keys(kbds).length);
|
||||
|
||||
let renderer = this;
|
||||
|
||||
let keyboardIterator = function(i) {
|
||||
return new Promise(function(resolve) {
|
||||
renderer.processKeyboard(kbds[Object.keys(kbds)[i]]).then(function () {
|
||||
//console.log("Keyboard " + i + " processed!");
|
||||
resolve(i);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.arrayPromiseIteration(keyboardIterator, Object.keys(kbds).length).then(function() {
|
||||
// Once all renders are done, we can now tidy the page up and prep it for final display + potential file-saving.
|
||||
|
||||
// This will go at the top of the page when finished, but not when actively rendering.
|
||||
// We want to leave as much space visible as possible when actively rendering keyboards
|
||||
// so that auto-scrolling isn't an issue.
|
||||
renderer.fillDeviceNotes();
|
||||
});
|
||||
} else {
|
||||
console.error("KeymanWeb not detected!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(function(){
|
||||
window['kmw_renderer'] = new com.keyman.renderer.BatchRenderer();
|
||||
})();
|
||||
}
|
||||
13
web/bulk_rendering/tsconfig.json
Normal file
13
web/bulk_rendering/tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"inlineSources": true,
|
||||
"module": "none",
|
||||
"outFile": "../release/renderer/bulk_render.js",
|
||||
"sourceMap": true,
|
||||
"target": "es5"
|
||||
},
|
||||
"files": [
|
||||
"renderer_core.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
* Add `setNumericLayer()` for embedded platforms to change OSK to numeric layer.
|
||||
* Fixes issue where file extensions are upper-case, e.g. ".TTF"
|
||||
* Fixes keyboard layout issues after mobile device rotations. (#248) (#970)
|
||||
* Adds support for Promises to init() and setActiveKeyboard(). (#100)
|
||||
|
||||
## 2018-07-06 10.0.103 stable
|
||||
* Fixes issue for embedded Android, iOS apps where a keyboard with varying row counts in different layers could crash (#1055)
|
||||
|
|
|
|||
23
web/package-lock.json
generated
23
web/package-lock.json
generated
|
|
@ -701,6 +701,15 @@
|
|||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
|
||||
"dev": true
|
||||
},
|
||||
"css-line-break": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-1.0.1.tgz",
|
||||
"integrity": "sha1-GfIGOjPpX7KDG4ZEbAuAwYivRQo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-arraybuffer": "^0.1.5"
|
||||
}
|
||||
},
|
||||
"custom-event": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
|
||||
|
|
@ -1975,6 +1984,15 @@
|
|||
"integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
|
||||
"dev": true
|
||||
},
|
||||
"html2canvas": {
|
||||
"version": "1.0.0-alpha.12",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.0.0-alpha.12.tgz",
|
||||
"integrity": "sha1-OxmS48mz9WBjw1/WIElPN+uohRM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"css-line-break": "1.0.1"
|
||||
}
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
|
||||
|
|
@ -3034,6 +3052,11 @@
|
|||
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
|
||||
"dev": true
|
||||
},
|
||||
"promise-polyfill": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.0.tgz",
|
||||
"integrity": "sha512-OzSf6gcCUQ01byV4BgwyUCswlaQQ6gzXc23aLQWhicvfX9kfsUiUhgt3CCQej8jDnl8/PhGF31JdHX2/MzF3WA=="
|
||||
},
|
||||
"qjobs": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"chai": "^4.2.0",
|
||||
"google-closure-compiler": "^20171203.0.0",
|
||||
"google-closure-library": "^20171203.0.0",
|
||||
"html2canvas": "^1.0.0-alpha.12",
|
||||
"karma": "^3.1.1",
|
||||
"karma-browserstack-launcher": "^1.3.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
|
|
@ -44,5 +45,8 @@
|
|||
"test": "bash ./unit_tests/test.sh",
|
||||
"karma": "karma",
|
||||
"modernizr": "modernizr"
|
||||
},
|
||||
"dependencies": {
|
||||
"promise-polyfill": "^8.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// Includes KMW-added property declaration extensions for HTML elements.
|
||||
/// <reference path="kmwexthtml.ts" />
|
||||
// Includes a promise polyfill (needed for IE)
|
||||
/// <reference path="../node_modules/promise-polyfill/lib/polyfill.js" />
|
||||
// Defines the web-page interface object.
|
||||
/// <reference path="kmwdom.ts" />
|
||||
// Includes KMW-added property declaration extensions for HTML elements.
|
||||
|
|
@ -283,8 +285,8 @@ namespace com.keyman {
|
|||
* @param {string} PInternalName Internal name
|
||||
* @param {string} PLgCode Language code
|
||||
*/
|
||||
['setActiveKeyboard'](PInternalName: string, PLgCode: string) {
|
||||
this.keyboardManager.setActiveKeyboard(PInternalName,PLgCode);
|
||||
['setActiveKeyboard'](PInternalName: string, PLgCode: string): Promise<void> {
|
||||
return this.keyboardManager.setActiveKeyboard(PInternalName,PLgCode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -391,8 +393,8 @@ namespace com.keyman {
|
|||
* @param {Object} arg object array of user-defined properties
|
||||
* Description KMW window initialization
|
||||
*/
|
||||
['init'](arg) {
|
||||
this.domManager.init(arg);
|
||||
['init'](arg): Promise<any> {
|
||||
return this.domManager.init(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1467,7 +1467,7 @@ namespace com.keyman {
|
|||
* @param {Object} arg object array of user-defined properties
|
||||
* Description KMW window initialization
|
||||
*/
|
||||
init(arg) {
|
||||
init: (arg:any) => Promise<any> = function(arg): Promise<any> {
|
||||
var i,j,c,e,p,eTextArea,eInput,opt,dTrailer,ds;
|
||||
var osk = this.keyman.osk;
|
||||
var util = this.keyman.util;
|
||||
|
|
@ -1496,7 +1496,7 @@ namespace com.keyman {
|
|||
|
||||
// Otherwise, assume relative to source path
|
||||
return this.keyman.srcPath+p;
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
// Explicit (user-defined) parameter initialization
|
||||
opt=this.keyman.options;
|
||||
|
|
@ -1537,7 +1537,7 @@ namespace com.keyman {
|
|||
|
||||
// Only do remainder of initialization once!
|
||||
if(this.keyman.initialized) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var keyman: KeymanBase = this.keyman;
|
||||
|
|
@ -1546,10 +1546,13 @@ namespace com.keyman {
|
|||
// Do not initialize until the document has been fully loaded
|
||||
if(document.readyState !== 'complete')
|
||||
{
|
||||
window.setTimeout(function(){
|
||||
domManager.init(arg);
|
||||
}, 50);
|
||||
return;
|
||||
return new Promise(function(resolve) {
|
||||
window.setTimeout(function(){
|
||||
domManager.init(arg).then(function() {
|
||||
resolve();
|
||||
});
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
|
||||
this.keyman._MasterDocument = window.document;
|
||||
|
|
@ -1592,7 +1595,7 @@ namespace com.keyman {
|
|||
if(!this.keyman.keyboardManager.setDefaultKeyboard()) {
|
||||
console.error("No keyboard stubs exist - cannot initialize keyboard!");
|
||||
}
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Determine the default font for mapped elements
|
||||
|
|
@ -1716,7 +1719,8 @@ namespace com.keyman {
|
|||
|
||||
// Set exposed initialization flag to 2 to indicate deferred initialization also complete
|
||||
this.keyman.setInitialized(2);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}.bind(this);
|
||||
|
||||
/**
|
||||
* Initialize the desktop user interface as soon as it is ready
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ namespace com.keyman {
|
|||
'KOskFont': KeyboardFont;
|
||||
|
||||
// Used when loading a stub's keyboard.
|
||||
asyncLoader: any;
|
||||
asyncLoader?: any;
|
||||
|
||||
constructor(id: string, langCode: string) {
|
||||
this['KI'] = 'Keyboard_' + id;
|
||||
|
|
@ -305,7 +305,7 @@ namespace com.keyman {
|
|||
|
||||
// Fixed OSK font issue Github #7 (9/1/2015)
|
||||
if(typeof(lp['oskFont']) != 'undefined') {
|
||||
sp['KOskFont'] = (typeof sp['KOskFont'] === 'undefined') ? new KeyboardFont(lp['oskfont'], fontPath) : sp['KOskFont'];
|
||||
sp['KOskFont'] = (typeof sp['KOskFont'] === 'undefined') ? new KeyboardFont(lp['oskFont'], fontPath) : sp['KOskFont'];
|
||||
}
|
||||
|
||||
// Update the UI
|
||||
|
|
@ -357,7 +357,7 @@ namespace com.keyman {
|
|||
* @param {string} PInternalName Internal name
|
||||
* @param {string} PLgCode Language code
|
||||
*/
|
||||
setActiveKeyboard(PInternalName: string, PLgCode: string) {
|
||||
setActiveKeyboard(PInternalName: string, PLgCode: string): Promise<void> {
|
||||
//TODO: This does not make sense: the callbacks should be in _SetActiveKeyboard, not here,
|
||||
// since this is always called FROM the UI, which should not need notification.
|
||||
// If UI callbacks are needed at all, they should be within _SetActiveKeyboard
|
||||
|
|
@ -368,7 +368,7 @@ namespace com.keyman {
|
|||
}
|
||||
|
||||
this.doBeforeKeyboardChange(PInternalName,PLgCode);
|
||||
this._SetActiveKeyboard(PInternalName,PLgCode,true);
|
||||
let p = this._SetActiveKeyboard(PInternalName,PLgCode,true);
|
||||
if(this.keymanweb.domManager.getLastActiveElement() != null) {
|
||||
this.keymanweb.domManager.focusLastActiveElement(); // TODO: Resolve without need for the cast.
|
||||
}
|
||||
|
|
@ -378,6 +378,8 @@ namespace com.keyman {
|
|||
// PLgCode = (<KeymanBase>keymanweb).keyboardManager.activeStub['KLC'];
|
||||
// }
|
||||
this.doKeyboardChange(PInternalName, PLgCode);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -395,7 +397,7 @@ namespace com.keyman {
|
|||
* @param {string=} PLgCode
|
||||
* @param {boolean=} saveCookie
|
||||
*/
|
||||
_SetActiveKeyboard(PInternalName: string, PLgCode?: string, saveCookie?: boolean) {
|
||||
_SetActiveKeyboard(PInternalName: string, PLgCode?: string, saveCookie?: boolean): Promise<void> {
|
||||
var n, Ln;
|
||||
|
||||
var util = this.keymanweb.util;
|
||||
|
|
@ -430,7 +432,7 @@ namespace com.keyman {
|
|||
if(this.activeStub && this.activeKeyboard && this.activeKeyboard['KI'] == PInternalName
|
||||
&& this.activeStub['KI'] == PInternalName //this part of test should not be necessary, but keep anyway
|
||||
&& this.activeStub['KLC'] == PLgCode && !this.keymanweb.mustReloadKeyboard
|
||||
) return;
|
||||
) return Promise.resolve();
|
||||
|
||||
// Check if current keyboard matches requested keyboard, but not stub
|
||||
if(this.activeKeyboard && (this.activeKeyboard['KI'] == PInternalName)) {
|
||||
|
|
@ -448,7 +450,7 @@ namespace com.keyman {
|
|||
if(this.keymanweb.mustReloadKeyboard) {
|
||||
osk._Load();
|
||||
}
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -464,7 +466,7 @@ namespace com.keyman {
|
|||
util.wait(false);
|
||||
}
|
||||
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Determine if the keyboard was previously loaded but is not active and use the prior load if so.
|
||||
|
|
@ -552,12 +554,14 @@ namespace com.keyman {
|
|||
// It works much more reliably if deferred (KMEW-101, build 356)
|
||||
// The effect of a delay can also be tested, for example, by setting the timeout to 5000
|
||||
var manager = this;
|
||||
window.setTimeout(function(){
|
||||
manager.installKeyboard(loadingStub);
|
||||
},0);
|
||||
}
|
||||
loadingStub.asyncLoader.promise = new Promise(function(resolve, reject) {
|
||||
window.setTimeout(function(){
|
||||
manager.installKeyboard(resolve, reject, loadingStub);
|
||||
},0);
|
||||
});
|
||||
}
|
||||
this.activeStub=this.keyboardStubs[Ln];
|
||||
return;
|
||||
return this.keyboardStubs[Ln].asyncLoader.promise;
|
||||
}
|
||||
}
|
||||
this.keymanweb.domManager._SetTargDir(this.keymanweb.domManager.getLastActiveElement()); // I2077 - LTR/RTL timing
|
||||
|
|
@ -569,15 +573,17 @@ namespace com.keyman {
|
|||
|
||||
// Initialize the OSK (provided that the base code has been loaded)
|
||||
osk._Load();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Install a keyboard script that has been downloaded from a keyboard server
|
||||
* Operates as the core of a Promise, hence the 'resolve' and 'reject' parameters.
|
||||
*
|
||||
* @param {Object} kbdStub keyboard stub to be loaded.
|
||||
*
|
||||
**/
|
||||
installKeyboard(kbdStub: KeyboardStub) {
|
||||
installKeyboard(resolve: () => void, reject: () => void, kbdStub: KeyboardStub) {
|
||||
var util = this.keymanweb.util;
|
||||
var osk = this.keymanweb.osk;
|
||||
|
||||
|
|
@ -607,6 +613,8 @@ namespace com.keyman {
|
|||
// We already know the load has failed... why wait?
|
||||
kbdStub.asyncLoader.callback('Cannot find the ' + kbdName + ' keyboard for ' + kbdLang + '.', 'warn');
|
||||
kbdStub.asyncLoader = null;
|
||||
|
||||
reject();
|
||||
}, false);
|
||||
|
||||
|
||||
|
|
@ -644,23 +652,31 @@ namespace com.keyman {
|
|||
if(!manager.keymanweb.isEmbedded) {
|
||||
util.wait(false);
|
||||
}
|
||||
|
||||
kbdStub.asyncLoader = null;
|
||||
resolve();
|
||||
// A handler portion for cases where the new <script> block loads, but fails to process.
|
||||
} else { // Output error messages even when embedded - they're useful when debugging the apps and KMEA/KMEI engines.
|
||||
kbdStub.asyncLoader.callback('Error registering the ' + kbdName + ' keyboard for ' + kbdLang + '.', 'error');
|
||||
kbdStub.asyncLoader = null;
|
||||
reject();
|
||||
}
|
||||
kbdStub.asyncLoader = null;
|
||||
}, false);
|
||||
|
||||
// IE likes to instantly start loading the file when assigned to an element, so we do this after the rest
|
||||
// of our setup. This method is not relocated here (yet) b/c it varies based upon 'native' vs 'embedded'.
|
||||
Lscript.src = this.keymanweb.getKeyboardPath(kbdFile);
|
||||
|
||||
try {
|
||||
try {
|
||||
document.body.appendChild(Lscript);
|
||||
this.linkedScripts.push(Lscript);
|
||||
}
|
||||
catch(ex) {
|
||||
document.getElementsByTagName('head')[0].appendChild(Lscript);
|
||||
catch(ex) {
|
||||
try {
|
||||
document.getElementsByTagName('head')[0].appendChild(Lscript);
|
||||
} catch(ex2) {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4097,7 +4097,20 @@ if(!window['keyman']['initialized']) {
|
|||
var Ls = osk._Box.style;
|
||||
|
||||
// Do not display OSK until it has been positioned correctly
|
||||
if(device.touchable && Ls.bottom == '') Ls.visibility='hidden';
|
||||
if(device.touchable && Ls.bottom == '') {
|
||||
Ls.visibility='hidden';
|
||||
}
|
||||
|
||||
if(device.touchable) {
|
||||
/* In case it's still '0' from a hide() operation.
|
||||
* Happens when _Show is called before the transitionend events are processed,
|
||||
* which can happen in bulk-rendering contexts.
|
||||
*
|
||||
* (Opacity is only modified when device.touchable = true, though a couple of extra
|
||||
* conditions may apply.)
|
||||
*/
|
||||
Ls.opacity='1';
|
||||
}
|
||||
|
||||
// The following code will always be executed except for externally created OSK such as EuroLatin
|
||||
if(osk.ddOSK)
|
||||
|
|
|
|||
20
web/testing/issue382/issue382.kpj
Normal file
20
web/testing/issue382/issue382.kpj
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<KeymanDeveloperProject>
|
||||
<Options>
|
||||
<BuildPath></BuildPath>
|
||||
<CompilerWarningsAsErrors>False</CompilerWarningsAsErrors>
|
||||
<WarnDeprecatedCode>True</WarnDeprecatedCode>
|
||||
</Options>
|
||||
<Files>
|
||||
<File>
|
||||
<ID>id_d6d0aefd0ac869e89e3e93e50e54f856</ID>
|
||||
<Filename>issue382.kmn</Filename>
|
||||
<Filepath>issue382.kmn</Filepath>
|
||||
<FileVersion>1.0</FileVersion>
|
||||
<FileType>.kmn</FileType>
|
||||
<Details>
|
||||
<Name>Keycap Scaling Test</Name>
|
||||
</Details>
|
||||
</File>
|
||||
</Files>
|
||||
</KeymanDeveloperProject>
|
||||
Loading…
Add table
Reference in a new issue