summaryrefslogtreecommitdiff
path: root/ext/bg/js/options.js
diff options
context:
space:
mode:
authorAlex Yatskov <alex@foosoft.net>2019-09-23 17:35:36 -0700
committerAlex Yatskov <alex@foosoft.net>2019-09-23 17:35:36 -0700
commitf4b6527ed6ed1f0f4f5a63b94766b20f3b90e6ec (patch)
tree0d2f733c13597dd4067d3dc01e6da27f96bfe81b /ext/bg/js/options.js
parentcfc6363a01ee00e89866c54709006d6f55d093de (diff)
parentf5afe590ad0730a695614b32032b7ea70b46c7b0 (diff)
Merge branch 'master' into testing
Diffstat (limited to 'ext/bg/js/options.js')
-rw-r--r--ext/bg/js/options.js226
1 files changed, 164 insertions, 62 deletions
diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js
index df95aae9..e9e321df 100644
--- a/ext/bg/js/options.js
+++ b/ext/bg/js/options.js
@@ -17,7 +17,67 @@
*/
-function optionsFieldTemplates() {
+/*
+ * Generic options functions
+ */
+
+function optionsGenericApplyUpdates(options, updates) {
+ const targetVersion = updates.length;
+ const currentVersion = options.version;
+ if (typeof currentVersion === 'number' && Number.isFinite(currentVersion)) {
+ for (let i = Math.max(0, Math.floor(currentVersion)); i < targetVersion; ++i) {
+ const update = updates[i];
+ if (update !== null) {
+ update(options);
+ }
+ }
+ }
+
+ options.version = targetVersion;
+ return options;
+}
+
+
+/*
+ * Per-profile options
+ */
+
+const profileOptionsVersionUpdates = [
+ null,
+ null,
+ null,
+ null,
+ (options) => {
+ options.general.audioSource = options.general.audioPlayback ? 'jpod101' : 'disabled';
+ },
+ (options) => {
+ options.general.showGuide = false;
+ },
+ (options) => {
+ options.scanning.modifier = options.scanning.requireShift ? 'shift' : 'none';
+ },
+ (options) => {
+ const fieldTemplatesDefault = profileOptionsGetDefaultFieldTemplates();
+ options.general.resultOutputMode = options.general.groupResults ? 'group' : 'split';
+ options.anki.fieldTemplates = (
+ (utilStringHashCode(options.anki.fieldTemplates) !== -805327496) ?
+ `{{#if merge}}${fieldTemplatesDefault}{{else}}${options.anki.fieldTemplates}{{/if}}` :
+ fieldTemplatesDefault
+ );
+ },
+ (options) => {
+ if (utilStringHashCode(options.anki.fieldTemplates) === 1285806040) {
+ options.anki.fieldTemplates = profileOptionsGetDefaultFieldTemplates();
+ }
+ },
+ (options) => {
+ if (utilStringHashCode(options.anki.fieldTemplates) === -250091611) {
+ options.anki.fieldTemplates = profileOptionsGetDefaultFieldTemplates();
+ }
+ }
+];
+
+function profileOptionsGetDefaultFieldTemplates() {
return `
{{#*inline "glossary-single"}}
{{~#unless brief~}}
@@ -183,8 +243,8 @@ function optionsFieldTemplates() {
`.trim();
}
-function optionsSetDefaults(options) {
- const defaults = {
+function profileOptionsCreateDefaults() {
+ return {
general: {
enable: true,
audioSource: 'jpod101',
@@ -235,9 +295,13 @@ function optionsSetDefaults(options) {
screenshot: {format: 'png', quality: 92},
terms: {deck: '', model: '', fields: {}},
kanji: {deck: '', model: '', fields: {}},
- fieldTemplates: optionsFieldTemplates()
+ fieldTemplates: profileOptionsGetDefaultFieldTemplates()
}
};
+}
+
+function profileOptionsSetDefaults(options) {
+ const defaults = profileOptionsCreateDefaults();
const combine = (target, source) => {
for (const key in source) {
@@ -257,81 +321,119 @@ function optionsSetDefaults(options) {
return options;
}
-function optionsVersion(options) {
- const fixups = [
- () => {},
- () => {},
- () => {},
- () => {},
- () => {
- if (options.general.audioPlayback) {
- options.general.audioSource = 'jpod101';
- } else {
- options.general.audioSource = 'disabled';
- }
- },
- () => {
- options.general.showGuide = false;
- },
- () => {
- if (options.scanning.requireShift) {
- options.scanning.modifier = 'shift';
- } else {
- options.scanning.modifier = 'none';
- }
- },
- () => {
- if (options.general.groupResults) {
- options.general.resultOutputMode = 'group';
- } else {
- options.general.resultOutputMode = 'split';
- }
- if (utilStringHashCode(options.anki.fieldTemplates) !== -805327496) {
- options.anki.fieldTemplates = `{{#if merge}}${optionsFieldTemplates()}{{else}}${options.anki.fieldTemplates}{{/if}}`;
- } else {
- options.anki.fieldTemplates = optionsFieldTemplates();
- }
- },
- () => {
- if (utilStringHashCode(options.anki.fieldTemplates) === 1285806040) {
- options.anki.fieldTemplates = optionsFieldTemplates();
- }
- },
- () => {
- if (utilStringHashCode(options.anki.fieldTemplates) === -250091611) {
- options.anki.fieldTemplates = optionsFieldTemplates();
- }
+function profileOptionsUpdateVersion(options) {
+ profileOptionsSetDefaults(options);
+ return optionsGenericApplyUpdates(options, profileOptionsVersionUpdates);
+}
+
+
+/*
+ * Global options
+ *
+ * Each profile has an array named "conditionGroups", which is an array of condition groups
+ * which enable the contextual selection of profiles. The structure of the array is as follows:
+ * [
+ * {
+ * conditions: [
+ * {
+ * type: "string",
+ * operator: "string",
+ * value: "string"
+ * },
+ * // ...
+ * ]
+ * },
+ * // ...
+ * ]
+ */
+
+const optionsVersionUpdates = [];
+
+function optionsUpdateVersion(options, defaultProfileOptions) {
+ // Ensure profiles is an array
+ if (!Array.isArray(options.profiles)) {
+ options.profiles = [];
+ }
+
+ // Remove invalid
+ const profiles = options.profiles;
+ for (let i = profiles.length - 1; i >= 0; --i) {
+ if (!utilIsObject(profiles[i])) {
+ profiles.splice(i, 1);
}
- ];
+ }
- optionsSetDefaults(options);
- if (!options.hasOwnProperty('version')) {
- options.version = fixups.length;
+ // Require at least one profile
+ if (profiles.length === 0) {
+ profiles.push({
+ name: 'Default',
+ options: defaultProfileOptions,
+ conditionGroups: []
+ });
}
- while (options.version < fixups.length) {
- fixups[options.version++]();
+ // Ensure profileCurrent is valid
+ const profileCurrent = options.profileCurrent;
+ if (!(
+ typeof profileCurrent === 'number' &&
+ Number.isFinite(profileCurrent) &&
+ Math.floor(profileCurrent) === profileCurrent &&
+ profileCurrent >= 0 &&
+ profileCurrent < profiles.length
+ )) {
+ options.profileCurrent = 0;
}
- return options;
+ // Update profile options
+ for (const profile of profiles) {
+ if (!Array.isArray(profile.conditionGroups)) {
+ profile.conditionGroups = [];
+ }
+ profile.options = profileOptionsUpdateVersion(profile.options);
+ }
+
+ // Generic updates
+ return optionsGenericApplyUpdates(options, optionsVersionUpdates);
}
function optionsLoad() {
return new Promise((resolve, reject) => {
- chrome.storage.local.get(null, store => resolve(store.options));
+ chrome.storage.local.get(['options'], store => {
+ const error = chrome.runtime.lastError;
+ if (error) {
+ reject(error);
+ } else {
+ resolve(store.options);
+ }
+ });
}).then(optionsStr => {
- return optionsStr ? JSON.parse(optionsStr) : {};
- }).catch(error => {
+ if (typeof optionsStr === 'string') {
+ const options = JSON.parse(optionsStr);
+ if (utilIsObject(options)) {
+ return options;
+ }
+ }
+ return {};
+ }).catch(() => {
return {};
}).then(options => {
- return optionsVersion(options);
+ return (
+ Array.isArray(options.profiles) ?
+ optionsUpdateVersion(options, {}) :
+ optionsUpdateVersion({}, options)
+ );
});
}
function optionsSave(options) {
return new Promise((resolve, reject) => {
- chrome.storage.local.set({options: JSON.stringify(options)}, resolve);
- }).then(() => {
- apiOptionsSet(options);
+ chrome.storage.local.set({options: JSON.stringify(options)}, () => {
+ const error = chrome.runtime.lastError;
+ if (error) {
+ reject(error);
+ } else {
+ resolve();
+ }
+ });
});
}