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.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
822
gaia/shared/js/keyboard_helper.js
Executable file
822
gaia/shared/js/keyboard_helper.js
Executable file
|
|
@ -0,0 +1,822 @@
|
|||
'use strict';
|
||||
/* global InputAppList, ManifestHelper */
|
||||
|
||||
/**
|
||||
* Helper object to find all installed keyboard apps and layouts.
|
||||
*
|
||||
* (Need mozApps.mgmt and settings permission)
|
||||
*/
|
||||
|
||||
(function(exports) {
|
||||
/* jshint validthis: true */
|
||||
|
||||
/**
|
||||
* The set of "basic keyboard" types
|
||||
*/
|
||||
var BASE_TYPES = new Set([
|
||||
'text', 'url', 'email', 'password', 'number', 'option'
|
||||
]);
|
||||
|
||||
/**
|
||||
* The keys stored in the settings database
|
||||
*/
|
||||
var SETTINGS_KEYS = {
|
||||
ENABLED: 'keyboard.enabled-layouts',
|
||||
DEFAULT: 'keyboard.default-layouts'
|
||||
};
|
||||
|
||||
var DEPRECATE_KEYBOARD_SETTINGS = {
|
||||
en: 'keyboard.layouts.english',
|
||||
'en-Dvorak': 'keyboard.layouts.dvorak',
|
||||
cs: 'keyboard.layouts.czech',
|
||||
fr: 'keyboard.layouts.french',
|
||||
de: 'keyboard.layouts.german',
|
||||
hu: 'keyboard.layouts.hungarian',
|
||||
nb: 'keyboard.layouts.norwegian',
|
||||
my: 'keyboard.layouts.myanmar',
|
||||
sl: 'keyboard.layouts.slovak',
|
||||
tr: 'keyboard.layouts.turkish',
|
||||
ro: 'keyboard.layouts.romanian',
|
||||
ru: 'keyboard.layouts.russian',
|
||||
ar: 'keyboard.layouts.arabic',
|
||||
he: 'keyboard.layouts.hebrew',
|
||||
'zh-Hant-Zhuyin': 'keyboard.layouts.zhuyin',
|
||||
'zh-Hans-Pinyin': 'keyboard.layouts.pinyin',
|
||||
el: 'keyboard.layouts.greek',
|
||||
'jp-kanji': 'keyboard.layouts.japanese',
|
||||
pl: 'keyboard.layouts.polish',
|
||||
'pt-BR': 'keyboard.layouts.portuguese',
|
||||
sr: 'keyboard.layouts.serbian',
|
||||
es: 'keyboard.layouts.spanish',
|
||||
ca: 'keyboard.layouts.catalan'
|
||||
};
|
||||
|
||||
var MULTI_LAYOUT_MAP = {
|
||||
sr: ['sr-Cyrl', 'sr-Latn']
|
||||
};
|
||||
|
||||
// In order to provide default defaults, we need to know the default keyboard
|
||||
var defaultKeyboardManifestURL =
|
||||
'app://keyboard.gaiamobile.org/manifest.webapp';
|
||||
|
||||
// Stores a local copy of whatever is in the settings database
|
||||
var currentSettings = {
|
||||
defaultLayouts: {}
|
||||
};
|
||||
|
||||
// until we read otherwise, asssume the default keyboards are en and number
|
||||
currentSettings.defaultLayouts[defaultKeyboardManifestURL] = {
|
||||
en: true,
|
||||
number: true
|
||||
};
|
||||
|
||||
// and also assume that the defaults are the enabled
|
||||
currentSettings.enabledLayouts = map2dClone(currentSettings.defaultLayouts);
|
||||
|
||||
/**
|
||||
* helper function for reading a value in one of the currentSettings
|
||||
*/
|
||||
function map2dIs(manifestURL, layoutId) {
|
||||
// force boolean true or false
|
||||
return !!(this[manifestURL] && this[manifestURL][layoutId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* helper function for setting a value to true in one of the currentSettings
|
||||
*/
|
||||
function map2dSet(manifestURL, layoutId) {
|
||||
if (!this[manifestURL]) {
|
||||
this[manifestURL] = {};
|
||||
}
|
||||
this[manifestURL][layoutId] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* helper function for setting a value to false in one of the currentSettings
|
||||
* deletes the appropriate keys
|
||||
*/
|
||||
function map2dUnset(manifestURL, layoutId) {
|
||||
if (!this[manifestURL]) {
|
||||
return;
|
||||
}
|
||||
delete this[manifestURL][layoutId];
|
||||
if (!Object.keys(this[manifestURL]).length) {
|
||||
delete this[manifestURL];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* helper function for cloning one of the currentSettings
|
||||
*/
|
||||
function map2dClone(obj) {
|
||||
var result = {};
|
||||
for (var key1 in obj) {
|
||||
result[key1] = {};
|
||||
for (var key2 in obj[key1]) {
|
||||
result[key1][key2] = obj[key1][key2];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// callbacks when something changes
|
||||
var watchQueries = [];
|
||||
|
||||
// holds the result of keyboard_layouts.json
|
||||
var defaultLayoutConfig;
|
||||
|
||||
/**
|
||||
* Sends any watchers the result of their query.
|
||||
*/
|
||||
function kh_updateWatchers(reason) {
|
||||
if (watchQueries.length) {
|
||||
|
||||
// we do a getApps in order to make sure that we have the most recent
|
||||
// app data before calling getLayouts
|
||||
KeyboardHelper.getApps(function withApps() {
|
||||
watchQueries.forEach(function eachWatchQuery(watch) {
|
||||
KeyboardHelper.getLayouts(watch.query, function withLayouts(layouts) {
|
||||
watch.callback(layouts, reason);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// callbacks waiting to know when settings are loaded
|
||||
var waitingForSettings = [];
|
||||
var loadedSettings = new Set();
|
||||
|
||||
/**
|
||||
* Tracks the number of settings loaded and calls the callbacks
|
||||
*/
|
||||
function kh_loadedSetting(setting) {
|
||||
loadedSettings.add(setting);
|
||||
|
||||
if (loadedSettings.size >= Object.keys(SETTINGS_KEYS).length) {
|
||||
waitingForSettings.forEach(function(callback) {
|
||||
callback();
|
||||
});
|
||||
waitingForSettings = [];
|
||||
kh_updateWatchers({ settings: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a callback when the settings have loaded, or immediately if already
|
||||
* loaded.
|
||||
*/
|
||||
function kh_withSettings(callback) {
|
||||
if (loadedSettings.size >= Object.keys(SETTINGS_KEYS).length) {
|
||||
callback();
|
||||
} else {
|
||||
waitingForSettings.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal helper to read the settings again
|
||||
*/
|
||||
function kh_getSettings() {
|
||||
loadedSettings = new Set();
|
||||
var lock = window.navigator.mozSettings.createLock();
|
||||
lock.get(SETTINGS_KEYS.DEFAULT).onsuccess = kh_parseDefault;
|
||||
lock.get(SETTINGS_KEYS.ENABLED).onsuccess = kh_parseEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the result from the settings query for default layouts
|
||||
*/
|
||||
function kh_parseDefault() {
|
||||
var value = this.result[SETTINGS_KEYS.DEFAULT];
|
||||
if (value) {
|
||||
currentSettings.defaultLayouts = value;
|
||||
}
|
||||
kh_loadedSetting(SETTINGS_KEYS.DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the result from the settings query for enabled layouts
|
||||
*/
|
||||
function kh_parseEnabled() {
|
||||
var value = this.result[SETTINGS_KEYS.ENABLED];
|
||||
var needsSave = false;
|
||||
if (!value) {
|
||||
currentSettings.enabledLayouts = map2dClone(currentSettings.defaultLayouts);
|
||||
needsSave = true;
|
||||
} else {
|
||||
var type = typeof value;
|
||||
|
||||
// deal with the old string format - might be able to be removed once test
|
||||
// devices have been living with this new format for a bit.
|
||||
if (type === 'string') {
|
||||
try {
|
||||
currentSettings.enabledLayouts = {};
|
||||
var oldSettings = JSON.parse(value);
|
||||
oldSettings.forEach(function(layout) {
|
||||
if (layout.enabled) {
|
||||
var manifestURL = layout.manifestURL;
|
||||
if (!manifestURL) {
|
||||
manifestURL = layout.appOrigin + '/manifest.webapp';
|
||||
}
|
||||
map2dSet.call(currentSettings.enabledLayouts, manifestURL,
|
||||
layout.layoutId);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
currentSettings.enabledLayouts =
|
||||
map2dClone(currentSettings.defaultLayouts);
|
||||
needsSave = true;
|
||||
}
|
||||
} else {
|
||||
// if it wasn't a string, assume its our object
|
||||
currentSettings.enabledLayouts = value;
|
||||
}
|
||||
}
|
||||
|
||||
kh_loadedSetting(SETTINGS_KEYS.ENABLED);
|
||||
|
||||
if (needsSave) {
|
||||
KeyboardHelper.saveToSettings();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and migrate the result for the deprecated settings (v1.1) for enabled
|
||||
* layouts.
|
||||
*/
|
||||
function kh_migrateDeprecatedSettings(deprecatedSettings) {
|
||||
/* jshint loopfunc: true */
|
||||
var settingEntry = DEPRECATE_KEYBOARD_SETTINGS.en;
|
||||
|
||||
// No need to do migration if the deprecated settings are not available
|
||||
if (deprecatedSettings[settingEntry] === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reset the enabled layouts
|
||||
currentSettings.enabledLayouts[defaultKeyboardManifestURL] = {
|
||||
number: true
|
||||
};
|
||||
|
||||
var hasEnabledLayout = false;
|
||||
for (var key in DEPRECATE_KEYBOARD_SETTINGS) {
|
||||
settingEntry = DEPRECATE_KEYBOARD_SETTINGS[key];
|
||||
// this layout was set as enabled in the old settings
|
||||
if (deprecatedSettings[settingEntry]) {
|
||||
hasEnabledLayout = true;
|
||||
|
||||
if (key in MULTI_LAYOUT_MAP) {
|
||||
MULTI_LAYOUT_MAP[key].forEach(function enableLayout(layoutId) {
|
||||
map2dSet.call(currentSettings.enabledLayouts,
|
||||
defaultKeyboardManifestURL, layoutId);
|
||||
});
|
||||
} else {
|
||||
map2dSet.call(currentSettings.enabledLayouts,
|
||||
defaultKeyboardManifestURL, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// None of the layouts has been set enabled, so enable English by default
|
||||
if (!hasEnabledLayout) {
|
||||
map2dSet.call(currentSettings.enabledLayouts,
|
||||
defaultKeyboardManifestURL, 'en');
|
||||
}
|
||||
|
||||
// Clean up all the deprecated settings
|
||||
var deprecatedSettingsQuery = {};
|
||||
for (key in DEPRECATE_KEYBOARD_SETTINGS) {
|
||||
// the deprecated setting entry, e.g. keyboard.layout.english
|
||||
settingEntry = DEPRECATE_KEYBOARD_SETTINGS[key];
|
||||
|
||||
// Set the default value for each entry
|
||||
deprecatedSettingsQuery[settingEntry] = null;
|
||||
}
|
||||
window.navigator.mozSettings.createLock().set(deprecatedSettingsQuery);
|
||||
|
||||
KeyboardHelper.saveToSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON loader
|
||||
*/
|
||||
function kh_loadJSON(href, callback) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onerror = function() {
|
||||
console.error('Failed to fetch file: ' + href, xhr.statusText);
|
||||
};
|
||||
xhr.onload = function() {
|
||||
callback(xhr.response);
|
||||
};
|
||||
xhr.open('GET', href, true); // async
|
||||
xhr.responseType = 'json';
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
//
|
||||
// getSettings: Query the value of multiple settings at once.
|
||||
//
|
||||
// settings is an object whose property names are the settings to query
|
||||
// and whose property values are the default values to use if no such
|
||||
// setting is found. This function will create a setting lock and
|
||||
// request the value of each of the specified settings. Once it receives
|
||||
// a response to all of the queries, it passes all the settings values to
|
||||
// the specified callback function. The argument to the callback function
|
||||
// is an object just like the settings object, where the property name is
|
||||
// the setting name and the property value is the setting value (or the
|
||||
// default value if the setting was not found).
|
||||
//
|
||||
function kh_getMultiSettings(settings, callback) {
|
||||
var results = {};
|
||||
try {
|
||||
var lock = navigator.mozSettings.createLock();
|
||||
}
|
||||
catch (e) {
|
||||
// If settings is broken, just return the default values
|
||||
console.warn('Exception in mozSettings.createLock():', e,
|
||||
'\nUsing default values');
|
||||
for (var p in settings) {
|
||||
results[p] = settings[p];
|
||||
}
|
||||
callback(results);
|
||||
}
|
||||
var settingNames = Object.keys(settings);
|
||||
var numSettings = settingNames.length;
|
||||
var numResults = 0;
|
||||
|
||||
for (var i = 0; i < numSettings; i++) {
|
||||
requestSetting(settingNames[i]);
|
||||
}
|
||||
|
||||
function requestSetting(name) {
|
||||
var request;
|
||||
try {
|
||||
request = lock.get(name);
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('Exception querying setting', name, ':', e,
|
||||
'\nUsing default value');
|
||||
recordResult(name, settings[name]);
|
||||
return;
|
||||
}
|
||||
request.onsuccess = function() {
|
||||
var value = request.result[name];
|
||||
if (value === undefined) {
|
||||
value = settings[name]; // Use the default value
|
||||
}
|
||||
recordResult(name, value);
|
||||
};
|
||||
request.onerror = function(evt) {
|
||||
console.warn('Error querying setting', name, ':', evt.error);
|
||||
recordResult(name, settings[name]);
|
||||
};
|
||||
}
|
||||
|
||||
function recordResult(name, value) {
|
||||
results[name] = value;
|
||||
numResults++;
|
||||
if (numResults === numSettings) {
|
||||
callback(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object that describes a keyboard layout. The objects prototype
|
||||
* will give it accessors for 'default' and 'enabled' that will read or change
|
||||
* the proper setting from currentSettings.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function KeyboardLayout(options) {
|
||||
for (var key in options) {
|
||||
this[key] = options[key];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperties(KeyboardLayout.prototype, {
|
||||
'default': {
|
||||
get: function kh_getLayoutIsDefault() {
|
||||
return map2dIs.call(
|
||||
currentSettings.defaultLayouts, this.app.manifestURL, this.layoutId
|
||||
);
|
||||
}
|
||||
},
|
||||
enabled: {
|
||||
get: function kh_getLayoutIsEnabled() {
|
||||
return map2dIs.call(
|
||||
currentSettings.enabledLayouts, this.app.manifestURL, this.layoutId
|
||||
);
|
||||
},
|
||||
set: function kh_setLayoutIsDefault(value) {
|
||||
var method = value ? map2dSet : map2dUnset;
|
||||
method.call(currentSettings.enabledLayouts,
|
||||
this.app.manifestURL, this.layoutId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Exposed as KeyboardHelper.settings this gives us a fairly safe way to read
|
||||
* and write to the settings data structures directly.
|
||||
* XXX: is this really used anywhere?
|
||||
*/
|
||||
var kh_SettingsHelper = {};
|
||||
Object.defineProperties(kh_SettingsHelper, {
|
||||
'default': {
|
||||
get: function() {
|
||||
return map2dClone(currentSettings.defaultLayouts);
|
||||
},
|
||||
set: function(value) {
|
||||
currentSettings.defaultLayouts = map2dClone(value);
|
||||
},
|
||||
enumerable: true
|
||||
},
|
||||
'enabled': {
|
||||
get: function() {
|
||||
return map2dClone(currentSettings.enabledLayouts);
|
||||
},
|
||||
set: function(value) {
|
||||
currentSettings.enabledLayouts = map2dClone(value);
|
||||
},
|
||||
enumerable: true
|
||||
}
|
||||
});
|
||||
|
||||
var KeyboardHelper = exports.KeyboardHelper = {
|
||||
settings: kh_SettingsHelper,
|
||||
|
||||
// bug 1035117: define the fallback layout for a group if no layout has been
|
||||
// selected for that group (if it's not enforced in settings)
|
||||
// please see the bug and its related UX spec for the sense of 'fallback'
|
||||
fallbackLayoutNames: {
|
||||
password: 'en'
|
||||
},
|
||||
|
||||
fallbackLayouts: {},
|
||||
|
||||
// InputAppList manages the current input apps for us.
|
||||
inputAppList: null,
|
||||
|
||||
/**
|
||||
* Listen for changes in settings or apps and read the deafault settings
|
||||
*/
|
||||
init: function kh_init() {
|
||||
watchQueries = [];
|
||||
|
||||
this.inputAppList = new InputAppList();
|
||||
this.inputAppList.onupdate =
|
||||
kh_updateWatchers.bind(undefined, { apps: true });
|
||||
this.inputAppList.start();
|
||||
|
||||
// load the current settings, and watch for changes to settings
|
||||
var settings = window.navigator.mozSettings;
|
||||
if (settings) {
|
||||
kh_getSettings();
|
||||
settings.addObserver(SETTINGS_KEYS.ENABLED, kh_getSettings);
|
||||
settings.addObserver(SETTINGS_KEYS.DEFAULT, kh_getSettings);
|
||||
|
||||
// read deprecated settings
|
||||
var deprecatedSettingsQuery = {};
|
||||
for (var key in DEPRECATE_KEYBOARD_SETTINGS) {
|
||||
// the deprecated setting entry, e.g. keyboard.layout.english
|
||||
var settingEntry = DEPRECATE_KEYBOARD_SETTINGS[key];
|
||||
|
||||
// Set the default value for each entry
|
||||
deprecatedSettingsQuery[settingEntry] = undefined;
|
||||
}
|
||||
kh_getMultiSettings(deprecatedSettingsQuery,
|
||||
kh_migrateDeprecatedSettings);
|
||||
}
|
||||
|
||||
window.addEventListener('applicationinstallsuccess', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the application changed events. Clears the cache and updates
|
||||
* any listening watchers.
|
||||
*/
|
||||
handleEvent: function(event) {
|
||||
kh_updateWatchers({ apps: true });
|
||||
},
|
||||
|
||||
/**
|
||||
* If this is a basic keyboard type
|
||||
*/
|
||||
isKeyboardType: function kh_isKeyboardType(type) {
|
||||
return BASE_TYPES.has(type);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables or disables a layout based on manifest URL and layoutId
|
||||
*/
|
||||
setLayoutEnabled: function kh_setLayoutEnabled(manifestURL, layoutId,
|
||||
enabled) {
|
||||
var method = enabled ? map2dSet : map2dUnset;
|
||||
method.call(currentSettings.enabledLayouts, manifestURL, layoutId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the layout specified by manifest URL and layoutId is
|
||||
* enabled.
|
||||
*/
|
||||
getLayoutEnabled: function kh_getLayoutEnabled(manifestURL, layoutId) {
|
||||
return map2dIs.call(currentSettings.enabledLayouts, manifestURL, layoutId);
|
||||
},
|
||||
|
||||
/**
|
||||
* set/unset a layout as default based on manifest URL and layoutId
|
||||
*/
|
||||
setLayoutIsDefault: function kh_setLayoutIsDefault(manifestURL, layoutId,
|
||||
enabled) {
|
||||
var method = enabled ? map2dSet : map2dUnset;
|
||||
method.call(currentSettings.defaultLayouts, manifestURL, layoutId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the layout specified by manifest URL and layoutId
|
||||
* is default.
|
||||
*/
|
||||
getLayoutIsDefault: function kh_getLayoutIsDefault(manifestURL, layoutId) {
|
||||
return map2dIs.call(currentSettings.defaultLayouts, manifestURL, layoutId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks the currently enabled layouts to ensure there is at least one
|
||||
* 'text', 'url' and 'number' keyboard enabled. If there is not, it will
|
||||
* enable the corresponding default keyboard.
|
||||
*
|
||||
* Only calls the callback if a default was enabled, and passes an array
|
||||
* of layouts that were enabled.
|
||||
*/
|
||||
checkDefaults: function kh_checkDefaults(callback) {
|
||||
var layoutsEnabled = [];
|
||||
var missingTypes = [];
|
||||
['text', 'url', 'number'].forEach(function eachType(type) {
|
||||
// getLayouts is sync when we already have data
|
||||
var enabled;
|
||||
this.getLayouts({ type: type, enabled: true }, function(layouts) {
|
||||
enabled = layouts.length;
|
||||
});
|
||||
if (!enabled) {
|
||||
missingTypes.push(type);
|
||||
this.getLayouts({ type: type, 'default': true }, function(layouts) {
|
||||
if (layouts[0]) {
|
||||
layouts[0].enabled = true;
|
||||
layoutsEnabled.push(layouts[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (layoutsEnabled.length) {
|
||||
if (callback) {
|
||||
callback(layoutsEnabled, missingTypes);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Saves the current state to settings. None of the changes made will be
|
||||
* saved automatically by KeyboardHelper. This allows someone to batch
|
||||
* whatever settings changes are nessecary, then call save.
|
||||
*/
|
||||
saveToSettings: function ke_saveToSettings() {
|
||||
var toSet = {};
|
||||
toSet[SETTINGS_KEYS.ENABLED] = currentSettings.enabledLayouts;
|
||||
toSet[SETTINGS_KEYS.DEFAULT] = currentSettings.defaultLayouts;
|
||||
window.navigator.mozSettings.createLock().set(toSet);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a list of current keyboard applications.
|
||||
*/
|
||||
getApps: function kh_getApps(callback) {
|
||||
// every time we get a list of apps, clean up the settings
|
||||
var cleanupSettings = function(inputApps) {
|
||||
Object.keys(currentSettings.enabledLayouts)
|
||||
.concat(Object.keys(currentSettings.defaultLayouts))
|
||||
.forEach(function(manifestURL) {
|
||||
// if the manifestURL doesn't exist in the list of apps, delete it
|
||||
// from the settings maps
|
||||
if (!inputApps.some(function(inputApp) {
|
||||
return (inputApp.domApp.manifestURL === manifestURL);
|
||||
})) {
|
||||
delete currentSettings.enabledLayouts[manifestURL];
|
||||
delete currentSettings.defaultLayouts[manifestURL];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// XXX We have to preserve the original getApps() behavior here,
|
||||
// i.e. call the sync callback when we already have the data.
|
||||
if (this.inputAppList.ready) {
|
||||
var inputApps = this.inputAppList.getListSync();
|
||||
cleanupSettings(inputApps);
|
||||
callback(inputApps);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.inputAppList.getList().then(function(inputApps) {
|
||||
cleanupSettings(inputApps);
|
||||
callback(inputApps);
|
||||
})['catch'](function(e) { // workaround gjslint error
|
||||
console.error(e);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests an array of layouts matching an optional set of search params.
|
||||
* Calls the callback when data is received, or immediately if already
|
||||
* cached locally.
|
||||
*
|
||||
* options.default - When true, only default layouts are returned.
|
||||
* options.enabled - When true, only enabled layouts are returned.
|
||||
* options.type - A string, or array of strings. Layouts filtered to type.
|
||||
*/
|
||||
getLayouts: function kh_getLayouts(options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
function withApps(inputApps) {
|
||||
/* jshint loopfunc: true */
|
||||
// we'll delete keys in this active copy (= the purpose of copying)
|
||||
var fallbackLayoutNames = {};
|
||||
for (var group in this.fallbackLayoutNames) {
|
||||
fallbackLayoutNames[group] = this.fallbackLayoutNames[group];
|
||||
}
|
||||
this.fallbackLayouts = {};
|
||||
|
||||
var layouts = inputApps.reduce(function eachApp(result, inputApp) {
|
||||
var domApp = inputApp.domApp;
|
||||
|
||||
var manifest = new ManifestHelper(domApp.manifest);
|
||||
var inputs = inputApp.getInputs();
|
||||
for (var layoutId in inputs) {
|
||||
var inputManifest = inputs[layoutId];
|
||||
if (!inputManifest.types) {
|
||||
console.warn(domApp.manifestURL, layoutId, 'did not declare type.');
|
||||
continue;
|
||||
}
|
||||
|
||||
var layout = new KeyboardLayout({
|
||||
app: domApp,
|
||||
inputApp: inputApp,
|
||||
manifest: manifest,
|
||||
inputManifest: inputManifest,
|
||||
layoutId: layoutId
|
||||
});
|
||||
|
||||
// bug 1035117: insert a fallback layout regardless of its
|
||||
// and enabledness
|
||||
// XXX: we only do this for built-in keyboard?
|
||||
if (domApp.manifestURL === defaultKeyboardManifestURL) {
|
||||
for (var group in fallbackLayoutNames) {
|
||||
if (layoutId === fallbackLayoutNames[group]) {
|
||||
this.fallbackLayouts[group] = layout;
|
||||
delete fallbackLayoutNames[group];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options['default'] && !layout['default']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options.enabled && !layout.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options.type) {
|
||||
options.type = [].concat(options.type);
|
||||
// check for any type in our options to be any type in the layout
|
||||
if (!options.type.some(function(type) {
|
||||
return inputManifest.types.indexOf(type) !== -1;
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result.push(layout);
|
||||
}
|
||||
|
||||
// sort the default query by most specific keyboard
|
||||
if (options['default']) {
|
||||
result.sort(function(a, b) {
|
||||
return a.inputManifest.types.length - b.inputManifest.types.length;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}.bind(this), []);
|
||||
|
||||
kh_withSettings(callback.bind(null, layouts));
|
||||
}
|
||||
|
||||
this.getApps(withApps.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears all watch queries
|
||||
*/
|
||||
stopWatching: function() {
|
||||
watchQueries = [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls the callback whenever settings or apps are changed. The query is
|
||||
* passed onto getLayouts. The callback will be called as soon as the current
|
||||
* settings and apps are loaded as well.
|
||||
*
|
||||
* The callback will be passed an array of layouts, and a reason object.
|
||||
* I.E. function callback(layouts, reason) {}
|
||||
* reason.apps will be true when apps have changed, and reason.settings
|
||||
* will be true if settings changed. Both will be true on the first
|
||||
* callback.
|
||||
*/
|
||||
watchLayouts: function(query, callback) {
|
||||
if (typeof query === 'function') {
|
||||
callback = query;
|
||||
query = {};
|
||||
}
|
||||
|
||||
var watch = {
|
||||
query: query,
|
||||
callback: callback
|
||||
};
|
||||
watchQueries.push(watch);
|
||||
|
||||
this.getLayouts(query, function initialCall(layouts) {
|
||||
watch.layouts = layouts;
|
||||
callback(layouts, { apps: true, settings: true });
|
||||
});
|
||||
},
|
||||
|
||||
// Read keyboard_layouts for language -> layouts mapping
|
||||
getDefaultLayoutConfig: function kh_getDefaultLayoutConfig(callback) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (defaultLayoutConfig) {
|
||||
callback(defaultLayoutConfig);
|
||||
} else {
|
||||
var KEYBOARDS = '/shared/resources/keyboard_layouts.json';
|
||||
kh_loadJSON(KEYBOARDS, function loadKeyboardLayouts(data) {
|
||||
if (data) {
|
||||
defaultLayoutConfig = data;
|
||||
callback(defaultLayoutConfig);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Change the default layouts set according to the language
|
||||
// language: the current system language, used to look up the mapping table
|
||||
// reset: if set as true, will reset the current enabled layouts
|
||||
changeDefaultLayouts: function kh_changeDefaultLayouts(language, reset) {
|
||||
this.getDefaultLayoutConfig(function gotDefaultLayouts(keyboards) {
|
||||
var newKbLayouts = keyboards.layout[language];
|
||||
|
||||
// XXX: change this so that it could support multiple built-in
|
||||
// keyboard apps
|
||||
var kbManifestURL = defaultKeyboardManifestURL;
|
||||
|
||||
// reset the set of default layouts
|
||||
currentSettings.defaultLayouts = {};
|
||||
|
||||
// set the language-independent default layouts
|
||||
var langIndependentLayouts = keyboards.langIndependentLayouts;
|
||||
for (var i = langIndependentLayouts.length - 1; i >= 0; i--) {
|
||||
this.setLayoutIsDefault(kbManifestURL,
|
||||
langIndependentLayouts[i].layoutId,
|
||||
true);
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
// reset the set of default layouts
|
||||
currentSettings.enabledLayouts =
|
||||
map2dClone(currentSettings.defaultLayouts);
|
||||
}
|
||||
|
||||
// Enable the language specific keyboard layout group
|
||||
for (i = newKbLayouts.length - 1; i >= 0; i--) {
|
||||
this.setLayoutIsDefault(kbManifestURL, newKbLayouts[i].layoutId, true);
|
||||
this.setLayoutEnabled(kbManifestURL, newKbLayouts[i].layoutId, true);
|
||||
}
|
||||
|
||||
this.saveToSettings(); // save changes to settings
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
KeyboardHelper.init();
|
||||
|
||||
}(window));
|
||||
Loading…
Reference in a new issue