2021-08-23 23:56:36 +00:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
// Root element and localization manager for a Toolkit application
|
|
|
|
Toolkit.Application = class Application extends Toolkit.Panel {
|
|
|
|
|
|
|
|
// Object constructor
|
2021-08-26 19:23:18 +00:00
|
|
|
constructor(options) {
|
|
|
|
super(null, options);
|
2021-08-23 23:56:36 +00:00
|
|
|
|
|
|
|
// Configure instance fields
|
2021-08-26 19:23:18 +00:00
|
|
|
this.application = this;
|
|
|
|
this.components = [];
|
|
|
|
this.locale = null;
|
|
|
|
this.locales = { first: null };
|
|
|
|
this.propagationListeners = [];
|
2021-08-23 23:56:36 +00:00
|
|
|
|
|
|
|
// Configure element
|
|
|
|
this.element.setAttribute("application", "");
|
2021-08-26 19:23:18 +00:00
|
|
|
this.element.addEventListener("mousedown" , e=>this.onpropagation(e));
|
|
|
|
this.element.addEventListener("pointerdown", e=>this.onpropagation(e));
|
2021-08-23 23:56:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////// Public Methods //////////////////////////////
|
|
|
|
|
|
|
|
// Add a component for localization management
|
|
|
|
addComponent(component) {
|
|
|
|
if (this.components.indexOf(component) != -1)
|
|
|
|
return;
|
|
|
|
this.components.push(component);
|
|
|
|
component.localize();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register a locale with the application
|
|
|
|
addLocale(source) {
|
|
|
|
let loc = null;
|
|
|
|
|
|
|
|
// Process the locale object from the source
|
|
|
|
try { loc = new Function("return (" + source + ");")(); }
|
|
|
|
catch(e) { console.log(e); }
|
|
|
|
|
|
|
|
// Error checking
|
|
|
|
if (
|
|
|
|
!loc || typeof loc != "object" ||
|
|
|
|
!("key" in loc) || !("name" in loc)
|
|
|
|
) return null;
|
|
|
|
|
|
|
|
// Register the locale
|
|
|
|
if (this.locales.first == null)
|
|
|
|
this.locales.first = loc;
|
|
|
|
this.locales[loc.key] = loc;
|
|
|
|
return loc.key;
|
|
|
|
}
|
|
|
|
|
2021-08-26 19:23:18 +00:00
|
|
|
// Add a callback for propagation events
|
|
|
|
addPropagationListener(listener) {
|
|
|
|
if (this.propagationListeners.indexOf(listener) == -1)
|
|
|
|
this.propagationListeners.push(listener);
|
|
|
|
}
|
|
|
|
|
2021-08-23 23:56:36 +00:00
|
|
|
// Produce a list of all registered locale keys
|
|
|
|
listLocales() {
|
|
|
|
return Object.values(this.locales);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove a compnent from being localized
|
|
|
|
removeComponent(component) {
|
|
|
|
let index = this.components.indexOf(component);
|
|
|
|
if (index == -1)
|
|
|
|
return false;
|
|
|
|
this.components.splice(index, 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Specify which localized strings to use for application controls
|
|
|
|
setLocale(lang) {
|
|
|
|
|
|
|
|
// Error checking
|
|
|
|
if (this.locales.first == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
// Working variables
|
|
|
|
lang = lang.toLowerCase();
|
|
|
|
let parts = lang.split("-");
|
|
|
|
let best = null;
|
|
|
|
|
|
|
|
// Check all locales
|
|
|
|
for (let loc of Object.values(this.locales)) {
|
|
|
|
let key = loc.key.toLowerCase();
|
|
|
|
|
|
|
|
// The language is an exact match
|
|
|
|
if (key == lang) {
|
|
|
|
best = loc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The language matches, but the region may not
|
|
|
|
if (best == null && key.split("-")[0] == parts[0])
|
|
|
|
best = loc;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The language did not match: use the first locale that was registered
|
|
|
|
if (best == null)
|
|
|
|
best = this.locales.first;
|
|
|
|
|
|
|
|
// Select the locale
|
|
|
|
this.locale = best;
|
|
|
|
return best.key;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Localize text for a component
|
|
|
|
translate(text, properties) {
|
|
|
|
properties = !properties ? {} :
|
|
|
|
properties instanceof Toolkit.Component ? properties.properties :
|
|
|
|
properties;
|
|
|
|
|
|
|
|
// Process all characters from the input
|
|
|
|
let sub = { text: "", parent: null };
|
|
|
|
for (let x = 0; x < text.length; x++) {
|
|
|
|
let c = text[x];
|
|
|
|
let last = x == text.length - 1;
|
|
|
|
|
|
|
|
// Left curly brace
|
|
|
|
if (c == '{') {
|
|
|
|
|
|
|
|
// Literal left curly brace
|
|
|
|
if (!last && text[x + 1] == '{') {
|
|
|
|
sub.text += c;
|
|
|
|
x++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open a substring
|
|
|
|
sub = { text: "", parent: sub };
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Right curly brace
|
|
|
|
if (c == '}') {
|
|
|
|
|
|
|
|
// Literal right curly brace
|
|
|
|
if (!last && text[x + 1] == '}') {
|
|
|
|
sub.text += c;
|
|
|
|
x++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close a sub (if there are any to close)
|
|
|
|
if (sub.parent != null) {
|
|
|
|
|
|
|
|
// Text comes from component property
|
|
|
|
if (sub.text in properties) {
|
|
|
|
sub.parent.text += properties[sub.text];
|
|
|
|
sub = sub.parent;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Text comes from locale
|
|
|
|
let value = this.fromLocale(sub.text, true);
|
|
|
|
if (value !== null) {
|
|
|
|
text = value + text.substring(x + 1);
|
|
|
|
x = -1;
|
|
|
|
sub = sub.parent;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Take the text as-is
|
|
|
|
sub.parent.text += "{" + sub.text + "}";
|
|
|
|
sub = sub.parent;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append the character to the sub's text
|
|
|
|
sub.text += c;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close any remaining subs (should never happen)
|
|
|
|
for (; sub.parent != null; sub = sub.parent)
|
|
|
|
sub.parent.text += sub.text;
|
|
|
|
return sub.text;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////// Private Methods /////////////////////////////
|
|
|
|
|
|
|
|
// Retrieve the text for a key in the locale
|
|
|
|
fromLocale(key) {
|
|
|
|
let locale = this.locale || {};
|
|
|
|
for (let part of key.split(".")) {
|
|
|
|
if (!(part in locale))
|
|
|
|
return null;
|
|
|
|
locale = locale[part];
|
|
|
|
}
|
|
|
|
return typeof locale == "string" ? locale : null;
|
|
|
|
}
|
|
|
|
|
2021-08-26 19:23:18 +00:00
|
|
|
// A pointer or mouse down even has propagated
|
|
|
|
onpropagation(e) {
|
|
|
|
e.stopPropagation();
|
|
|
|
for (let listener of this.propagationListeners)
|
|
|
|
listener(e, this);
|
|
|
|
}
|
|
|
|
|
2021-08-23 23:56:36 +00:00
|
|
|
};
|