mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-07 17:36:00 +00:00
Merge branch 'master' into lmlayer-specify-punctuation
This commit is contained in:
commit
fe0a99c6e4
27 changed files with 654 additions and 225 deletions
|
|
@ -9,7 +9,7 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
applicationId "com.tavultesoft.kmapro"
|
||||
minSdkVersion 16
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 28
|
||||
|
||||
//println "===DUMPING PROPERTIES==="
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
var device = window.jsInterface.getDeviceType();
|
||||
var oskHeight = window.jsInterface.getKeyboardHeight();
|
||||
var oskWidth = 0;
|
||||
var fragmentToggle = 0;
|
||||
window.addEventListener('load', init, false);
|
||||
|
||||
function init() {
|
||||
|
|
@ -39,6 +40,18 @@
|
|||
kmw.addEventListener('keyboardloaded', setIsChiral);
|
||||
kmw.addEventListener('keyboardchange', setIsChiral);
|
||||
kmw.addEventListener('mm.modelchange', onModelChange);
|
||||
|
||||
notifyHost('pageLoaded');
|
||||
}
|
||||
|
||||
function notifyHost(event, params) {
|
||||
// TODO: Update all other host notifications to use notifyHost instead of directly setting window.location.hash
|
||||
window.setTimeout(function() {
|
||||
// We use a timeout so that the navigation doesn't cause the calling function to abort after the call
|
||||
fragmentToggle = (fragmentToggle + 1) % 100;
|
||||
params = params ? '+'+params : '';
|
||||
window.location.hash = event+'-'+fragmentToggle+params;
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// Update the KMW banner height
|
||||
|
|
@ -211,7 +224,6 @@
|
|||
kmw['setActiveElement'](ta);
|
||||
}
|
||||
|
||||
var fragmentToggle = 0;
|
||||
function oskCreateKeyPreview(x,y,w,h,t) {
|
||||
fragmentToggle = (fragmentToggle + 1) % 100;
|
||||
var div = document.createElement('div');
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ final class KMKeyboard extends WebView {
|
|||
private String keyboardName;
|
||||
private String keyboardVersion;
|
||||
|
||||
protected ArrayList<String> javascriptAfterLoad = new ArrayList<String>();
|
||||
|
||||
private static String currentKeyboard = null;
|
||||
private static String txtFont = "";
|
||||
private static String oskFont = null;
|
||||
|
|
@ -136,12 +138,16 @@ final class KMKeyboard extends WebView {
|
|||
public boolean onConsoleMessage(ConsoleMessage cm) {
|
||||
if (KMManager.isDebugMode()) {
|
||||
Log.d("KMEA", "Keyman JS Log: Line " + cm.lineNumber() + ", " + cm.sourceId() + ":" + cm.message());
|
||||
if (cm.messageLevel() == ConsoleMessage.MessageLevel.ERROR) {
|
||||
Log.e("KMEA", "Keyman JS Log: ERROR");
|
||||
}
|
||||
}
|
||||
|
||||
// Send console errors to Firebase Analytics.
|
||||
// (Ignoring spurious message "No keyboard stubs exist = ...")
|
||||
// TODO: Analyze if this error warrants reverting to default keyboard
|
||||
// TODO: Fix base error rather than trying to ignore it "No keyboard stubs exist"
|
||||
|
||||
if ((cm.messageLevel() == ConsoleMessage.MessageLevel.ERROR) && (!cm.message().startsWith("No keyboard stubs exist"))) {
|
||||
sendKMWError(cm.lineNumber(), cm.sourceId(), cm.message());
|
||||
Toast.makeText(context, "Fatal Error with " + currentKeyboard +
|
||||
|
|
@ -151,6 +157,7 @@ final class KMKeyboard extends WebView {
|
|||
KMManager.KMDefault_LanguageID, KMManager.KMDefault_KeyboardName,
|
||||
KMManager.KMDefault_LanguageName, KMManager.KMDefault_KeyboardFont, null);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
|
@ -167,10 +174,11 @@ final class KMKeyboard extends WebView {
|
|||
if (subKeysList != null) {
|
||||
showSubKeys(context);
|
||||
return;
|
||||
} else if(suggestionJSON != null) {
|
||||
} /* For future implementation
|
||||
else if(suggestionJSON != null) {
|
||||
showSuggestionLongpress(context);
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -182,20 +190,55 @@ final class KMKeyboard extends WebView {
|
|||
|
||||
public void loadKeyboard() {
|
||||
keyboardSet = false;
|
||||
|
||||
if(keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP)
|
||||
KMManager.InAppKeyboardLoaded = false;
|
||||
else
|
||||
KMManager.SystemKeyboardLoaded = false;
|
||||
|
||||
String htmlPath = "file://" + getContext().getDir("data", Context.MODE_PRIVATE) + "/" + KMManager.KMFilename_KeyboardHtml;
|
||||
loadUrl(htmlPath);
|
||||
setBackgroundColor(0);
|
||||
}
|
||||
|
||||
public void loadJavascript(String func) {
|
||||
this.javascriptAfterLoad.add(func);
|
||||
|
||||
if((keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP && KMManager.InAppKeyboardLoaded) ||
|
||||
(keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM && KMManager.SystemKeyboardLoaded)) {
|
||||
|
||||
if(this.javascriptAfterLoad.size() == 1)
|
||||
callJavascriptAfterLoad();
|
||||
}
|
||||
}
|
||||
|
||||
public void callJavascriptAfterLoad() {
|
||||
if(this.javascriptAfterLoad.size() > 0) {
|
||||
Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if(javascriptAfterLoad.size() > 0) {
|
||||
loadUrl("javascript:" + javascriptAfterLoad.get(0));
|
||||
javascriptAfterLoad.remove(0);
|
||||
if (javascriptAfterLoad.size() > 0) {
|
||||
callJavascriptAfterLoad();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideKeyboard() {
|
||||
String jsString = "javascript:hideKeyboard()";
|
||||
loadUrl(jsString);
|
||||
String jsString = "hideKeyboard()";
|
||||
loadJavascript(jsString);
|
||||
}
|
||||
|
||||
public void executeHardwareKeystroke(int code, int shift, int lstates, int eventModifiers) {
|
||||
String jsFormat = "javascript:executeHardwareKeystroke(%d,%d, %d, %d)";
|
||||
String jsFormat = "executeHardwareKeystroke(%d,%d, %d, %d)";
|
||||
String jsString = String.format(jsFormat, code, shift, lstates, eventModifiers);
|
||||
loadUrl(jsString);
|
||||
loadJavascript(jsString);
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
|
|
@ -225,8 +268,8 @@ final class KMKeyboard extends WebView {
|
|||
DisplayMetrics dms = context.getResources().getDisplayMetrics();
|
||||
int kbWidth = (int) (dms.widthPixels / dms.density);
|
||||
// Ensure window is loaded for javascript functions
|
||||
loadUrl(String.format(
|
||||
"javascript:window.onload = function(){ setOskWidth(\"%d\");"+
|
||||
loadJavascript(String.format(
|
||||
"window.onload = function(){ setOskWidth(\"%d\");"+
|
||||
"setOskHeight(\"0\"); };", kbWidth));
|
||||
if (ShouldShowHelpBubble) {
|
||||
ShouldShowHelpBubble = false;
|
||||
|
|
@ -234,7 +277,7 @@ final class KMKeyboard extends WebView {
|
|||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadUrl("javascript:showHelpBubble()");
|
||||
loadJavascript("showHelpBubble()");
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
|
@ -257,15 +300,15 @@ final class KMKeyboard extends WebView {
|
|||
dismissKeyPreview(0);
|
||||
dismissSubKeysWindow();
|
||||
int bannerHeight = KMManager.getBannerHeight(context);
|
||||
loadUrl(String.format("javascript:setBannerHeight(%d)", bannerHeight));
|
||||
loadUrl(String.format("javascript:setOskWidth(%d)", newConfig.screenWidthDp));
|
||||
loadUrl("javascript:setOskHeight(0)");
|
||||
loadJavascript(String.format("setBannerHeight(%d)", bannerHeight));
|
||||
loadJavascript(String.format("setOskWidth(%d)", newConfig.screenWidthDp));
|
||||
loadJavascript("setOskHeight(0)");
|
||||
if (dismissHelpBubble()) {
|
||||
Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadUrl("javascript:showHelpBubble()");
|
||||
loadJavascript("showHelpBubble()");
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
|
@ -385,9 +428,9 @@ final class KMKeyboard extends WebView {
|
|||
keyboardName = keyboardName.replaceAll("\'", "\\\\'"); // Double-escaped-backslash b/c regex.
|
||||
languageName = languageName.replaceAll("\'", "\\\\'");
|
||||
|
||||
String jsFormat = "javascript:setKeymanLanguage('%s','%s','%s','%s','%s', %s, %s, '%s')";
|
||||
String jsFormat = "setKeymanLanguage('%s','%s','%s','%s','%s', %s, %s, '%s')";
|
||||
String jsString = String.format(jsFormat, keyboardName, keyboardID, languageName, languageID, keyboardPath, tFont, oFont, packageID);
|
||||
loadUrl(jsString);
|
||||
loadJavascript(jsString);
|
||||
|
||||
this.packageID = packageID;
|
||||
this.keyboardID = keyboardID;
|
||||
|
|
@ -401,7 +444,7 @@ final class KMKeyboard extends WebView {
|
|||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadUrl("javascript:showHelpBubble()");
|
||||
loadJavascript("showHelpBubble()");
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
|
@ -486,9 +529,9 @@ final class KMKeyboard extends WebView {
|
|||
keyboardName = keyboardName.replaceAll("\'", "\\\\'"); // Double-escaped-backslash b/c regex.
|
||||
languageName = languageName.replaceAll("\'", "\\\\'");
|
||||
|
||||
String jsFormat = "javascript:setKeymanLanguage('%s','%s','%s','%s','%s', %s, %s, '%s')";
|
||||
String jsFormat = "setKeymanLanguage('%s','%s','%s','%s','%s', %s, %s, '%s')";
|
||||
String jsString = String.format(jsFormat, keyboardName, keyboardID, languageName, languageID, keyboardPath, tFont, oFont, packageID);
|
||||
loadUrl(jsString);
|
||||
loadJavascript(jsString);
|
||||
|
||||
this.packageID = packageID;
|
||||
this.keyboardID = keyboardID;
|
||||
|
|
@ -502,7 +545,7 @@ final class KMKeyboard extends WebView {
|
|||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadUrl("javascript:showHelpBubble()");
|
||||
loadJavascript("showHelpBubble()");
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
|
@ -712,14 +755,14 @@ final class KMKeyboard extends WebView {
|
|||
public void onDismiss() {
|
||||
suggestionJSON = null;
|
||||
suggestionMenuWindow = null;
|
||||
String jsString = "javascript:popupVisible(0)";
|
||||
loadUrl(jsString);
|
||||
String jsString = "popupVisible(0)";
|
||||
loadJavascript(jsString);
|
||||
}
|
||||
});
|
||||
|
||||
suggestionMenuWindow.showAtLocation(KMKeyboard.this, Gravity.TOP | Gravity.LEFT, posX , posY);
|
||||
String jsString = "javascript:popupVisible(1)";
|
||||
loadUrl(jsString);
|
||||
String jsString = "popupVisible(1)";
|
||||
loadJavascript(jsString);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -822,9 +865,9 @@ final class KMKeyboard extends WebView {
|
|||
int index = v.getId() - 1;
|
||||
String keyId = subkeyList.get(index).get("keyId");
|
||||
String keyText = getSubkeyText(keyId, subkeyList.get(index).get("keyText"));
|
||||
String jsFormat = "javascript:executePopupKey('%s','%s')";
|
||||
String jsFormat = "executePopupKey('%s','%s')";
|
||||
String jsString = String.format(jsFormat, keyId, keyText);
|
||||
loadUrl(jsString);
|
||||
loadJavascript(jsString);
|
||||
}
|
||||
});
|
||||
button.setClickable(false);
|
||||
|
|
@ -909,8 +952,8 @@ final class KMKeyboard extends WebView {
|
|||
public void onDismiss() {
|
||||
subKeysList = null;
|
||||
subKeysWindow = null;
|
||||
String jsString = "javascript:popupVisible(0)";
|
||||
loadUrl(jsString);
|
||||
String jsString = "popupVisible(0)";
|
||||
loadJavascript(jsString);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -933,8 +976,8 @@ final class KMKeyboard extends WebView {
|
|||
|
||||
// And now to actually display it.
|
||||
subKeysWindow.showAtLocation(KMKeyboard.this, Gravity.TOP | Gravity.LEFT, posX, posY);
|
||||
String jsString = "javascript:popupVisible(1)";
|
||||
loadUrl(jsString);
|
||||
String jsString = "popupVisible(1)";
|
||||
loadJavascript(jsString);
|
||||
}
|
||||
|
||||
// Attempt to get the subkey text.
|
||||
|
|
|
|||
|
|
@ -718,11 +718,11 @@ public final class KMManager {
|
|||
RelativeLayout.LayoutParams params = getKeyboardLayoutParams();
|
||||
if (InAppKeyboard != null && InAppKeyboardLoaded && !InAppKeyboardShouldIgnoreTextChange) {
|
||||
InAppKeyboard.setLayoutParams(params);
|
||||
InAppKeyboard.loadUrl(String.format("javascript:enableSuggestions(%s, %s, %s)", model, mayPredict, mayCorrect));
|
||||
InAppKeyboard.loadJavascript(String.format("enableSuggestions(%s, %s, %s)", model, mayPredict, mayCorrect));
|
||||
}
|
||||
if (SystemKeyboard != null && SystemKeyboardLoaded && !SystemKeyboardShouldIgnoreTextChange) {
|
||||
SystemKeyboard.setLayoutParams(params);
|
||||
SystemKeyboard.loadUrl(String.format("javascript:enableSuggestions(%s, %s, %s)", model, mayPredict, mayCorrect));
|
||||
SystemKeyboard.loadJavascript(String.format("enableSuggestions(%s, %s, %s)", model, mayPredict, mayCorrect));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -733,13 +733,13 @@ public final class KMManager {
|
|||
currentLexicalModel = null;
|
||||
}
|
||||
|
||||
String url = String.format("javascript:deregisterModel('%s')", modelID);
|
||||
if (InAppKeyboard != null && InAppKeyboardLoaded) {
|
||||
InAppKeyboard.loadUrl(url);
|
||||
String url = String.format("deregisterModel('%s')", modelID);
|
||||
if (InAppKeyboard != null) { // && InAppKeyboardLoaded) {
|
||||
InAppKeyboard.loadJavascript(url);
|
||||
}
|
||||
|
||||
if (SystemKeyboard != null && SystemKeyboardLoaded) {
|
||||
SystemKeyboard.loadUrl(url);
|
||||
if (SystemKeyboard != null) { // && SystemKeyboardLoaded) {
|
||||
SystemKeyboard.loadJavascript(url);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1103,11 +1103,11 @@ public final class KMManager {
|
|||
public static void setNumericLayer(KeyboardType kbType) {
|
||||
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
if (InAppKeyboard != null && InAppKeyboardLoaded && !InAppKeyboardShouldIgnoreTextChange) {
|
||||
InAppKeyboard.loadUrl("javascript:setNumericLayer()");
|
||||
InAppKeyboard.loadJavascript("setNumericLayer()");
|
||||
}
|
||||
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
if (SystemKeyboard != null && SystemKeyboardLoaded && !SystemKeyboardShouldIgnoreTextChange) {
|
||||
SystemKeyboard.loadUrl("javascript:setNumericLayer()");
|
||||
SystemKeyboard.loadJavascript("setNumericLayer()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1121,14 +1121,14 @@ public final class KMManager {
|
|||
|
||||
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
if (InAppKeyboard != null && InAppKeyboardLoaded && !InAppKeyboardShouldIgnoreTextChange) {
|
||||
InAppKeyboard.loadUrl(String.format("javascript:updateKMText('%s')", kmText));
|
||||
InAppKeyboard.loadJavascript(String.format("updateKMText('%s')", kmText));
|
||||
result = true;
|
||||
}
|
||||
|
||||
InAppKeyboardShouldIgnoreTextChange = false;
|
||||
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
if (SystemKeyboard != null && SystemKeyboardLoaded && !SystemKeyboardShouldIgnoreTextChange) {
|
||||
SystemKeyboard.loadUrl(String.format("javascript:updateKMText('%s')", kmText));
|
||||
SystemKeyboard.loadJavascript(String.format("updateKMText('%s')", kmText));
|
||||
result = true;
|
||||
}
|
||||
|
||||
|
|
@ -1142,7 +1142,7 @@ public final class KMManager {
|
|||
boolean result = false;
|
||||
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
if (InAppKeyboard != null && InAppKeyboardLoaded && !InAppKeyboardShouldIgnoreSelectionChange) {
|
||||
InAppKeyboard.loadUrl(String.format("javascript:updateKMSelectionRange(%d,%d)", selStart, selEnd));
|
||||
InAppKeyboard.loadJavascript(String.format("updateKMSelectionRange(%d,%d)", selStart, selEnd));
|
||||
result = true;
|
||||
}
|
||||
|
||||
|
|
@ -1157,7 +1157,7 @@ public final class KMManager {
|
|||
}
|
||||
}
|
||||
|
||||
SystemKeyboard.loadUrl(String.format("javascript:updateKMSelectionRange(%d,%d)", selStart, selEnd));
|
||||
SystemKeyboard.loadJavascript(String.format("updateKMSelectionRange(%d,%d)", selStart, selEnd));
|
||||
result = true;
|
||||
}
|
||||
|
||||
|
|
@ -1170,11 +1170,11 @@ public final class KMManager {
|
|||
public static void resetContext(KeyboardType kbType) {
|
||||
if (kbType == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
if (InAppKeyboard != null && InAppKeyboardLoaded) {
|
||||
InAppKeyboard.loadUrl("javascript:resetContext()");
|
||||
InAppKeyboard.loadJavascript("resetContext()");
|
||||
}
|
||||
} else if (kbType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
if (SystemKeyboard != null && SystemKeyboardLoaded) {
|
||||
SystemKeyboard.loadUrl("javascript:resetContext()");
|
||||
SystemKeyboard.loadJavascript("resetContext()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1301,15 +1301,18 @@ public final class KMManager {
|
|||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
if (url.endsWith(KMFilename_KeyboardHtml)) {
|
||||
InAppKeyboardLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
Log.d("KMEA", "onPageFinished: [inapp] " + url);
|
||||
shouldOverrideUrlLoading(view, url);
|
||||
}
|
||||
|
||||
private void pageLoaded(WebView view, String url) {
|
||||
String langId = KMManager.KMKey_LanguageID;
|
||||
if (url.endsWith(KMFilename_KeyboardHtml)) {
|
||||
Log.d("KMEA", "pageLoaded: [inapp] " + url);
|
||||
if (url.startsWith("file")) { //endsWith(KMFilename_KeyboardHtml)) {
|
||||
InAppKeyboardLoaded = true;
|
||||
|
||||
if (!InAppKeyboard.keyboardSet) {
|
||||
|
|
@ -1342,20 +1345,23 @@ public final class KMManager {
|
|||
public void run() {
|
||||
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
if (prefs.getBoolean(KMManager.KMKey_ShouldShowHelpBubble, true)) {
|
||||
InAppKeyboard.loadUrl("javascript:showHelpBubble()");
|
||||
InAppKeyboard.loadJavascript("showHelpBubble()");
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
InAppKeyboard.callJavascriptAfterLoad();
|
||||
|
||||
KeyboardEventHandler.notifyListeners(KMTextView.kbEventListeners, KeyboardType.KEYBOARD_TYPE_INAPP, EventType.KEYBOARD_LOADED, null);
|
||||
}
|
||||
|
||||
shouldOverrideUrlLoading(view, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
if (url.indexOf("hideKeyboard") >= 0) {
|
||||
Log.d("KMEA", "shouldOverrideUrlLoading [inapp]: "+url);
|
||||
if(url.indexOf("pageLoaded") >= 0) {
|
||||
pageLoaded(view, url);
|
||||
} else if (url.indexOf("hideKeyboard") >= 0) {
|
||||
if (KMTextView.activeView != null && KMTextView.activeView.getClass() == KMTextView.class) {
|
||||
InAppKeyboard.dismissHelpBubble();
|
||||
KMTextView textView = (KMTextView) KMTextView.activeView;
|
||||
|
|
@ -1490,17 +1496,19 @@ public final class KMManager {
|
|||
JSONParser parser = new JSONParser();
|
||||
JSONObject obj = parser.getJSONObjectFromURIString(suggestionJSON);
|
||||
|
||||
/* // For future implementation
|
||||
InAppKeyboard.suggestionWindowPos = new double[]{x, y};
|
||||
InAppKeyboard.suggestionJSON = suggestionJSON;
|
||||
|
||||
try {
|
||||
Log.v("KMEA", "Suggestion display: " + obj.getString("displayAs"));
|
||||
Log.v("KMEA", "Suggestion's banner coords: " + x + ", " + y + ", " + width + ", " + height);
|
||||
Log.v("KMEA", "Is a <keep> suggestion: " + isCustom);
|
||||
Log.v("KMEA", "Is a <keep> suggestion: " + isCustom); // likely outdated now that tags exist.
|
||||
} catch (JSONException e) {
|
||||
//e.printStackTrace();
|
||||
Log.v("KMEA", "JSON parsing error: " + e.getMessage());
|
||||
}
|
||||
*/
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1515,15 +1523,18 @@ public final class KMManager {
|
|||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
if (url.endsWith(KMFilename_KeyboardHtml)) {
|
||||
SystemKeyboardLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
Log.d("KMEA", "onPageFinished: [system] " + url);
|
||||
shouldOverrideUrlLoading(view, url);
|
||||
}
|
||||
|
||||
private void pageLoaded(WebView view, String url) {
|
||||
String langId = KMManager.KMKey_LanguageID;
|
||||
if (url.endsWith(KMFilename_KeyboardHtml)) {
|
||||
Log.d("KMEA", "pageLoaded: [system] " + url);
|
||||
if (url.startsWith("file:")) {
|
||||
SystemKeyboardLoaded = true;
|
||||
if (!SystemKeyboard.keyboardSet) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
|
|
@ -1555,20 +1566,22 @@ public final class KMManager {
|
|||
public void run() {
|
||||
SharedPreferences prefs = context.getSharedPreferences(context.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE);
|
||||
if (prefs.getBoolean(KMManager.KMKey_ShouldShowHelpBubble, true)) {
|
||||
SystemKeyboard.loadUrl("javascript:showHelpBubble()");
|
||||
SystemKeyboard.loadJavascript("showHelpBubble()");
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
KeyboardEventHandler.notifyListeners(KMTextView.kbEventListeners, KeyboardType.KEYBOARD_TYPE_SYSTEM, EventType.KEYBOARD_LOADED, null);
|
||||
}
|
||||
|
||||
shouldOverrideUrlLoading(view, url);
|
||||
SystemKeyboard.callJavascriptAfterLoad();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
if (url.indexOf("hideKeyboard") >= 0) {
|
||||
if(url.indexOf("pageLoaded") >= 0) {
|
||||
pageLoaded(view, url);
|
||||
} else if (url.indexOf("hideKeyboard") >= 0) {
|
||||
SystemKeyboard.dismissHelpBubble();
|
||||
IMService.requestHideSelf(0);
|
||||
} else if (url.indexOf("globeKeyAction") >= 0) {
|
||||
|
|
@ -1718,14 +1731,19 @@ public final class KMManager {
|
|||
JSONParser parser = new JSONParser();
|
||||
JSONObject obj = parser.getJSONObjectFromURIString(suggestionJSON);
|
||||
|
||||
/* // For future implementation
|
||||
SystemKeyboard.suggestionWindowPos = new double[]{x, y};
|
||||
SystemKeyboard.suggestionJSON = suggestionJSON;
|
||||
|
||||
try {
|
||||
Log.v("KMEA", "Suggestion display: " + obj.getString("displayAs"));
|
||||
Log.v("KMEA", "Suggestion's banner coords: " + x + ", " + y + ", " + width + ", " + height);
|
||||
Log.v("KMEA", "Is a <keep> suggestion: " + isCustom);
|
||||
Log.v("KMEA", "Is a <keep> suggestion: " + isCustom); // likely outdated now that tags exist.
|
||||
} catch (JSONException e) {
|
||||
//e.printStackTrace();
|
||||
Log.v("KMEA", "JSON parsing error: " + e.getMessage());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
# Keyman for Android
|
||||
|
||||
## 12.0 alpha
|
||||
## 2019-07-29 12.0.2200 beta
|
||||
* Add new "Settings" menu (#1751)
|
||||
* Update compile and target Android SDK version to 28 (#1654)
|
||||
* Add feature to install lexical model packages (#1641, 1653)
|
||||
* Started work on Keyman for Android 12.
|
||||
* Add predictive text support (#1641, #1653)
|
||||
* Minimum supported Android version is now 4.4 (KitKat) (#1905)
|
||||
|
||||
## 2019-06-28 11.0.2108 stable
|
||||
* Changes:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
"url": "git+https://github.com/eddieantonio/keyman-lmlayer-prototype.git"
|
||||
},
|
||||
"author": "Eddie Antonio Santos <easantos@ualberta.ca> (http://eddieantonio.ca/)",
|
||||
"license": "AGPL-3.0",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/eddieantonio/keyman-lmlayer-prototype/issues"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -73,12 +73,14 @@ module.exports = function(config) {
|
|||
};
|
||||
|
||||
var CURRENT_WIN_LAUNCHERS = {
|
||||
bs_firefox_win: {
|
||||
os: 'Windows',
|
||||
os_version: '10',
|
||||
browser: 'firefox',
|
||||
browser_version: '62.0'
|
||||
},
|
||||
// Currently, Firefox launcher is unstable; see https://github.com/karma-runner/karma-firefox-launcher/issues/93
|
||||
// (in particular "not maintained" commentary).
|
||||
//bs_firefox_win: {
|
||||
// os: 'Windows',
|
||||
// os_version: '10',
|
||||
// browser: 'firefox',
|
||||
// browser_version: '62.0'
|
||||
//},
|
||||
bs_chrome_win: {
|
||||
os: 'Windows',
|
||||
os_version: '10',
|
||||
|
|
@ -91,12 +93,13 @@ module.exports = function(config) {
|
|||
browser: 'ie',
|
||||
browser_version: '11.0'
|
||||
},
|
||||
bs_edge_win: {
|
||||
os: 'Windows',
|
||||
os_version: '10',
|
||||
browser: 'edge',
|
||||
browser_version: '17.0'
|
||||
}
|
||||
// On recent versions of Edge, launcher fails to start and/or stop Edge successfully
|
||||
//bs_edge_win: {
|
||||
// os: 'Windows',
|
||||
// os_version: '10',
|
||||
// browser: 'edge',
|
||||
// browser_version: '17.0'
|
||||
//}
|
||||
}
|
||||
|
||||
var CURRENT_ANDROID_LAUNCHERS = {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ get_browser_set_for_OS ( ) {
|
|||
if [ $os_id = "mac" ]; then
|
||||
BROWSERS="--browsers Firefox,Chrome,Safari"
|
||||
elif [ $os_id = "win" ]; then
|
||||
BROWSERS="--browsers Firefox,Chrome,IE,Edge"
|
||||
BROWSERS="--browsers Chrome,IE"
|
||||
else
|
||||
BROWSERS="--browsers Firefox,Chrome"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ module.exports = function(config) {
|
|||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ['Firefox', 'IE', 'Chrome', 'Edge'], // Can be specified at run-time instead!
|
||||
browsers: ['IE', 'Chrome'], // Can be specified at run-time instead!
|
||||
// Note: disabled Firefox, Edge due to instability
|
||||
// Future note for us: https://www.npmjs.com/package/karma-browserstack-launcher
|
||||
|
||||
// Concurrency level
|
||||
|
|
|
|||
|
|
@ -135,8 +135,54 @@ interface LMLayerWorkerState {
|
|||
* The model implementation, within the Worker.
|
||||
*/
|
||||
interface WorkerInternalModel {
|
||||
/**
|
||||
* Processes `config` messages, configuring the newly-loaded model based on the host
|
||||
* platform's capability restrictions.
|
||||
*
|
||||
* This allows the model to configure its suggestions according to what the platform
|
||||
* allows the host to actually perform - for example, if post-caret deletions are not
|
||||
* supported, no suggestions requiring this feature should be produced by the model.
|
||||
*
|
||||
* Returns a `Configuration` object detailing the capabilities the model plans to
|
||||
* actually utilize, which must be as restrictive or more restrictive than those
|
||||
* indicated within the provided `Capabilities` object.
|
||||
* @param capabilities
|
||||
*/
|
||||
configure(capabilities: Capabilities): Configuration;
|
||||
|
||||
/**
|
||||
* Generates predictive suggestions corresponding to the state of context after the proposed
|
||||
* transform is applied to it. This transform may correspond to a 'correction' of a recent
|
||||
* keystroke rather than one actually received.
|
||||
*
|
||||
* This method should NOT attempt to perform any form of correction; this is modeled within a
|
||||
* separate component of the LMLayer predictive engine. That is, "th" + "e" should not be
|
||||
* have "this" for a suggestion ("e" has been 'corrected' to "i"), while "there" would be
|
||||
* a reasonable prediction.
|
||||
*
|
||||
* However, addition of diacritics to characters (which may transform the underlying char code
|
||||
* when Unicode-normalized) is permitted. For example, "pur" + "e" may reasonably predict
|
||||
* "purée", where "e" has been transformed to "é" as part of the suggestion.
|
||||
*
|
||||
* When both prediction and correction are permitted, said component (the `ModelCompositor`) will
|
||||
* generally call this method once per 'likely' generated corrected state of the context,
|
||||
* utilizing the results to compute an overall likelihood across all possible suggestions.
|
||||
* @param transform A Transform corresponding to a recent input keystroke
|
||||
* @param context A depiction of the context to which `transform` is applied.
|
||||
* @returns A probability distribution (`Distribution<Suggestion>`) on the resulting `Suggestion`
|
||||
* space for use in determining the most optimal overall suggestions.
|
||||
*/
|
||||
predict(transform: Transform, context: Context): Distribution<Suggestion>;
|
||||
|
||||
/**
|
||||
* Performs a wordbreak operation given the current context state, returning whatever word
|
||||
* or word fragment exists that starts before the caret but after the most recent whitespace
|
||||
* preceding the caret. If no such text exists, the empty string is returned.
|
||||
*
|
||||
* This function is designed for use in generating display text for 'keep' `Suggestions`
|
||||
* and display text for reverting any previously-applied `Suggestions`.
|
||||
* @param context
|
||||
*/
|
||||
wordbreak(context: Context): USVString;
|
||||
|
||||
/**
|
||||
|
|
|
|||
59
developer/js/build.sh
Executable file
59
developer/js/build.sh
Executable file
|
|
@ -0,0 +1,59 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Compiles the developer tools, including the language model compilers.
|
||||
#
|
||||
|
||||
# Exit on command failure and when using unset variables:
|
||||
set -eu
|
||||
|
||||
# Include some helper functions from resources
|
||||
. ../../resources/shellHelperFunctions.sh
|
||||
EX_USAGE=64
|
||||
|
||||
|
||||
# Build the main script.
|
||||
build () {
|
||||
npm run build || fail "Could not build top-level JavaScript file."
|
||||
}
|
||||
|
||||
display_usage ( ) {
|
||||
echo "Usage: $0 [-test]"
|
||||
echo " $0 -help"
|
||||
echo
|
||||
echo " -help displays this screen and exits"
|
||||
echo " -test runs unit tests after building"
|
||||
}
|
||||
|
||||
################################ Main script ################################
|
||||
|
||||
run_tests=0
|
||||
|
||||
# Process command-line arguments
|
||||
while [[ $# -gt 0 ]] ; do
|
||||
key="$1"
|
||||
case $key in
|
||||
-help|-h)
|
||||
display_usage
|
||||
exit
|
||||
;;
|
||||
-test)
|
||||
run_tests=1
|
||||
;;
|
||||
*)
|
||||
echo "$0: invalid option: $key"
|
||||
display_usage
|
||||
exit $EX_USAGE
|
||||
esac
|
||||
shift # past the processed argument
|
||||
done
|
||||
|
||||
# Check if Node.JS/npm is installed.
|
||||
type npm >/dev/null ||\
|
||||
fail "Build environment setup error detected! Please ensure Node.js is installed!"
|
||||
|
||||
build || fail "Compilation failed."
|
||||
echo "Typescript compilation successful."
|
||||
|
||||
if (( run_tests )); then
|
||||
npm test || fail "Tests failed"
|
||||
fi
|
||||
|
|
@ -123,12 +123,12 @@
|
|||
9A3B14D3229370B20052A11F /* InstalledLanguagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */; };
|
||||
9A4609972241B39B00B0BFD1 /* LexicalModelInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */; };
|
||||
9A4609992242047400B0BFD1 /* LexicalModelAPICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A4609982242047400B0BFD1 /* LexicalModelAPICall.swift */; };
|
||||
9A60764422893A4E003BCFBA /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A60764322893A4E003BCFBA /* SettingsViewController.swift */; };
|
||||
9A7EEFA222DCF64F00877C22 /* LanguageSpecificViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7EEFA122DCF64F00877C22 /* LanguageSpecificViewController.swift */; };
|
||||
9A5091B122B418140094B99C /* KeyboardCommandStructs.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE67D960228A6F190029F2B5 /* KeyboardCommandStructs.swift */; };
|
||||
9A5091B222B418A90094B99C /* KeyboardPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62161F67CFB800268D03 /* KeyboardPickerViewController.swift */; };
|
||||
9A5091B322B418CA0094B99C /* KeyboardInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62141F67C8D500268D03 /* KeyboardInfoViewController.swift */; };
|
||||
9A5091B422B419B10094B99C /* LanguageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04C2A6C1F6B7D9A00BA42B6 /* LanguageViewController.swift */; };
|
||||
9A60764422893A4E003BCFBA /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A60764322893A4E003BCFBA /* SettingsViewController.swift */; };
|
||||
9A7EEFA222DCF64F00877C22 /* LanguageSpecificViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7EEFA122DCF64F00877C22 /* LanguageSpecificViewController.swift */; };
|
||||
9A862D2822B429FB005FE26D /* KeyboardNameTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08C62121F67C31100268D03 /* KeyboardNameTableViewCell.swift */; };
|
||||
9A862D2922B42A0C005FE26D /* LanguageDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C092D8381F6A70C8005C5485 /* LanguageDetailViewController.swift */; };
|
||||
9A9CB08022416E5400231FB9 /* LexicalModelPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */; };
|
||||
|
|
@ -139,6 +139,11 @@
|
|||
9AD4F53D229F85AC007992D3 /* LanguageSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9AD4F53B229F85AC007992D3 /* LanguageSettingsViewController.xib */; };
|
||||
9AD4F53E22A8A25B007992D3 /* LanguageSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AD4F53A229F85AC007992D3 /* LanguageSettingsViewController.swift */; };
|
||||
9AD4F53F22A8A286007992D3 /* LanguageSettingsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9AD4F53B229F85AC007992D3 /* LanguageSettingsViewController.xib */; };
|
||||
9ADC459A22E186F5004C78C6 /* LanguageSpecificViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A7EEFA122DCF64F00877C22 /* LanguageSpecificViewController.swift */; };
|
||||
9ADC459D22E1895D004C78C6 /* LanguageLMDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADC459B22E1895D004C78C6 /* LanguageLMDetailViewController.swift */; };
|
||||
9ADC459E22E1895D004C78C6 /* LanguageLMDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADC459B22E1895D004C78C6 /* LanguageLMDetailViewController.swift */; };
|
||||
9ADC459F22E1895D004C78C6 /* LanguageLMDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9ADC459C22E1895D004C78C6 /* LanguageLMDetailViewController.xib */; };
|
||||
9ADC45A022E1895D004C78C6 /* LanguageLMDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9ADC459C22E1895D004C78C6 /* LanguageLMDetailViewController.xib */; };
|
||||
C007C4651F9F52D8006461B9 /* LanguagesAPICall.swift in Sources */ = {isa = PBXBuildFile; fileRef = C007C4641F9F52D8006461B9 /* LanguagesAPICall.swift */; };
|
||||
C024C9941FA6EB470060583B /* NotificationName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C024C9931FA6EB470060583B /* NotificationName.swift */; };
|
||||
C024C9961FA6EC650060583B /* NotificationCenter+Typed.swift in Sources */ = {isa = PBXBuildFile; fileRef = C024C9951FA6EC650060583B /* NotificationCenter+Typed.swift */; };
|
||||
|
|
@ -335,8 +340,8 @@
|
|||
9A079E3F223B602B00581263 /* LexicalModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModel.swift; sourceTree = "<group>"; };
|
||||
9A079E42223B61AE00581263 /* FullLexicalModelID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullLexicalModelID.swift; sourceTree = "<group>"; };
|
||||
9A082558227589360051EBB0 /* Formatter+ISODateExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Formatter+ISODateExtension.swift"; sourceTree = "<group>"; };
|
||||
9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InstalledLanguagesViewController.swift; path = Settings/InstalledLanguagesViewController.swift; sourceTree = "<group>"; };
|
||||
9A0FC9FC22D66D9E00D33F86 /* Reachability.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Reachability.framework; path = ../../Carthage/Build/iOS/Reachability.framework; sourceTree = "<group>"; };
|
||||
9A3B14D1229370B20052A11F /* InstalledLanguagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InstalledLanguagesViewController.swift; path = Settings/InstalledLanguagesViewController.swift; sourceTree = "<group>"; };
|
||||
9A4609962241B39B00B0BFD1 /* LexicalModelInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelInfoViewController.swift; sourceTree = "<group>"; };
|
||||
9A4609982242047400B0BFD1 /* LexicalModelAPICall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelAPICall.swift; sourceTree = "<group>"; };
|
||||
9A60763C22892485003BCFBA /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Settings.storyboard; path = Settings/Settings.storyboard; sourceTree = "<group>"; };
|
||||
|
|
@ -348,6 +353,8 @@
|
|||
9A9CB0852241717C00231FB9 /* LexicalModelRepositoryDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LexicalModelRepositoryDelegate.swift; sourceTree = "<group>"; };
|
||||
9AD4F53A229F85AC007992D3 /* LanguageSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LanguageSettingsViewController.swift; path = Settings/LanguageSettingsViewController.swift; sourceTree = "<group>"; };
|
||||
9AD4F53B229F85AC007992D3 /* LanguageSettingsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = LanguageSettingsViewController.xib; path = Settings/LanguageSettingsViewController.xib; sourceTree = "<group>"; };
|
||||
9ADC459B22E1895D004C78C6 /* LanguageLMDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageLMDetailViewController.swift; sourceTree = "<group>"; };
|
||||
9ADC459C22E1895D004C78C6 /* LanguageLMDetailViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LanguageLMDetailViewController.xib; sourceTree = "<group>"; };
|
||||
C007C4641F9F52D8006461B9 /* LanguagesAPICall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguagesAPICall.swift; sourceTree = "<group>"; };
|
||||
C024C9931FA6EB470060583B /* NotificationName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationName.swift; sourceTree = "<group>"; };
|
||||
C024C9951FA6EC650060583B /* NotificationCenter+Typed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationCenter+Typed.swift"; sourceTree = "<group>"; };
|
||||
|
|
@ -801,6 +808,8 @@
|
|||
C08C62161F67CFB800268D03 /* KeyboardPickerViewController.swift */,
|
||||
9A9CB07F22416E5400231FB9 /* LexicalModelPickerViewController.swift */,
|
||||
C092D8381F6A70C8005C5485 /* LanguageDetailViewController.swift */,
|
||||
9ADC459B22E1895D004C78C6 /* LanguageLMDetailViewController.swift */,
|
||||
9ADC459C22E1895D004C78C6 /* LanguageLMDetailViewController.xib */,
|
||||
C04C2A6C1F6B7D9A00BA42B6 /* LanguageViewController.swift */,
|
||||
9A7EEFA122DCF64F00877C22 /* LanguageSpecificViewController.swift */,
|
||||
98C0B11817A22DB5007ECAFC /* PickerButtons */,
|
||||
|
|
@ -1030,6 +1039,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9AD4F53F22A8A286007992D3 /* LanguageSettingsViewController.xib in Resources */,
|
||||
9ADC45A022E1895D004C78C6 /* LanguageLMDetailViewController.xib in Resources */,
|
||||
9A0FCA0922D7C58B00D33F86 /* Keyman.bundle in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -1039,6 +1049,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C06D37601F82095200F61AE0 /* Keyman.bundle in Resources */,
|
||||
9ADC459F22E1895D004C78C6 /* LanguageLMDetailViewController.xib in Resources */,
|
||||
9AD4F53D229F85AC007992D3 /* LanguageSettingsViewController.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
@ -1145,6 +1156,7 @@
|
|||
9A31E2C8224AE87600D9A491 /* RegisteredFont.swift in Sources */,
|
||||
9A5091B422B419B10094B99C /* LanguageViewController.swift in Sources */,
|
||||
9A079E352236235700581263 /* Version.swift in Sources */,
|
||||
9ADC459E22E1895D004C78C6 /* LanguageLMDetailViewController.swift in Sources */,
|
||||
9A079DDC2231A11000581263 /* Font.swift in Sources */,
|
||||
9A079E49223F176100581263 /* String+Helpers.swift in Sources */,
|
||||
9A079DE82236138800581263 /* HTTPDownloadRequest.swift in Sources */,
|
||||
|
|
@ -1170,7 +1182,7 @@
|
|||
9A5091B222B418A90094B99C /* KeyboardPickerViewController.swift in Sources */,
|
||||
9A31E2C2224AAD9A00D9A491 /* Notifications.swift in Sources */,
|
||||
9A079DDD2231A11C00581263 /* InstallableKeyboard.swift in Sources */,
|
||||
9A7EEFA222DCF64F00877C22 /* LanguageSpecificViewController.swift in Sources */,
|
||||
9ADC459A22E186F5004C78C6 /* LanguageSpecificViewController.swift in Sources */,
|
||||
9A079DD2223194B100581263 /* KeymanEngineTests.swift in Sources */,
|
||||
9A079E41223B602B00581263 /* LexicalModel.swift in Sources */,
|
||||
9A3B14C0228F59490052A11F /* SettingsViewController.swift in Sources */,
|
||||
|
|
@ -1241,6 +1253,7 @@
|
|||
C06D37381F81F5C400F61AE0 /* SubKeysView.swift in Sources */,
|
||||
C0A93A541F8B21240079948B /* Manager.swift in Sources */,
|
||||
C024C9981FA6F2340060583B /* NotificationObserver.swift in Sources */,
|
||||
9ADC459D22E1895D004C78C6 /* LanguageLMDetailViewController.swift in Sources */,
|
||||
C06D373E1F81F5C400F61AE0 /* KeyboardInfoViewController.swift in Sources */,
|
||||
C0B901AA1FA1AFC200764EB8 /* UserDefaults+Types.swift in Sources */,
|
||||
9A079E372238680700581263 /* KMPLexicalModel.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -179,8 +179,10 @@ open class InputViewController: UIInputViewController, KeymanWebDelegate {
|
|||
open override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
// Necessary for existing infrastructure to resend info for the keyboard after reloading
|
||||
// as system keyboard.
|
||||
Manager.shared.shouldReloadKeyboard = true
|
||||
// as system keyboard. Do NOT perform if in-app, as this unnecessarily resets the WebView.
|
||||
if(Manager.shared.isSystemKeyboard) {
|
||||
Manager.shared.shouldReloadKeyboard = true
|
||||
}
|
||||
}
|
||||
|
||||
open override func textDidChange(_ textInput: UITextInput?) {
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ class KeyboardNameTableViewCell: UITableViewCell, UIAlertViewDelegate {
|
|||
}
|
||||
self.accessoryType = accessoryType
|
||||
|
||||
let isSelectedView = viewWithTag(cellSelectedTag)
|
||||
let selectedView = viewWithTag(cellSelectedTag)
|
||||
if isSelected {
|
||||
if isSelectedView == nil {
|
||||
if selectedView == nil {
|
||||
let view = UIView(frame: CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height))
|
||||
view.tag = cellSelectedTag
|
||||
view.backgroundColor = UIColor.green
|
||||
|
|
@ -39,7 +39,7 @@ class KeyboardNameTableViewCell: UITableViewCell, UIAlertViewDelegate {
|
|||
addSubview(view)
|
||||
}
|
||||
} else {
|
||||
isSelectedView?.removeFromSuperview()
|
||||
selectedView?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,9 +50,11 @@ class LanguageDetailViewController: UITableViewController, UIAlertViewDelegate {
|
|||
override open func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
log.info("didAppear: LanguageDetailViewController (actually willAppear)")
|
||||
log.info("willAppear: LanguageDetailViewController")
|
||||
}
|
||||
|
||||
// MARK: - Table view data source UITableViewDataSource
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return language.keyboards!.count
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,201 @@
|
|||
//
|
||||
// LanguageLMDetailViewController.swift
|
||||
// KeymanEngine
|
||||
//
|
||||
// Created by Randy Boring on 7/19/19.
|
||||
// Copyright © 2019 SIL International. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
import UIKit
|
||||
|
||||
private let toolbarButtonTag = 100
|
||||
private let toolbarLabelTag = 101
|
||||
private let toolbarActivityIndicatorTag = 102
|
||||
|
||||
class LanguageLMDetailViewController: UITableViewController, UIAlertViewDelegate {
|
||||
private var userLexicalModels: [String: InstallableLexicalModel] = [:]
|
||||
private var isUpdate = false // unused currently, may be used when we switch to HTTPDownloader
|
||||
private let language: Language
|
||||
public var lexicalModels: [LexicalModel]? = nil
|
||||
|
||||
private var lexicalModelDownloadStartedObserver: NotificationObserver?
|
||||
//NOTE: there is no need for a CompletedObserver, as our parent LexicalModelPickerViewController
|
||||
// is registered for that and deals with it by popping us out to root.
|
||||
private var lexicalModelDownloadFailedObserver: NotificationObserver?
|
||||
|
||||
init(language: Language) {
|
||||
self.language = language
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
super.loadView()
|
||||
loadUserLexicalModels()
|
||||
tableView.dataSource = self
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
lexicalModelDownloadStartedObserver = NotificationCenter.default.addObserver(
|
||||
forName: Notifications.lexicalModelDownloadStarted,
|
||||
observer: self,
|
||||
function: LanguageLMDetailViewController.lexicalModelDownloadStarted)
|
||||
lexicalModelDownloadFailedObserver = NotificationCenter.default.addObserver(
|
||||
forName: Notifications.lexicalModelDownloadFailed,
|
||||
observer: self,
|
||||
function: LanguageLMDetailViewController.lexicalModelDownloadFailed)
|
||||
log.info("viewDidLoad: LanguageLMDetailViewController (registered for lexicalModelDownloadStarted)")
|
||||
}
|
||||
|
||||
override open func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
log.info("willAppear: LanguageLMDetailViewController")
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
log.info("didAppear: LanguageLMDetailViewController")
|
||||
|
||||
navigationController?.setToolbarHidden(true, animated: true)
|
||||
}
|
||||
|
||||
// MARK: - Table view data source UITableViewDataSource
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return lexicalModels!.count
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cellIdentifier = "Cell"
|
||||
if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) {
|
||||
return cell
|
||||
}
|
||||
|
||||
let cell = KeyboardNameTableViewCell(style: .subtitle, reuseIdentifier: cellIdentifier)
|
||||
let selectionColor = UIView()
|
||||
selectionColor.backgroundColor = UIColor(red: 204.0 / 255.0, green: 136.0 / 255.0, blue: 34.0 / 255.0, alpha: 1.0)
|
||||
cell.selectedBackgroundView = selectionColor
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
let lexicalModel = lexicalModels![indexPath.section]
|
||||
let cell = cell as! KeyboardNameTableViewCell
|
||||
cell.indexPath = indexPath
|
||||
cell.textLabel?.text = lexicalModel.name
|
||||
if isAdded(languageID: language.id, lexicalModelID: lexicalModel.id) {
|
||||
cell.accessoryType = .checkmark
|
||||
cell.isUserInteractionEnabled = false
|
||||
cell.textLabel?.isEnabled = false
|
||||
cell.detailTextLabel?.isEnabled = false
|
||||
} else {
|
||||
cell.accessoryType = .none
|
||||
cell.isUserInteractionEnabled = true
|
||||
cell.textLabel?.isEnabled = true
|
||||
cell.detailTextLabel?.isEnabled = true
|
||||
}
|
||||
|
||||
let kbState = Manager.shared.stateForLexicalModel(withID: lexicalModel.id)
|
||||
cell.setKeyboardState(kbState, selected: false, defaultAccessoryType: cell.accessoryType)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.cellForRow(at: indexPath)?.isSelected = false
|
||||
let lexicalModelIndex = indexPath.section
|
||||
let lexicalModel = lexicalModels![lexicalModelIndex]
|
||||
|
||||
let state = Manager.shared.stateForLexicalModel(withID: lexicalModel.id)
|
||||
if state != .downloading {
|
||||
if state == .needsDownload {
|
||||
isUpdate = false
|
||||
} else {
|
||||
isUpdate = true
|
||||
}
|
||||
|
||||
let alertController = UIAlertController(title: "\(language.name): \(lexicalModel.name)",
|
||||
message: "Would you like to download this dictionary?",
|
||||
preferredStyle: UIAlertControllerStyle.alert)
|
||||
alertController.addAction(UIAlertAction(title: "Cancel",
|
||||
style: UIAlertActionStyle.cancel,
|
||||
handler: nil))
|
||||
alertController.addAction(UIAlertAction(title: "Download",
|
||||
style: UIAlertActionStyle.default,
|
||||
handler: {_ in self.downloadHandler(lexicalModelIndex)} ))
|
||||
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
func downloadHandler(_ lexicalModelIndex: Int) {
|
||||
Manager.shared.downloadLexicalModelPackage(string: (lexicalModels?[lexicalModelIndex].packageFilename)!)
|
||||
}
|
||||
|
||||
private func lexicalModelDownloadStarted() {
|
||||
log.info("lexicalModelDownloadStarted: LanguageLMDetailViewController")
|
||||
view.isUserInteractionEnabled = false
|
||||
navigationItem.setHidesBackButton(true, animated: true)
|
||||
|
||||
let toolbarFrame = navigationController!.toolbar.frame
|
||||
let labelFrame = CGRect(origin: toolbarFrame.origin,
|
||||
size: CGSize(width: toolbarFrame.width * 0.95, height: toolbarFrame.height * 0.7))
|
||||
let label = UILabel(frame: labelFrame)
|
||||
label.backgroundColor = UIColor.clear
|
||||
label.textColor = UIColor.white
|
||||
label.textAlignment = .center
|
||||
label.center = CGPoint(x: toolbarFrame.width * 0.5, y: toolbarFrame.height * 0.5)
|
||||
label.text = "Downloading\u{2026}"
|
||||
label.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin,
|
||||
.flexibleBottomMargin, .flexibleWidth, .flexibleHeight]
|
||||
label.tag = toolbarLabelTag
|
||||
|
||||
let indicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
|
||||
indicatorView.center = CGPoint(x: toolbarFrame.width - indicatorView.frame.width,
|
||||
y: toolbarFrame.height * 0.5)
|
||||
indicatorView.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin, .flexibleBottomMargin]
|
||||
indicatorView.tag = toolbarActivityIndicatorTag
|
||||
indicatorView.startAnimating()
|
||||
|
||||
navigationController?.toolbar.viewWithTag(toolbarButtonTag)?.removeFromSuperview()
|
||||
navigationController?.toolbar.viewWithTag(toolbarLabelTag)?.removeFromSuperview()
|
||||
navigationController?.toolbar.viewWithTag(toolbarActivityIndicatorTag)?.removeFromSuperview()
|
||||
navigationController?.toolbar.addSubview(label)
|
||||
navigationController?.toolbar.addSubview(indicatorView)
|
||||
navigationController?.setToolbarHidden(false, animated: true)
|
||||
}
|
||||
|
||||
private func lexicalModelDownloadFailed() {
|
||||
log.info("lexicalModelDownloadFailed: LanguageLMDetailViewController")
|
||||
view.isUserInteractionEnabled = true
|
||||
navigationItem.setHidesBackButton(false, animated: true)
|
||||
}
|
||||
|
||||
private func loadUserLexicalModels() {
|
||||
guard let userLmList = Storage.active.userDefaults.userLexicalModels, !userLmList.isEmpty else {
|
||||
userLexicalModels = [:]
|
||||
return
|
||||
}
|
||||
|
||||
userLexicalModels = [:]
|
||||
for lm in userLmList {
|
||||
let dictKey = "\(lm.languageID)_\(lm.id)"
|
||||
userLexicalModels[dictKey] = lm
|
||||
}
|
||||
}
|
||||
|
||||
private func isAdded(languageID: String?, lexicalModelID: String?) -> Bool {
|
||||
guard let languageID = languageID, let lexicalModelID = lexicalModelID else {
|
||||
return false
|
||||
}
|
||||
return userLexicalModels["\(languageID)_\(lexicalModelID)"] != nil
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="LanguageLMDetailViewController" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableView opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" bouncesZoom="NO" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="i5M-Pr-FkT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="vLr-E1-eTs"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="-1" id="Tng-2m-Rnh"/>
|
||||
<outlet property="delegate" destination="-1" id="9aC-8N-iBw"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -101,7 +101,9 @@ class LanguageViewController: UITableViewController, UIAlertViewDelegate {
|
|||
} else {
|
||||
log.info("didAppear: LanguageViewController")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Table view data source UITableViewDataSource
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return languages.count
|
||||
|
|
@ -242,7 +244,7 @@ class LanguageViewController: UITableViewController, UIAlertViewDelegate {
|
|||
func downloadHandler(_ keyboardIndex: Int) {
|
||||
let language = languages[selectedSection]
|
||||
let keyboard = language.keyboards![keyboardIndex]
|
||||
Manager.shared.downloadKeyboard(withID: keyboard.id, languageID: language.id, isUpdate: isUpdate)
|
||||
Manager.shared.downloadKeyboard(withID: keyboard.id, languageID: language.id, isUpdate: isUpdate)
|
||||
}
|
||||
|
||||
private func keyboardDownloadStarted() {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate
|
|||
|
||||
infoArray = [[String: String]]()
|
||||
infoArray.append([
|
||||
"title": "Model version",
|
||||
"title": "Dictionary version",
|
||||
"subtitle": lexicalModelVersion
|
||||
])
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate
|
|||
])
|
||||
}
|
||||
infoArray.append([
|
||||
"title": "Uninstall model",
|
||||
"title": "Uninstall dictionary",
|
||||
"subtitle": ""
|
||||
])
|
||||
}
|
||||
|
|
@ -135,7 +135,7 @@ class LexicalModelInfoViewController: UITableViewController, UIAlertViewDelegate
|
|||
}
|
||||
|
||||
private func showDeleteLexicalModel() {
|
||||
let alertController = UIAlertController(title: title ?? "", message: "Would you like to delete this lexical model?",
|
||||
let alertController = UIAlertController(title: title ?? "", message: "Would you like to delete this dictionary?",
|
||||
preferredStyle: UIAlertControllerStyle.alert)
|
||||
alertController.addAction(UIAlertAction(title: "Cancel",
|
||||
style: UIAlertActionStyle.cancel,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ private let toolbarActivityIndicatorTag = 102
|
|||
|
||||
class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelegate {
|
||||
private var userLexicalModels: [InstallableLexicalModel] = [InstallableLexicalModel]()
|
||||
public var language: Language? = nil
|
||||
public var language: Language!
|
||||
private var updateQueue: [InstallableLexicalModel]?
|
||||
private var _isDoneButtonEnabled = false
|
||||
private var isDidUpdateCheck = false
|
||||
|
|
@ -24,17 +24,19 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
private var lexicalModelDownloadCompletedObserver: NotificationObserver?
|
||||
private var lexicalModelDownloadFailedObserver: NotificationObserver?
|
||||
|
||||
public init(_ language: Language) {
|
||||
self.language = language
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
if let langName = language?.name {
|
||||
title = "\(langName) Lexical Models"
|
||||
} else {
|
||||
title = "Lexical Models"
|
||||
}
|
||||
if language == nil {
|
||||
setIsDoneButtonEnabled(false)
|
||||
}
|
||||
title = "\(language.name) Dictionaries"
|
||||
isDidUpdateCheck = false
|
||||
updateQueue = nil
|
||||
if Manager.shared.canAddNewLexicalModels {
|
||||
|
|
@ -137,7 +139,6 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
if Manager.shared.removeLexicalModel(at: globalIndex) {
|
||||
loadUserLexicalModels()
|
||||
}
|
||||
setIsDoneButtonEnabled(true)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView,
|
||||
|
|
@ -146,9 +147,6 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
}
|
||||
|
||||
func showLexicalModelInfoView(with index: Int) {
|
||||
if language == nil {
|
||||
setIsDoneButtonEnabled(true)
|
||||
}
|
||||
let lm = userLexicalModels[index]
|
||||
let version = lm.version
|
||||
|
||||
|
|
@ -178,7 +176,7 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
if Manager.shared.currentLexicalModelID == lm.fullID {
|
||||
cell.selectionStyle = .blue
|
||||
cell.isSelected = true
|
||||
cell.accessoryType = .checkmark
|
||||
cell.accessoryType = .detailDisclosureButton
|
||||
} else {
|
||||
cell.selectionStyle = .none
|
||||
cell.isSelected = false
|
||||
|
|
@ -236,14 +234,14 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
}
|
||||
updateQueue = nil
|
||||
let label = navigationController?.toolbar?.viewWithTag(toolbarLabelTag) as? UILabel
|
||||
label?.text = "Lexical models successfully updated!"
|
||||
label?.text = "Dictionaries successfully updated!"
|
||||
navigationController?.toolbar?.viewWithTag(toolbarActivityIndicatorTag)?.removeFromSuperview()
|
||||
Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(self.hideToolbarDelayed),
|
||||
userInfo: nil, repeats: false)
|
||||
}
|
||||
} else {
|
||||
let label = navigationController?.toolbar?.viewWithTag(toolbarLabelTag) as? UILabel
|
||||
label?.text = "Lexical model successfully downloaded!"
|
||||
label?.text = "Dictionary successfully downloaded!"
|
||||
navigationController?.toolbar?.viewWithTag(toolbarActivityIndicatorTag)?.removeFromSuperview()
|
||||
Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(self.hideToolbarDelayed),
|
||||
userInfo: nil, repeats: false)
|
||||
|
|
@ -276,9 +274,9 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
let title: String
|
||||
if view == navigationController?.topViewController?.view {
|
||||
updateQueue = nil
|
||||
title = "Lexical model Update Error"
|
||||
title = "Dictionary Update Error"
|
||||
} else {
|
||||
title = "Lexical model Download Error"
|
||||
title = "Dictionary Download Error"
|
||||
}
|
||||
navigationController?.setToolbarHidden(true, animated: true)
|
||||
|
||||
|
|
@ -399,8 +397,8 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
}
|
||||
|
||||
private func scroll(toSelectedLexicalModel animated: Bool) {
|
||||
let index = userLexicalModels.index { kb in
|
||||
return Manager.shared.currentLexicalModelID == kb.fullID
|
||||
let index = userLexicalModels.index { lm in
|
||||
return Manager.shared.currentLexicalModelID == lm.fullID
|
||||
}
|
||||
|
||||
if let index = index {
|
||||
|
|
@ -428,13 +426,32 @@ class LexicalModelPickerViewController: UITableViewController, UIAlertViewDelega
|
|||
}
|
||||
|
||||
func showAddLexicalModel() {
|
||||
let button: UIButton? = (navigationController?.toolbar?.viewWithTag(toolbarButtonTag) as? UIButton)
|
||||
button?.isEnabled = false
|
||||
let vc = LanguageViewController(Manager.shared.apiLexicalModelRepository) //may need to be different for models
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
if language == nil {
|
||||
setIsDoneButtonEnabled(true)
|
||||
//get list of lexical models for this languageID and show it
|
||||
func listCompletionHandler(lexicalModels: [LexicalModel]?, error: Error?) -> Void {
|
||||
if let error = error {
|
||||
log.info("Failed to fetch lexical model list for "+language.id+". error: "+(error as! String))
|
||||
DispatchQueue.main.async {
|
||||
self.lexicalModelDownloadFailed(LexicalModelDownloadFailedNotification(lmOrLanguageID: self.language.id, error: error))
|
||||
}
|
||||
} else if nil == lexicalModels {
|
||||
log.info("No lexical models available for language \(language.id) (nil)")
|
||||
} else if 0 == lexicalModels?.count {
|
||||
log.info("No lexical models available for language \(language.id) (empty)")
|
||||
} else {
|
||||
log.info("Fetched lexical model list for "+language.id+".")
|
||||
// show the list of lexical models (on the main thread)
|
||||
DispatchQueue.main.async {
|
||||
let button: UIButton? = (self.navigationController?.toolbar?.viewWithTag(toolbarButtonTag) as? UIButton)
|
||||
button?.isEnabled = false
|
||||
let vc = LanguageLMDetailViewController(language: self.language)
|
||||
vc.lexicalModels = lexicalModels!
|
||||
self.navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Manager.shared.apiLexicalModelRepository.fetchList(languageID: language.id, completionHandler: listCompletionHandler)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ typealias FetchKeyboardsBlock = ([String: Any]?) -> Void
|
|||
|
||||
// MARK: - Constants
|
||||
|
||||
// Possible states that a keyboard can be in
|
||||
// Possible states that a keyboard or lexical model can be in
|
||||
public enum KeyboardState {
|
||||
case needsDownload
|
||||
case needsUpdate
|
||||
|
|
@ -26,15 +26,6 @@ public enum KeyboardState {
|
|||
case none
|
||||
}
|
||||
|
||||
// Possible states that a lexical model can be in
|
||||
public enum LexicalModelState {
|
||||
case needsDownload
|
||||
case needsUpdate
|
||||
case upToDate
|
||||
case downloading
|
||||
case none
|
||||
}
|
||||
|
||||
public enum VibrationSupport {
|
||||
case none // Has no vibrator
|
||||
case basic // Has only the basic 0.4 sec long vibration
|
||||
|
|
@ -313,6 +304,8 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat
|
|||
// If we have a lexical model for the keyboard's language, activate it.
|
||||
if let preferred_model = preferredLexicalModel(userDefaults, forLanguage: kb.languageID) {
|
||||
_ = Manager.shared.registerLexicalModel(preferred_model)
|
||||
} else if let first_model = userDefaults.userLexicalModels?.first(where: { $0.languageID == kb.languageID }) {
|
||||
_ = Manager.shared.registerLexicalModel(first_model)
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
@ -482,30 +475,50 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat
|
|||
}
|
||||
|
||||
/// Removes the lexical model at index from the lexical models list if it exists.
|
||||
/// - Returns: The lexical model exists and was removed
|
||||
public func removeLexicalModel(at index: Int) -> Bool {
|
||||
let userData = Storage.active.userDefaults
|
||||
|
||||
public func removeLexicalModelFromUserList(userDefs ud: UserDefaults, at index: Int) -> InstallableLexicalModel? {
|
||||
// If user defaults for lexical models list does not exist, do nothing.
|
||||
guard var userLexicalModels = userData.userLexicalModels else {
|
||||
return false
|
||||
guard var userLexicalModels = ud.userLexicalModels else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard index < userLexicalModels.count else {
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
let lm = userLexicalModels[index]
|
||||
log.info("Removing lexical model with ID \(lm.id) and languageID \(lm.languageID) from user list of all models")
|
||||
userLexicalModels.remove(at: index)
|
||||
userData.userLexicalModels = userLexicalModels
|
||||
userData.set([Date()], forKey: Key.synchronizeSWLexicalModel)
|
||||
userData.synchronize()
|
||||
|
||||
log.info("Removing lexical model with ID \(lm.id) and languageID \(lm.languageID)")
|
||||
ud.userLexicalModels = userLexicalModels
|
||||
ud.set([Date()], forKey: Key.synchronizeSWLexicalModel)
|
||||
ud.synchronize()
|
||||
return lm
|
||||
}
|
||||
|
||||
/// Removes the lexical model at index from the lexical models list if it exists.
|
||||
public func removeLexicalModelFromLanguagePreference(userDefs ud: UserDefaults, _ lm: InstallableLexicalModel) {
|
||||
log.info("Removing lexical model with ID \(lm.id) and languageID \(lm.languageID) from per-language prefs")
|
||||
ud.set(preferredLexicalModelID: nil, forKey: lm.languageID)
|
||||
}
|
||||
|
||||
/// Removes the lexical model at index from the lexical models list if it exists.
|
||||
/// - Returns: The lexical model exists and was removed
|
||||
public func removeLexicalModel(at index: Int) -> Bool {
|
||||
let userData = Storage.active.userDefaults
|
||||
guard let lm = removeLexicalModelFromUserList(userDefs: userData, at: index) else {
|
||||
return false
|
||||
}
|
||||
|
||||
removeLexicalModelFromLanguagePreference(userDefs: userData, lm)
|
||||
// Set a new lexical model if deleting the current one
|
||||
let userLexicalModels = userData.userLexicalModels! //removeLexicalModelFromUserList fails above if this is not present
|
||||
|
||||
if lm.fullID == currentLexicalModelID {
|
||||
_ = registerLexicalModel(userLexicalModels[0])
|
||||
if let first_lm = userLexicalModels.first(where: {$0.languageID == lm.languageID}) {
|
||||
_ = registerLexicalModel(first_lm)
|
||||
} else {
|
||||
log.info("no more lexical models available for language \(lm.fullID)")
|
||||
currentLexicalModelID = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !userLexicalModels.contains(where: { $0.id == lm.id }) {
|
||||
|
|
@ -1076,6 +1089,7 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat
|
|||
log.info("Failed to fetch lexical model list for "+languageID+". error: "+(error as! String))
|
||||
self.downloadFailed(forLanguageID: languageID, error: error) //???forKeyboards
|
||||
} else if nil == lexicalModels {
|
||||
//TODO: put up an alert instead
|
||||
log.info("No lexical models available for language \(languageID) (nil)")
|
||||
} else if 0 == lexicalModels?.count {
|
||||
log.info("No lexical models available for language \(languageID) (empty)")
|
||||
|
|
@ -1276,7 +1290,8 @@ public class Manager: NSObject, HTTPDownloadDelegate, UIGestureRecognizerDelegat
|
|||
}
|
||||
|
||||
/// - Returns: The current state for a lexical model
|
||||
public func stateForLexicalModel(withID lexicalModelID: String) -> LexicalModelState {
|
||||
//TODO: rename KeyboardState to ResourceState? so it can be used with both keybaoards and lexical models without confusion
|
||||
public func stateForLexicalModel(withID lexicalModelID: String) -> KeyboardState {
|
||||
if lexicalModelIdForCurrentRequest() == lexicalModelID {
|
||||
return .downloading
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import Foundation
|
||||
|
||||
struct InstallableConstants {
|
||||
static let defaultVersion = "0.1.0"
|
||||
static let defaultVersion = "1.0"
|
||||
}
|
||||
|
||||
/// Mainly differs from the API `LexicalModel` by having an associated language.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ public class LexicalModelKeymanPackage : KeymanPackage {
|
|||
public var models : [KMPLexicalModel]!
|
||||
|
||||
public override func defaultInfoHtml() -> String {
|
||||
var str = "Found lexical models in package:<br/>"
|
||||
let count = models.count
|
||||
var str = "Found "+(count > 1 ? "\(count) dictionaries" : "dictionary")+" in package:<br/>"
|
||||
for model in models {
|
||||
str += model.lexicalModelId! + "<br/>"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega
|
|||
private var sectionIndexTitles: [String] = []
|
||||
private var indices: [Int] = []
|
||||
private var selectedSection = 0
|
||||
private var isUpdate = false
|
||||
private var installedLanguages: [String: Language]
|
||||
private var languages: [Language] = []
|
||||
private let keyboardRepository: KeyboardRepository?
|
||||
|
|
@ -165,49 +164,13 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega
|
|||
}
|
||||
|
||||
cell.textLabel?.text = language.name
|
||||
|
||||
if cell.accessoryType == .disclosureIndicator {
|
||||
return
|
||||
}
|
||||
guard let cell = cell as? KeyboardNameTableViewCell else {
|
||||
return
|
||||
}
|
||||
|
||||
let keyboardID = language.keyboards![0].id
|
||||
|
||||
cell.accessoryType = .none
|
||||
cell.isUserInteractionEnabled = true
|
||||
cell.textLabel?.isEnabled = true
|
||||
cell.detailTextLabel?.isEnabled = true
|
||||
|
||||
let kbState = Manager.shared.stateForKeyboard(withID: keyboardID)
|
||||
cell.setKeyboardState(kbState, selected: false, defaultAccessoryType: cell.accessoryType)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
selectedSection = indexPath.section
|
||||
tableView.cellForRow(at: indexPath)?.isSelected = false
|
||||
if tableView.cellForRow(at: indexPath)?.accessoryType == .disclosureIndicator {
|
||||
let title = tableView.cellForRow(at: indexPath)?.textLabel?.text ?? ""
|
||||
showLanguageSettingsView(title: title, languageIndex: indexPath.section)
|
||||
return
|
||||
}
|
||||
|
||||
let language = languages[indexPath.section]
|
||||
let keyboardIndex = 0;
|
||||
let keyboard = language.keyboards![keyboardIndex]
|
||||
|
||||
//TODO: launch real Language Settings controller here
|
||||
let languageSettingsController = UIAlertController(title: "\(language.name): \(keyboard.name)",
|
||||
message: "This should be Language Settings",
|
||||
preferredStyle: UIAlertControllerStyle.alert)
|
||||
languageSettingsController.addAction(UIAlertAction(title: "Cancel",
|
||||
style: UIAlertActionStyle.cancel,
|
||||
handler: nil))
|
||||
// languageSettingsController.addAction(UIAlertAction(title: "Download",
|
||||
// style: UIAlertActionStyle.default,
|
||||
// handler: {_ in self.downloadHandler(keyboardIndex)} ))
|
||||
self.present(languageSettingsController, animated: true, completion: nil)
|
||||
let title = tableView.cellForRow(at: indexPath)?.textLabel?.text ?? ""
|
||||
showLanguageSettingsView(title: title, languageIndex: indexPath.section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
|
||||
|
|
@ -230,7 +193,7 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega
|
|||
func downloadHandler(_ keyboardIndex: Int) {
|
||||
let language = languages[selectedSection]
|
||||
let keyboard = language.keyboards![keyboardIndex]
|
||||
Manager.shared.downloadKeyboard(withID: keyboard.id, languageID: language.id, isUpdate: isUpdate)
|
||||
Manager.shared.downloadKeyboard(withID: keyboard.id, languageID: language.id, isUpdate: false)
|
||||
}
|
||||
|
||||
private func restoreNavigation() {
|
||||
|
|
@ -336,7 +299,7 @@ class InstalledLanguagesViewController: UITableViewController, UIAlertViewDelega
|
|||
log.info("lexicalModelDownloadFailed: InstalledLanguagesViewController")
|
||||
restoreNavigation()
|
||||
|
||||
let title = "Lexical Model Download Error"
|
||||
let title = "Dictionary Download Error"
|
||||
navigationController?.setToolbarHidden(true, animated: true)
|
||||
|
||||
let alertController = UIAlertController(title: title, message: "",
|
||||
|
|
|
|||
|
|
@ -153,9 +153,9 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
var title: String
|
||||
switch (section) {
|
||||
case 0:
|
||||
title = "keyboards"
|
||||
title = "Keyboards"
|
||||
case 1:
|
||||
title = "language settings"
|
||||
title = "Language settings"
|
||||
default:
|
||||
title = "unknown header"
|
||||
}
|
||||
|
|
@ -199,19 +199,19 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
case 1:
|
||||
cell.textLabel?.text = "Enable corrections"
|
||||
case 2:
|
||||
cell.textLabel?.text = "Model"
|
||||
cell.textLabel?.text = "Dictionaries"
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
if let modelCt = language.lexicalModels?.count {
|
||||
switch modelCt {
|
||||
case 0:
|
||||
cell.detailTextLabel?.text = "no models installed"
|
||||
cell.detailTextLabel?.text = "no dictionaries installed"
|
||||
case 1:
|
||||
cell.detailTextLabel?.text = "one model installed"
|
||||
cell.detailTextLabel?.text = "\(language.lexicalModels![0].name)"
|
||||
default:
|
||||
cell.detailTextLabel?.text = "\(modelCt) models installed"
|
||||
cell.detailTextLabel?.text = "\(modelCt) dictionaries installed"
|
||||
}
|
||||
} else {
|
||||
cell.detailTextLabel?.text = "no models installed"
|
||||
cell.detailTextLabel?.text = "no dictionaries installed"
|
||||
}
|
||||
case 3: // future
|
||||
cell.textLabel?.text = "Manage dictionary"
|
||||
|
|
@ -297,7 +297,7 @@ class LanguageSettingsViewController: UITableViewController {
|
|||
|
||||
func showLexicalModelsView() {
|
||||
//LanguageLexicalModelPickerViewController? (should show just the models for this language)
|
||||
let lmListView = LexicalModelPickerViewController()
|
||||
let lmListView = LexicalModelPickerViewController(self.language)
|
||||
lmListView.language = self.language
|
||||
navigationController?.pushViewController(lmListView, animated: true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ open class SettingsViewController: UITableViewController {
|
|||
super.viewWillAppear(animated)
|
||||
|
||||
loadUserLanguages()
|
||||
log.info("didAppear: SettingsViewController (actually willAppear)")
|
||||
log.info("willAppear: SettingsViewController")
|
||||
}
|
||||
|
||||
override open func viewDidLoad() {
|
||||
|
|
@ -256,7 +256,13 @@ open class SettingsViewController: UITableViewController {
|
|||
}
|
||||
|
||||
// MARK: - language access -
|
||||
private func installed2API(_ installedList: [InstallableLexicalModel]) -> [LexicalModel] {
|
||||
|
||||
/** returns an array of LexicalModel created from an array of InstallableLexicalModel
|
||||
* @param installedList: The InstallableLexicalModel are probably already installed
|
||||
* The returned LexicalModels are not complete (usually we go the other way round)
|
||||
* but sufficient for future API calls
|
||||
*/
|
||||
public static func installed2API(_ installedList: [InstallableLexicalModel]) -> [LexicalModel] {
|
||||
var returnList = [LexicalModel]()
|
||||
for ilm in installedList {
|
||||
returnList.append(LexicalModel(id: ilm.id, name: ilm.name, license: "", version: ilm.version, languages: [], authorName: "", fileSize: 0, filename: "no filename", sourcePath: nil, authorEmail: nil, description: nil, packageFileSize: 0, packageFilename: "", packageIncludes: nil, isDefault: false, lastModified: nil, minKeymanVersion: nil))
|
||||
|
|
@ -283,7 +289,7 @@ open class SettingsViewController: UITableViewController {
|
|||
}
|
||||
let userDefaults : UserDefaults = Storage.active.userDefaults
|
||||
let lmListInstalled: [InstallableLexicalModel] = userDefaults.userLexicalModelsForLanguage(languageID: l) ?? []
|
||||
let lmList = installed2API(lmListInstalled)
|
||||
let lmList = SettingsViewController.installed2API(lmListInstalled)
|
||||
keyboardLanguages[l] = Language(name: k.languageName, id: k.languageID, keyboards: kbds, lexicalModels: lmList, font: nil, oskFont: nil)
|
||||
}
|
||||
// there shouldn't be any lexical models for languages that don't have a keyboard installed
|
||||
|
|
|
|||
46
web/package-lock.json
generated
46
web/package-lock.json
generated
|
|
@ -73,13 +73,13 @@
|
|||
"arraybuffer.slice": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
|
||||
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
|
||||
"integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=",
|
||||
"dev": true
|
||||
},
|
||||
"assertion-error": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
|
||||
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
|
||||
"integrity": "sha1-5gtrDo8wG9l+U3UhW9pAbIURjAs=",
|
||||
"dev": true
|
||||
},
|
||||
"async": {
|
||||
|
|
@ -94,7 +94,7 @@
|
|||
"async-limiter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
|
||||
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
|
||||
"integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=",
|
||||
"dev": true
|
||||
},
|
||||
"backo2": {
|
||||
|
|
@ -367,7 +367,7 @@
|
|||
"color-convert": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
|
||||
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
|
||||
"integrity": "sha1-wSYRB66y8pTr/+ye2eytUppgl+0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "^1.1.1"
|
||||
|
|
@ -424,7 +424,7 @@
|
|||
"content-type": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
||||
"integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=",
|
||||
"dev": true
|
||||
},
|
||||
"cookie": {
|
||||
|
|
@ -484,7 +484,7 @@
|
|||
"deep-eql": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
|
||||
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
|
||||
"integrity": "sha1-38lARACtHI/gI+faHfHBR8S0RN8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-detect": "^4.0.0"
|
||||
|
|
@ -872,7 +872,7 @@
|
|||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
|
|
@ -1217,7 +1217,7 @@
|
|||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
|
@ -1242,7 +1242,7 @@
|
|||
"karma-chrome-launcher": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz",
|
||||
"integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==",
|
||||
"integrity": "sha1-zxudBxNswY/iOTJ9JGVMPbw2is8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs-access": "^1.0.0",
|
||||
|
|
@ -1252,7 +1252,7 @@
|
|||
"karma-edge-launcher": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/karma-edge-launcher/-/karma-edge-launcher-0.4.2.tgz",
|
||||
"integrity": "sha512-YAJZb1fmRcxNhMIWYsjLuxwODBjh2cSHgTW/jkVmdpGguJjLbs9ZgIK/tEJsMQcBLUkO+yO4LBbqYxqgGW2HIw==",
|
||||
"integrity": "sha1-PZUpsJsTyQnF887uEtAOf5qYmz0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"edge-launcher": "1.2.2"
|
||||
|
|
@ -1261,7 +1261,7 @@
|
|||
"karma-firefox-launcher": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-1.1.0.tgz",
|
||||
"integrity": "sha512-LbZ5/XlIXLeQ3cqnCbYLn+rOVhuMIK9aZwlP6eOLGzWdo1UVp7t6CN3DP4SafiRLjexKwHeKHDm0c38Mtd3VxA==",
|
||||
"integrity": "sha1-LEcDBFLwRTHrfRPU/HZpYwu5Mzk=",
|
||||
"dev": true
|
||||
},
|
||||
"karma-fixture": {
|
||||
|
|
@ -1328,7 +1328,7 @@
|
|||
"ansi-styles": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
|
||||
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
|
||||
"integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
|
|
@ -1374,7 +1374,7 @@
|
|||
"karma-teamcity-reporter": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/karma-teamcity-reporter/-/karma-teamcity-reporter-1.1.0.tgz",
|
||||
"integrity": "sha512-Ca1uhHGtNqUuzsnW3I+QykNuS/jF9vdxnIrkbLCVJRunCc6yWJq+ai1UobQT13j0e3JVUOf0mKo3QHZ6A6mG9Q==",
|
||||
"integrity": "sha1-eYD33JHmqwhH4SKt4Ls0x8b0KBY=",
|
||||
"dev": true
|
||||
},
|
||||
"linkify-it": {
|
||||
|
|
@ -1405,7 +1405,7 @@
|
|||
"log-symbols": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
|
||||
"integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
|
||||
"integrity": "sha1-V0Dhxdbw39pK2TI7UzIQfva0xAo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.0.1"
|
||||
|
|
@ -1414,7 +1414,7 @@
|
|||
"ansi-styles": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
|
||||
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
|
||||
"integrity": "sha1-wVm41b4PnlpvNG2rlPFs4CIWG4g=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
|
|
@ -1548,7 +1548,7 @@
|
|||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
|
|
@ -1863,7 +1863,7 @@
|
|||
"readable-stream": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
||||
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
|
||||
"integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
|
|
@ -1954,7 +1954,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
|
||||
"integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=",
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
|
|
@ -2198,7 +2198,7 @@
|
|||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
|
|
@ -2258,7 +2258,7 @@
|
|||
"tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
||||
"integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"os-tmpdir": "~1.0.2"
|
||||
|
|
@ -2328,7 +2328,7 @@
|
|||
"ultron": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
|
||||
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
|
||||
"integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=",
|
||||
"dev": true
|
||||
},
|
||||
"universalify": {
|
||||
|
|
@ -2418,7 +2418,7 @@
|
|||
"which": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
|
||||
"integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
|
||||
"integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
|
|
@ -2482,7 +2482,7 @@
|
|||
"ws": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
|
||||
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
|
||||
"integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue