Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
274 lines
8.5 KiB
JavaScript
Executable file
274 lines
8.5 KiB
JavaScript
Executable file
/* global KeyboardHelper */
|
|
|
|
'use strict';
|
|
|
|
(function(exports) {
|
|
|
|
/**
|
|
* InputLayouts is responsible for processing and bookkeeping layouts returned
|
|
* from KeyboardHelper for use by KeyboardManager.
|
|
*/
|
|
var InputLayouts = function(keyboardManager, TYPE_GROUP_MAPPING) {
|
|
this._keyboardManager = keyboardManager;
|
|
|
|
// a mapping table from a group to an array of types mapped to the group.
|
|
// i.e. the reverse mapping of TYPE_GROUP_MAPPING
|
|
this._groupToTypeTable = {};
|
|
|
|
/**
|
|
*
|
|
* The set of installed keyboard layouts grouped by type_group
|
|
* (as in KeyboardManager)
|
|
* This is a map from type_group to an object arrays.
|
|
*
|
|
* i.e:
|
|
* {
|
|
* text: [ {...}, {...} ],
|
|
* number: [ {...}, {...} ]
|
|
* }
|
|
*
|
|
* Each element in the arrays represents a keyboard layout:
|
|
* {
|
|
* id: the unique id of the keyboard, the key of inputs
|
|
* name: the keyboard layout's name
|
|
* appName: the keyboard app name
|
|
* manifestURL: the keyboard's manifestURL
|
|
* path: the keyboard's launch path
|
|
* }
|
|
* Additionally, each array has an |_activeLayoutIdx|, which is the index,
|
|
* in that array, of the the currently-activated layout of the group.
|
|
* Outside world (i.e. KeyboardManager) is not supposed to directly write
|
|
* |_activeLayoutIdx| (KM should use the layout index as |setKeyboardToShow|
|
|
* parameter; and IL.saveGroupsCurrentActiveLayout is called by setKBToShow.
|
|
*/
|
|
this.layouts = {};
|
|
|
|
/*
|
|
* This is the reverse mapping from layout (manifestURL+id) to
|
|
* an array of {group: , index: }, indicating the groups that are supported
|
|
* by the layout.
|
|
* |index| is the index of the layout in the |group| in |this.layouts|.
|
|
*/
|
|
this._layoutToGroupMapping = {};
|
|
|
|
// A Set() of enabled KB apps
|
|
this._enabledApps = null;
|
|
|
|
Object.keys(TYPE_GROUP_MAPPING).forEach(function(type) {
|
|
var group = TYPE_GROUP_MAPPING[type];
|
|
this._groupToTypeTable[group] = this._groupToTypeTable[group] || [];
|
|
this._groupToTypeTable[group].push(type);
|
|
}, this);
|
|
|
|
this._currentActiveLayouts = {};
|
|
|
|
this._promise = null;
|
|
};
|
|
|
|
InputLayouts.prototype.start = function il_start() {
|
|
this._getSettings();
|
|
};
|
|
|
|
InputLayouts.prototype.stop = function il_stop() {
|
|
this._promise = null;
|
|
};
|
|
|
|
InputLayouts.prototype.SETTINGS_KEY_CURRENT_ACTIVE =
|
|
'keyboard.current-active-layouts';
|
|
|
|
InputLayouts.prototype._transformLayout =
|
|
function il_transformLayout(layout) {
|
|
var transformedLayout = {
|
|
id: layout.layoutId,
|
|
name: layout.inputManifest.name,
|
|
nameL10nId: layout.inputManifest.nameL10nId,
|
|
origin: layout.app.origin,
|
|
manifestURL: layout.app.manifestURL,
|
|
path: layout.inputManifest.launch_path
|
|
};
|
|
|
|
// define properties for names that resolve at display time
|
|
// to the correct language via the ManifestHelper.
|
|
Object.defineProperties(transformedLayout, {
|
|
appName: {
|
|
get: function() {
|
|
return layout.manifest.name;
|
|
},
|
|
enumerable: true
|
|
}
|
|
});
|
|
|
|
return transformedLayout;
|
|
};
|
|
|
|
InputLayouts.prototype._insertLayouts =
|
|
function il_insertLayouts(appLayouts) {
|
|
|
|
appLayouts.forEach(function (layout) {
|
|
this._enabledApps.add(layout.app.manifestURL);
|
|
|
|
layout.inputManifest.types.filter(KeyboardHelper.isKeyboardType)
|
|
.forEach(function(group) {
|
|
this.layouts[group] = this.layouts[group] || [];
|
|
this.layouts[group].push(this._transformLayout(layout));
|
|
}, this);
|
|
}, this);
|
|
};
|
|
|
|
InputLayouts.prototype._insertFallbackLayouts =
|
|
function il_insertFallbackLayouts() {
|
|
// bug 1035117:
|
|
// at this moment, if the 'fallback' groups (managed by KeyboardHelper)
|
|
// doesn't have any layouts, inject the fallback layout into it.
|
|
// (for example, user enables only CJKV IMEs, and for 'password'
|
|
// we need to enable 'en')
|
|
Object.keys(KeyboardHelper.fallbackLayouts).filter(
|
|
// linter fails if I use "group" instead of "grp" here
|
|
grp => !(grp in this.layouts)
|
|
).forEach(function (group) {
|
|
var layout = KeyboardHelper.fallbackLayouts[group];
|
|
|
|
this._enabledApps.add(layout.app.manifestURL);
|
|
|
|
this.layouts[group] = [this._transformLayout(layout)];
|
|
}, this);
|
|
};
|
|
|
|
InputLayouts.prototype._setSupportsSwitchingTypes = function() {
|
|
// Let chrome know about how many inputTypes should be marked as
|
|
// supporting swiching.
|
|
// inputTypes should only be marked as support switching if and only if
|
|
// there is more than one layout supporting this type.
|
|
var supportsSwitchingTypes =
|
|
Object.keys(this.layouts)
|
|
.reduce((types, group) => {
|
|
if (this.layouts[group].length > 1) {
|
|
types = types.concat(this._groupToTypeTable[group]);
|
|
}
|
|
|
|
return types;
|
|
}, /* supportsSwitchingTypes */ []);
|
|
|
|
navigator.mozInputMethod
|
|
.mgmt.setSupportsSwitchingTypes(supportsSwitchingTypes);
|
|
};
|
|
|
|
InputLayouts.prototype._generateToGroupMapping =
|
|
function il_generateToGroupMapping() {
|
|
Object.keys(this.layouts).forEach( group => {
|
|
this.layouts[group].forEach((layout, index) => {
|
|
var key = layout.manifestURL + '/' + layout.id;
|
|
this._layoutToGroupMapping[key] = this._layoutToGroupMapping[key] || [];
|
|
this._layoutToGroupMapping[key].push({
|
|
group: group,
|
|
index: index
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
InputLayouts.prototype.processLayouts =
|
|
function il_processLayouts(appLayouts) {
|
|
this.layouts = {};
|
|
this._enabledApps = new Set();
|
|
|
|
this._insertLayouts(appLayouts);
|
|
this._insertFallbackLayouts();
|
|
|
|
this._generateToGroupMapping();
|
|
|
|
// some initialization
|
|
for (var group in this.layouts) {
|
|
this.layouts[group]._activeLayoutIdx = undefined;
|
|
}
|
|
|
|
this._setSupportsSwitchingTypes();
|
|
|
|
return this._enabledApps;
|
|
};
|
|
|
|
// We only care about currentActiveLayout for now
|
|
InputLayouts.prototype._getSettings = function il_getSettings() {
|
|
if (!navigator.mozSettings) {
|
|
throw 'InputLayouts: No mozSettings?';
|
|
}
|
|
|
|
if (!this._promise) {
|
|
this._promise =
|
|
navigator.mozSettings.createLock().get(this.SETTINGS_KEY_CURRENT_ACTIVE)
|
|
.then(result => {
|
|
var value = result[this.SETTINGS_KEY_CURRENT_ACTIVE];
|
|
if (value) {
|
|
this._currentActiveLayouts = value;
|
|
}
|
|
|
|
return this._currentActiveLayouts;
|
|
}).catch(e => {
|
|
this._promise = null;
|
|
throw e;
|
|
});
|
|
}
|
|
|
|
return this._promise;
|
|
};
|
|
|
|
// Return the Promise that would resolve to the active layout index of the
|
|
// group, as indicated by settings
|
|
InputLayouts.prototype.getGroupCurrentActiveLayoutIndexAsync =
|
|
function il_getGroupCurrentActiveLayoutIndexAsync(group) {
|
|
return this._getSettings().then(currentActiveLayouts => {
|
|
var currentActiveLayout = currentActiveLayouts[group];
|
|
var currentActiveLayoutIdx;
|
|
|
|
if (currentActiveLayout && this.layouts[group]) {
|
|
this.layouts[group].every((layout, index) => {
|
|
if (layout.manifestURL === currentActiveLayout.manifestURL &&
|
|
layout.id === currentActiveLayout.id) {
|
|
// If so, default to that, saving the users choice
|
|
currentActiveLayoutIdx = index;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
return currentActiveLayoutIdx;
|
|
});
|
|
};
|
|
|
|
// Set the active layout index for the groups supported by the layout,
|
|
// to the bookkeeping variables and into the settings
|
|
InputLayouts.prototype.saveGroupsCurrentActiveLayout =
|
|
function il_saveGroupsCurrentActiveLayout(layout) {
|
|
var supportedGroups = [];
|
|
|
|
this._layoutToGroupMapping[layout.manifestURL + '/' + layout.id].forEach(
|
|
groupInfo => {
|
|
this.layouts[groupInfo.group]._activeLayoutIdx = groupInfo.index;
|
|
|
|
supportedGroups.push(groupInfo.group);
|
|
});
|
|
|
|
supportedGroups.forEach(group => {
|
|
var curr = this._currentActiveLayouts[group];
|
|
if (curr && curr.id === layout.id &&
|
|
curr.manifestURL === layout.manifestURL) {
|
|
return;
|
|
}
|
|
|
|
this._currentActiveLayouts[group] = {
|
|
id: layout.id,
|
|
manifestURL: layout.manifestURL
|
|
};
|
|
});
|
|
|
|
var toSet = {};
|
|
toSet[this.SETTINGS_KEY_CURRENT_ACTIVE] = this._currentActiveLayouts;
|
|
var req = navigator.mozSettings.createLock().set(toSet);
|
|
req.onerror =
|
|
() => console.error('Error while seaving currentActiveLayout', req.error);
|
|
};
|
|
|
|
exports.InputLayouts = InputLayouts;
|
|
|
|
})(window);
|