pvbemu/app/toolkit/Application.js

209 lines
6.0 KiB
JavaScript

"use strict";
// Root element and localization manager for a Toolkit application
Toolkit.Application = class Application extends Toolkit.Panel {
// Object constructor
constructor(options) {
super(null, options);
// Configure instance fields
this.application = this;
this.components = [];
this.locale = null;
this.locales = { first: null };
this.propagationListeners = [];
// Configure element
this.element.setAttribute("application", "");
this.element.addEventListener("mousedown" , e=>this.onpropagation(e));
this.element.addEventListener("pointerdown", e=>this.onpropagation(e));
}
///////////////////////////// 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;
}
// Add a callback for propagation events
addPropagationListener(listener) {
if (this.propagationListeners.indexOf(listener) == -1)
this.propagationListeners.push(listener);
}
// 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;
}
// A pointer or mouse down even has propagated
onpropagation(e) {
e.stopPropagation();
for (let listener of this.propagationListeners)
listener(e, this);
}
};