mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-21 15:17:40 +00:00
Merge pull request #10274 from keymanapp/feat/web/webview-es6-target-artifact
feat(web): es6 artifact for app/webview 📜
This commit is contained in:
commit
87acd6a2cf
11 changed files with 213 additions and 152 deletions
2
android/.gitignore
vendored
2
android/.gitignore
vendored
|
|
@ -37,6 +37,8 @@ KMEA/**/assets/keymanandroid.js
|
|||
KMEA/**/assets/keyman.js.map
|
||||
KMEA/**/assets/keymanweb-webview.js
|
||||
KMEA/**/assets/keymanweb-webview.js.map
|
||||
KMEA/**/assets/keymanweb-webview.es5.js
|
||||
KMEA/**/assets/keymanweb-webview.es5.js.map
|
||||
KMEA/**/assets/map-polyfill.js
|
||||
KMEA/**/assets/sentry.min.js
|
||||
KMEA/**/assets/keyman-sentry.js
|
||||
|
|
|
|||
30
android/KMEA/app/src/main/assets/keyboard.es5.html
Normal file
30
android/KMEA/app/src/main/assets/keyboard.es5.html
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Copyright (C) 2017-2019 SIL International. All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<title>Keyman</title>
|
||||
<!-- Provides polyfills needed by un-updated Chromium versions found
|
||||
on older, but still-supported, Android APIs.
|
||||
|
||||
If and when our minimum supported Android version shipped with
|
||||
Chromium version 54 or greater, we should be able to drop this
|
||||
polyfill.
|
||||
-->
|
||||
<script src="es6-shim.min.js"></script>
|
||||
<script src="other-polyfills.js"></script>
|
||||
<script src="map-polyfill.js"></script>
|
||||
<script src="keymanweb-webview.es5.js"></script>
|
||||
<script src="sentry.min.js"></script>
|
||||
<script src="keyman-sentry.js"></script>
|
||||
<script src="android-host.js"></script>
|
||||
<style type="text/css">
|
||||
body {background-color:#000000;}
|
||||
</style>
|
||||
</head>
|
||||
<body class="kmw-embedded keyman-app">
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -7,16 +7,6 @@
|
|||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<title>Keyman</title>
|
||||
<!-- Provides polyfills needed by un-updated Chromium versions found
|
||||
on older, but still-supported, Android APIs.
|
||||
|
||||
If and when our minimum supported Android version shipped with
|
||||
Chromium version 54 or greater, we should be able to drop this
|
||||
polyfill.
|
||||
-->
|
||||
<script src="es6-shim.min.js"></script>
|
||||
<script src="other-polyfills.js"></script>
|
||||
<script src="map-polyfill.js"></script>
|
||||
<script src="keymanweb-webview.js"></script>
|
||||
<script src="sentry.min.js"></script>
|
||||
<script src="keyman-sentry.js"></script>
|
||||
|
|
|
|||
|
|
@ -288,8 +288,11 @@ public final class KMManager {
|
|||
|
||||
// Keyman files
|
||||
protected static final String KMFilename_KeyboardHtml = "keyboard.html";
|
||||
protected static final String KMFilename_KeyboardHtml_Legacy = "keyboard.es5.html";
|
||||
protected static final String KMFilename_JSEngine = "keymanweb-webview.js";
|
||||
protected static final String KMFilename_JSEngine_Sourcemap = "keymanweb-webview.js.map";
|
||||
protected static final String KMFilename_JSLegacyEngine = "keymanweb-webview.es5.js";
|
||||
protected static final String KMFilename_JSLegacyEngine_Sourcemap = "keymanweb-webview.es5.js.map";
|
||||
protected static final String KMFilename_JSSentry = "sentry.min.js";
|
||||
protected static final String KMFilename_JSSentryInit = "keyman-sentry.js";
|
||||
protected static final String KMFilename_AndroidHost = "android-host.js";
|
||||
|
|
@ -849,25 +852,47 @@ public final class KMManager {
|
|||
|
||||
private static void copyAssets(Context context) {
|
||||
AssetManager assetManager = context.getAssets();
|
||||
|
||||
// Will build a temp WebView in order to check Chrome version internally.
|
||||
boolean legacyMode = WebViewUtils.getEngineWebViewVersionStatus(context, null, null) != WebViewUtils.EngineWebViewVersionStatus.FULL;
|
||||
|
||||
try {
|
||||
// Copy KMW files
|
||||
copyAsset(context, KMFilename_KeyboardHtml, "", true);
|
||||
copyAsset(context, KMFilename_JSEngine, "", true);
|
||||
if(legacyMode) {
|
||||
// Replaces the standard ES6-friendly version of the host page with a legacy one that
|
||||
// includes polyfill requests and that links the legacy, ES5-compatible version of KMW.
|
||||
copyAssetWithRename(context, KMFilename_KeyboardHtml_Legacy, KMFilename_KeyboardHtml, "", true);
|
||||
|
||||
copyAsset(context, KMFilename_JSLegacyEngine, "", true);
|
||||
if (KMManager.isDebugMode()) {
|
||||
copyAsset(context, KMFilename_JSLegacyEngine_Sourcemap, "", true);
|
||||
}
|
||||
} else {
|
||||
copyAsset(context, KMFilename_KeyboardHtml, "", true);
|
||||
|
||||
// For versions of Chrome with full ES6 support, we use the ES6 artifact.
|
||||
copyAsset(context, KMFilename_JSEngine, "", true);
|
||||
if (KMManager.isDebugMode()) {
|
||||
copyAsset(context, KMFilename_JSEngine_Sourcemap, "", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Is still built targeting ES5.
|
||||
copyAsset(context, KMFilename_JSSentry, "", true);
|
||||
copyAsset(context, KMFilename_JSSentryInit, "", true);
|
||||
copyAsset(context, KMFilename_AndroidHost, "", true);
|
||||
if(KMManager.isDebugMode()) {
|
||||
copyAsset(context, KMFilename_JSEngine_Sourcemap, "", true);
|
||||
}
|
||||
copyAsset(context, KMFilename_KmwCss, "", true);
|
||||
copyAsset(context, KMFilename_KmwGlobeHintCss, "", true);
|
||||
copyAsset(context, KMFilename_Osk_Ttf_Font, "", true);
|
||||
|
||||
// Copy default keyboard font
|
||||
copyAsset(context, KMDefault_KeyboardFont, "", true);
|
||||
copyAsset(context, KMFilename_JSPolyfill, "", true);
|
||||
copyAsset(context, KMFilename_JSPolyfill2, "", true);
|
||||
copyAsset(context, KMFilename_JSPolyfill3, "", true);
|
||||
|
||||
if(legacyMode) {
|
||||
copyAsset(context, KMFilename_JSPolyfill, "", true);
|
||||
copyAsset(context, KMFilename_JSPolyfill2, "", true);
|
||||
copyAsset(context, KMFilename_JSPolyfill3, "", true);
|
||||
}
|
||||
|
||||
// Keyboard packages directory
|
||||
File packagesDir = new File(getPackagesDir());
|
||||
|
|
@ -963,6 +988,10 @@ public final class KMManager {
|
|||
}
|
||||
|
||||
private static int copyAsset(Context context, String filename, String directory, boolean overwrite) {
|
||||
return copyAssetWithRename(context, filename, filename, directory, overwrite);
|
||||
}
|
||||
|
||||
private static int copyAssetWithRename(Context context, String srcName, String destName, String directory, boolean overwrite) {
|
||||
int result;
|
||||
AssetManager assetManager = context.getAssets();
|
||||
try {
|
||||
|
|
@ -979,9 +1008,9 @@ public final class KMManager {
|
|||
dirPath = getResourceRoot();
|
||||
}
|
||||
|
||||
File file = new File(dirPath, filename);
|
||||
File file = new File(dirPath, destName);
|
||||
if (!file.exists() || overwrite) {
|
||||
InputStream inputStream = assetManager.open(directory + filename);
|
||||
InputStream inputStream = assetManager.open(directory + srcName);
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
FileUtils.copy(inputStream, outputStream);
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ if builder_start_action build:engine; then
|
|||
echo "Copying Keyman Web artifacts"
|
||||
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/keymanweb-webview.js" "$ENGINE_ASSETS/keymanweb-webview.js"
|
||||
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/keymanweb-webview.js.map" "$ENGINE_ASSETS/keymanweb-webview.js.map"
|
||||
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/keymanweb-webview.es5.js" "$ENGINE_ASSETS/keymanweb-webview.es5.js"
|
||||
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/keymanweb-webview.es5.js.map" "$ENGINE_ASSETS/keymanweb-webview.es5.js.map"
|
||||
cp "$KEYMAN_WEB_ROOT/build/app/webview/$CONFIG/map-polyfill.js" "$ENGINE_ASSETS/map-polyfill.js"
|
||||
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/ajax-loader.gif" "$ENGINE_ASSETS/ajax-loader.gif"
|
||||
cp "$KEYMAN_WEB_ROOT/build/app/resources/osk/kmwosk.css" "$ENGINE_ASSETS/kmwosk.css"
|
||||
|
|
|
|||
|
|
@ -1,22 +1,8 @@
|
|||
import {
|
||||
export {
|
||||
SENTINEL_CODE_UNIT, applyTransform, buildMergedTransform, isHighSurrogate, isLowSurrogate, isSentinel,
|
||||
transformToSuggestion, defaultApplyCasing
|
||||
} from "./common.js";
|
||||
import PriorityQueue, { Comparator } from "./priority-queue.js";
|
||||
import QuoteBehavior from "./quote-behavior.js";
|
||||
import { Tokenization, tokenize, getLastPreCaretToken, wordbreak } from "./tokenization.js";
|
||||
import TrieModel, { TrieModelOptions } from "./trie-model.js";
|
||||
|
||||
import { extendString } from "@keymanapp/web-utils";
|
||||
|
||||
// This package requires our string-extension functions.
|
||||
extendString();
|
||||
|
||||
export {
|
||||
SENTINEL_CODE_UNIT, applyTransform, buildMergedTransform, isHighSurrogate, isLowSurrogate, isSentinel,
|
||||
transformToSuggestion, defaultApplyCasing, // "common.ts"
|
||||
PriorityQueue, Comparator, // "priority-queue.ts"
|
||||
QuoteBehavior, // "quote-behavior.ts",
|
||||
Tokenization, tokenize, getLastPreCaretToken, wordbreak, // "tokenization.ts"
|
||||
TrieModel, TrieModelOptions // "trie-model.ts"
|
||||
};
|
||||
export { default as PriorityQueue, Comparator } from "./priority-queue.js";
|
||||
export { default as QuoteBehavior } from "./quote-behavior.js";
|
||||
export { Tokenization, tokenize, getLastPreCaretToken, wordbreak } from "./tokenization.js";
|
||||
export { default as TrieModel, TrieModelOptions } from "./trie-model.js";
|
||||
|
|
@ -33,6 +33,8 @@ import { applyTransform, isHighSurrogate, isSentinel, SENTINEL_CODE_UNIT, transf
|
|||
import { getLastPreCaretToken } from "./tokenization.js";
|
||||
import PriorityQueue from "./priority-queue.js";
|
||||
|
||||
extendString();
|
||||
|
||||
/**
|
||||
* @file trie-model.ts
|
||||
*
|
||||
|
|
@ -81,6 +83,114 @@ type TextWithProbability = {
|
|||
p: number; // real-number weight, from 0 to 1
|
||||
}
|
||||
|
||||
class Traversal implements LexiconTraversal {
|
||||
/**
|
||||
* The lexical prefix corresponding to the current traversal state.
|
||||
*/
|
||||
prefix: String;
|
||||
|
||||
/**
|
||||
* The current traversal node. Serves as the 'root' of its own sub-Trie,
|
||||
* and we cannot navigate back to its parent.
|
||||
*/
|
||||
root: Node;
|
||||
|
||||
constructor(root: Node, prefix: string) {
|
||||
this.root = root;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
*children(): Generator<{char: string, traversal: () => LexiconTraversal}> {
|
||||
let root = this.root;
|
||||
|
||||
if(root.type == 'internal') {
|
||||
for(let entry of root.values) {
|
||||
let entryNode = root.children[entry];
|
||||
|
||||
// UTF-16 astral plane check.
|
||||
if(isHighSurrogate(entry)) {
|
||||
// First code unit of a UTF-16 code point.
|
||||
// For now, we'll just assume the second always completes such a char.
|
||||
//
|
||||
// Note: Things get nasty here if this is only sometimes true; in the future,
|
||||
// we should compile-time enforce that this assumption is always true if possible.
|
||||
if(entryNode.type == 'internal') {
|
||||
let internalNode = entryNode;
|
||||
for(let lowSurrogate of internalNode.values) {
|
||||
let prefix = this.prefix + entry + lowSurrogate;
|
||||
yield {
|
||||
char: entry + lowSurrogate,
|
||||
traversal: function() { return new Traversal(internalNode.children[lowSurrogate], prefix) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Determine how much of the 'leaf' entry has no Trie nodes, emulate them.
|
||||
let fullText = entryNode.entries[0].key;
|
||||
entry = entry + fullText[this.prefix.length + 1]; // The other half of the non-BMP char.
|
||||
let prefix = this.prefix + entry;
|
||||
|
||||
yield {
|
||||
char: entry,
|
||||
traversal: function () {return new Traversal(entryNode, prefix)}
|
||||
}
|
||||
}
|
||||
} else if(isSentinel(entry)) {
|
||||
continue;
|
||||
} else if(!entry) {
|
||||
// Prevent any accidental 'null' or 'undefined' entries from having an effect.
|
||||
continue;
|
||||
} else {
|
||||
let prefix = this.prefix + entry;
|
||||
yield {
|
||||
char: entry,
|
||||
traversal: function() { return new Traversal(entryNode, prefix)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
} else { // type == 'leaf'
|
||||
let prefix = this.prefix;
|
||||
|
||||
let children = root.entries.filter(function(entry) {
|
||||
return entry.key != prefix && prefix.length < entry.key.length;
|
||||
})
|
||||
|
||||
for(let {key} of children) {
|
||||
let nodeKey = key[prefix.length];
|
||||
|
||||
if(isHighSurrogate(nodeKey)) {
|
||||
// Merge the other half of an SMP char in!
|
||||
nodeKey = nodeKey + key[prefix.length+1];
|
||||
}
|
||||
yield {
|
||||
char: nodeKey,
|
||||
traversal: function() { return new Traversal(root, prefix + nodeKey)}
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
get entries(): string[] {
|
||||
if(this.root.type == 'leaf') {
|
||||
let prefix = this.prefix;
|
||||
let matches = this.root.entries.filter(function(entry) {
|
||||
return entry.key == prefix;
|
||||
});
|
||||
|
||||
return matches.map(function(value) { return value.content });
|
||||
} else {
|
||||
let matchingLeaf = this.root.children[SENTINEL_CODE_UNIT];
|
||||
if(matchingLeaf && matchingLeaf.type == 'leaf') {
|
||||
return matchingLeaf.entries.map(function(value) { return value.content });
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class TrieModel
|
||||
*
|
||||
|
|
@ -176,115 +286,7 @@ export default class TrieModel implements LexicalModel {
|
|||
}
|
||||
|
||||
public traverseFromRoot(): LexiconTraversal {
|
||||
return new TrieModel.Traversal(this._trie['root'], '');
|
||||
}
|
||||
|
||||
private static Traversal = class implements LexiconTraversal {
|
||||
/**
|
||||
* The lexical prefix corresponding to the current traversal state.
|
||||
*/
|
||||
prefix: String;
|
||||
|
||||
/**
|
||||
* The current traversal node. Serves as the 'root' of its own sub-Trie,
|
||||
* and we cannot navigate back to its parent.
|
||||
*/
|
||||
root: Node;
|
||||
|
||||
constructor(root: Node, prefix: string) {
|
||||
this.root = root;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
*children(): Generator<{char: string, traversal: () => LexiconTraversal}> {
|
||||
let root = this.root;
|
||||
|
||||
if(root.type == 'internal') {
|
||||
for(let entry of root.values) {
|
||||
let entryNode = root.children[entry];
|
||||
|
||||
// UTF-16 astral plane check.
|
||||
if(isHighSurrogate(entry)) {
|
||||
// First code unit of a UTF-16 code point.
|
||||
// For now, we'll just assume the second always completes such a char.
|
||||
//
|
||||
// Note: Things get nasty here if this is only sometimes true; in the future,
|
||||
// we should compile-time enforce that this assumption is always true if possible.
|
||||
if(entryNode.type == 'internal') {
|
||||
let internalNode = entryNode;
|
||||
for(let lowSurrogate of internalNode.values) {
|
||||
let prefix = this.prefix + entry + lowSurrogate;
|
||||
yield {
|
||||
char: entry + lowSurrogate,
|
||||
traversal: function() { return new TrieModel.Traversal(internalNode.children[lowSurrogate], prefix) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Determine how much of the 'leaf' entry has no Trie nodes, emulate them.
|
||||
let fullText = entryNode.entries[0].key;
|
||||
entry = entry + fullText[this.prefix.length + 1]; // The other half of the non-BMP char.
|
||||
let prefix = this.prefix + entry;
|
||||
|
||||
yield {
|
||||
char: entry,
|
||||
traversal: function () {return new TrieModel.Traversal(entryNode, prefix)}
|
||||
}
|
||||
}
|
||||
} else if(isSentinel(entry)) {
|
||||
continue;
|
||||
} else if(!entry) {
|
||||
// Prevent any accidental 'null' or 'undefined' entries from having an effect.
|
||||
continue;
|
||||
} else {
|
||||
let prefix = this.prefix + entry;
|
||||
yield {
|
||||
char: entry,
|
||||
traversal: function() { return new TrieModel.Traversal(entryNode, prefix)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
} else { // type == 'leaf'
|
||||
let prefix = this.prefix;
|
||||
|
||||
let children = root.entries.filter(function(entry) {
|
||||
return entry.key != prefix && prefix.length < entry.key.length;
|
||||
})
|
||||
|
||||
for(let {key} of children) {
|
||||
let nodeKey = key[prefix.length];
|
||||
|
||||
if(isHighSurrogate(nodeKey)) {
|
||||
// Merge the other half of an SMP char in!
|
||||
nodeKey = nodeKey + key[prefix.length+1];
|
||||
}
|
||||
yield {
|
||||
char: nodeKey,
|
||||
traversal: function() { return new TrieModel.Traversal(root, prefix + nodeKey)}
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
get entries(): string[] {
|
||||
if(this.root.type == 'leaf') {
|
||||
let prefix = this.prefix;
|
||||
let matches = this.root.entries.filter(function(entry) {
|
||||
return entry.key == prefix;
|
||||
});
|
||||
|
||||
return matches.map(function(value) { return value.content });
|
||||
} else {
|
||||
let matchingLeaf = this.root.children[SENTINEL_CODE_UNIT];
|
||||
if(matchingLeaf && matchingLeaf.type == 'leaf') {
|
||||
return matchingLeaf.entries.map(function(value) { return value.content });
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Traversal(this._trie['root'], '');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -105,6 +105,6 @@ function do_test() {
|
|||
}
|
||||
|
||||
builder_run_action configure verify_npm_setup
|
||||
builder_run_action clean rm -rf build/
|
||||
builder_run_action clean rm -rf build/ intermediate/
|
||||
builder_run_action build do_build
|
||||
builder_run_action test do_test
|
||||
|
|
@ -36,5 +36,8 @@
|
|||
"type": "module",
|
||||
"paths": {
|
||||
"@keymanapp/keyman-version": "*"
|
||||
}
|
||||
},
|
||||
"sideEffects": [
|
||||
"./src/kmwstring.ts", "./build/obj/kmwstring.js"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,16 +44,29 @@ compile_and_copy() {
|
|||
compile $SUBPROJECT_NAME
|
||||
|
||||
BUILD_ROOT="${KEYMAN_ROOT}/web/build/app/webview"
|
||||
SRC_ROOT="${KEYMAN_ROOT}/web/src/app/webview/src"
|
||||
|
||||
$BUNDLE_CMD "${BUILD_ROOT}/obj/debug-main.js" \
|
||||
--out "${BUILD_ROOT}/debug/keymanweb-webview.js" \
|
||||
--out "${BUILD_ROOT}/debug/keymanweb-webview.es5.js" \
|
||||
--sourceRoot "@keymanapp/keyman/web/build/app/webview/debug"
|
||||
|
||||
$BUNDLE_CMD "${BUILD_ROOT}/obj/release-main.js" \
|
||||
--out "${BUILD_ROOT}/release/keymanweb-webview.es5.js" \
|
||||
--profile "${BUILD_ROOT}/filesize-profile.es5.log" \
|
||||
--sourceRoot "@keymanapp/keyman/web/build/app/webview/release" \
|
||||
--minify
|
||||
|
||||
$BUNDLE_CMD "${SRC_ROOT}/debug-main.js" \
|
||||
--out "${BUILD_ROOT}/debug/keymanweb-webview.js" \
|
||||
--sourceRoot "@keymanapp/keyman/web/build/app/webview/debug" \
|
||||
--target "es6"
|
||||
|
||||
$BUNDLE_CMD "${SRC_ROOT}/release-main.js" \
|
||||
--out "${BUILD_ROOT}/release/keymanweb-webview.js" \
|
||||
--profile "${BUILD_ROOT}/filesize-profile.log" \
|
||||
--sourceRoot "@keymanapp/keyman/web/build/app/webview/release" \
|
||||
--minify
|
||||
--minify \
|
||||
--target "es6"
|
||||
|
||||
mkdir -p "$KEYMAN_ROOT/web/build/app/resources/osk"
|
||||
cp -R "$KEYMAN_ROOT/web/src/resources/osk/." "$KEYMAN_ROOT/web/build/app/resources/osk/"
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ export class StaticActivator extends Activator {
|
|||
return true;
|
||||
}
|
||||
|
||||
set enabled(value: boolean) {
|
||||
// does nothing; it's static.
|
||||
}
|
||||
|
||||
get activate(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue