VulpesOS/gaia/apps/settings/js/modules/keyboard_context.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

301 lines
8.8 KiB
JavaScript
Executable file

/**
* KeyboardContext provides installed keyboard apps and enabled keyboard layouts
* in terms of ObservableArrays. It listens to the events from KeyboardHelper
* and update the ObservableArrays.
* KeyboardHelper helps on the following things:
* - Get all installed keyboard apps and layouts.
* - Enable or disable keyboard layouts.
* - Notify keyboard layout changes via the 'keyboardsrefresh' event.
* KeyboardContext handles only data and does not involve in any UI logic.
*
* @module KeyboardContext
*/
define(function(require) {
'use strict';
var Observable = require('modules/mvvm/observable');
var ObservableArray = require('modules/mvvm/observable_array');
var KeyboardHelper = require('shared/keyboard_helper');
var ManifestHelper = require('shared/manifest_helper');
// stores layout indexed by app manifestURL and layoutId
var _layoutDict = null;
var _keyboards = ObservableArray([]);
var _enabledLayouts = ObservableArray([]);
var _isReady = false;
var _parsingApps = false;
var _callbacks = [];
var _defaultEnabledCallbacks = [];
/**
* @alias module:Keyboard
* @class Keyboard
* @param {String} name
The name of the keyboard.
* @param {String} description
The description of the keyboard.
* @param {String} launchPath
The launch path of the keyboard.
* @param {Array} layouts
All layouts included in the keyboard.
* @param {DOMApplication} app
The keyboard app object.
* @returns {Keyboard}
*/
var Keyboard = function(name, description, launchPath, layouts, app) {
var _observable = Observable({
name: name,
description: description,
launchPath: launchPath,
layouts: layouts,
app: app
});
return _observable;
};
/**
* @alias module:Layout
* @class Layout
* @param {Object} Contain the following properties
* - id
The id of the layout.
* - appName
The name of the keyboard app containing the layout.
* - appManifestURL
The manifest url of the keyboard app containing
the layout.
* - name
The name of the layout.
* - nameL10nId
The l10n id of the layout name.
* - description
The description of the layout.
* - types
The supported input types of the layout.
* - enabled
The value indicating if the layout is enabled or not.
* @returns {Layout}
*/
var Layout = function(options) {
var _observable = Observable({
id: options.id,
appName: options.appName,
name: options.name,
nameL10nId: options.nameL10nId,
description: options.description,
types: options.types,
enabled: options.enabled
});
// Layout enabled changed.
_observable.observe('enabled', function(newValue, oldValue) {
if (!_parsingApps) {
KeyboardHelper.setLayoutEnabled(
options.appManifestURL, options.id, newValue);
// only check the defaults if we disabled a checkbox
if (!newValue) {
KeyboardHelper.checkDefaults(function(layouts, missingTypes) {
refreshEnabledLayouts(layouts);
notifyDefaultEnabled(layouts, missingTypes);
});
}
}
});
return _observable;
};
var _waitForLayouts;
function refreshEnabledLayouts(reEnabledLayouts) {
reEnabledLayouts.forEach(function(layout) {
var app = _layoutDict[layout.app.manifestURL];
if (app) {
app[layout.layoutId].enabled = true;
}
});
}
function notifyDefaultEnabled(layouts, missingTypes) {
_defaultEnabledCallbacks.forEach(function withCallbacks(callback) {
callback(layouts[0], missingTypes[0]);
});
}
function updateLayouts(layouts, reason) {
function mapLayout(layout) {
var app = _layoutDict[layout.app.manifestURL];
if (!app) {
app = _layoutDict[layout.app.manifestURL] = {};
}
if (app[layout.layoutId]) {
app[layout.layoutId].enabled = layout.enabled;
return app[layout.layoutId];
}
app[layout.layoutId] = Layout({
id: layout.layoutId,
appName: layout.manifest.name,
appManifestURL: layout.app.manifestURL,
name: layout.inputManifest.name,
nameL10nId: layout.inputManifest.nameL10nId,
description: layout.inputManifest.description,
types: layout.inputManifest.types,
enabled: layout.enabled});
return app[layout.layoutId];
}
function reduceApps(carry, layout) {
// if we already found this app, add it to the layouts
if (!carry.some(function checkApp(app) {
if (app.app === layout.app) {
app.layouts.push(mapLayout(layout));
return true;
}
})) {
carry.push({
app: layout.app,
manifest: layout.manifest,
layouts: [mapLayout(layout)]
});
}
return carry;
}
function mapKeyboard(app) {
return Keyboard(app.manifest.name, app.manifest.description,
app.manifest.launch_path, app.layouts, app.app);
}
_parsingApps = true;
// if we changed apps
if (reason.apps) {
// re parse every layout
_layoutDict = {};
var apps = layouts.reduce(reduceApps, []);
var keyboards = apps.map(mapKeyboard);
_keyboards.reset(keyboards);
}
var enabled = layouts.filter(function filterEnabled(layout) {
return layout.enabled;
}).map(mapLayout);
_enabledLayouts.reset(enabled);
_parsingApps = false;
if (_waitForLayouts) {
_waitForLayouts();
_waitForLayouts = undefined;
}
}
var _init = function(callback) {
document.addEventListener('DOMRetranslated', function() {
// refresh keyboard and layout in _keyboards
_keyboards.forEach(function(keyboard) {
var keyboardAppInstance = keyboard.app;
var keyboardManifest =
new ManifestHelper(keyboardAppInstance.manifest);
keyboard.name = keyboardManifest.name;
keyboard.description = keyboardManifest.description;
keyboard.layouts.forEach(function(layout) {
layout.appName = keyboardManifest.name;
});
});
});
_waitForLayouts = callback;
KeyboardHelper.stopWatching();
KeyboardHelper.watchLayouts(updateLayouts);
};
var _ready = function(callback) {
if (!callback) {
return;
}
if (_isReady) {
callback();
} else {
_callbacks.push(callback);
}
};
return {
/**
* Reset the keyboard context. It clears all cached data of installed
* keyboards and current enabled layouts.
*
* @alias module:KeyboardContext#reset
*/
reset: function kc_reset() {
_layoutDict = null;
_keyboards = ObservableArray([]);
_enabledLayouts = ObservableArray([]);
_isReady = false;
_parsingApps = false;
_callbacks = [];
_defaultEnabledCallbacks = [];
},
/**
* Initialize the keyboard context. After the context initialized, we are
* able to get the installed keyboards and enabled layouts.
*
* @alias module:KeyboardContext#init
* @param {Function} callback
* The callback when the context is initialized.
*/
init: function kc_init(callback) {
_defaultEnabledCallbacks = [];
_isReady = false;
_init(function() {
_isReady = true;
_callbacks.forEach(function(callback) {
callback();
});
});
_ready(callback);
},
/**
* Get the installed keyboards in terms of an observable array.
*
* @alias module:KeyboardContext#keyboards
* @param {Function} callback
* The result is passed to the callback when ready.
*/
keyboards: function kc_keyboards(callback) {
_ready(function() {
callback(_keyboards);
});
},
/**
* Get the enabled layouts in terms of an observable array.
*
* @alias module:KeyboardContext#enabledLayouts
* @param {Function} callback
* The result is passed to the callback when ready.
*/
enabledLayouts: function kc_enabledLayouts(callback) {
_ready(function() {
callback(_enabledLayouts);
});
},
/**
* Add a callback to be triggered when the default keyboard is enabled.
*
* @alias module:KeyboardContext#defaultKeyboardEnabled
* @param {Function} callback
* The callback to be triggered.
*/
defaultKeyboardEnabled: function kc_defaultKeyboardEnabled(callback) {
_defaultEnabledCallbacks.push(callback);
}
};
});