Merge pull request #209 from toasted-nutbread/settings-profiles
Settings profiles
This commit is contained in:
commit
e3fb9603e2
@ -21,9 +21,13 @@ function apiOptionsGet(optionsContext) {
|
|||||||
return utilBackend().getOptions(optionsContext);
|
return utilBackend().getOptions(optionsContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function apiOptionsGetFull() {
|
||||||
|
return utilBackend().getFullOptions();
|
||||||
|
}
|
||||||
|
|
||||||
async function apiOptionsSave(source) {
|
async function apiOptionsSave(source) {
|
||||||
const backend = utilBackend();
|
const backend = utilBackend();
|
||||||
const options = await backend.getFullOptions();
|
const options = await apiOptionsGetFull();
|
||||||
await optionsSave(options);
|
await optionsSave(options);
|
||||||
backend.onOptionsUpdated(source);
|
backend.onOptionsUpdated(source);
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,15 @@ class Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getOptionsSync(optionsContext) {
|
getOptionsSync(optionsContext) {
|
||||||
return this.options;
|
return this.getProfileSync(optionsContext).options;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProfileSync(optionsContext) {
|
||||||
|
const profiles = this.options.profiles;
|
||||||
|
if (typeof optionsContext.index === 'number') {
|
||||||
|
return profiles[optionsContext.index];
|
||||||
|
}
|
||||||
|
return this.options.profiles[this.options.profileCurrent];
|
||||||
}
|
}
|
||||||
|
|
||||||
setExtensionBadgeBackgroundColor(color) {
|
setExtensionBadgeBackgroundColor(color) {
|
||||||
|
@ -17,7 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
function optionsApplyUpdates(options, updates) {
|
/*
|
||||||
|
* Generic options functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
function optionsGenericApplyUpdates(options, updates) {
|
||||||
const targetVersion = updates.length;
|
const targetVersion = updates.length;
|
||||||
const currentVersion = options.version;
|
const currentVersion = options.version;
|
||||||
if (typeof currentVersion === 'number' && Number.isFinite(currentVersion)) {
|
if (typeof currentVersion === 'number' && Number.isFinite(currentVersion)) {
|
||||||
@ -33,7 +37,12 @@ function optionsApplyUpdates(options, updates) {
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
const optionsVersionUpdates = [
|
|
||||||
|
/*
|
||||||
|
* Per-profile options
|
||||||
|
*/
|
||||||
|
|
||||||
|
const profileOptionsVersionUpdates = [
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@ -48,7 +57,7 @@ const optionsVersionUpdates = [
|
|||||||
options.scanning.modifier = options.scanning.requireShift ? 'shift' : 'none';
|
options.scanning.modifier = options.scanning.requireShift ? 'shift' : 'none';
|
||||||
},
|
},
|
||||||
(options) => {
|
(options) => {
|
||||||
const fieldTemplatesDefault = optionsFieldTemplates();
|
const fieldTemplatesDefault = profileOptionsGetDefaultFieldTemplates();
|
||||||
options.general.resultOutputMode = options.general.groupResults ? 'group' : 'split';
|
options.general.resultOutputMode = options.general.groupResults ? 'group' : 'split';
|
||||||
options.anki.fieldTemplates = (
|
options.anki.fieldTemplates = (
|
||||||
(utilStringHashCode(options.anki.fieldTemplates) !== -805327496) ?
|
(utilStringHashCode(options.anki.fieldTemplates) !== -805327496) ?
|
||||||
@ -58,17 +67,17 @@ const optionsVersionUpdates = [
|
|||||||
},
|
},
|
||||||
(options) => {
|
(options) => {
|
||||||
if (utilStringHashCode(options.anki.fieldTemplates) === 1285806040) {
|
if (utilStringHashCode(options.anki.fieldTemplates) === 1285806040) {
|
||||||
options.anki.fieldTemplates = optionsFieldTemplates();
|
options.anki.fieldTemplates = profileOptionsGetDefaultFieldTemplates();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(options) => {
|
(options) => {
|
||||||
if (utilStringHashCode(options.anki.fieldTemplates) === -250091611) {
|
if (utilStringHashCode(options.anki.fieldTemplates) === -250091611) {
|
||||||
options.anki.fieldTemplates = optionsFieldTemplates();
|
options.anki.fieldTemplates = profileOptionsGetDefaultFieldTemplates();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
function optionsFieldTemplates() {
|
function profileOptionsGetDefaultFieldTemplates() {
|
||||||
return `
|
return `
|
||||||
{{#*inline "glossary-single"}}
|
{{#*inline "glossary-single"}}
|
||||||
{{~#unless brief~}}
|
{{~#unless brief~}}
|
||||||
@ -234,7 +243,7 @@ function optionsFieldTemplates() {
|
|||||||
`.trim();
|
`.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
function optionsCreateDefaults() {
|
function profileOptionsCreateDefaults() {
|
||||||
return {
|
return {
|
||||||
general: {
|
general: {
|
||||||
enable: true,
|
enable: true,
|
||||||
@ -286,13 +295,13 @@ function optionsCreateDefaults() {
|
|||||||
screenshot: {format: 'png', quality: 92},
|
screenshot: {format: 'png', quality: 92},
|
||||||
terms: {deck: '', model: '', fields: {}},
|
terms: {deck: '', model: '', fields: {}},
|
||||||
kanji: {deck: '', model: '', fields: {}},
|
kanji: {deck: '', model: '', fields: {}},
|
||||||
fieldTemplates: optionsFieldTemplates()
|
fieldTemplates: profileOptionsGetDefaultFieldTemplates()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function optionsSetDefaults(options) {
|
function profileOptionsSetDefaults(options) {
|
||||||
const defaults = optionsCreateDefaults();
|
const defaults = profileOptionsCreateDefaults();
|
||||||
|
|
||||||
const combine = (target, source) => {
|
const combine = (target, source) => {
|
||||||
for (const key in source) {
|
for (const key in source) {
|
||||||
@ -312,9 +321,59 @@ function optionsSetDefaults(options) {
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
function optionsVersion(options) {
|
function profileOptionsUpdateVersion(options) {
|
||||||
optionsSetDefaults(options);
|
profileOptionsSetDefaults(options);
|
||||||
return optionsApplyUpdates(options, optionsVersionUpdates);
|
return optionsGenericApplyUpdates(options, profileOptionsVersionUpdates);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global options
|
||||||
|
*/
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require at least one profile
|
||||||
|
if (profiles.length === 0) {
|
||||||
|
profiles.push({
|
||||||
|
name: 'Default',
|
||||||
|
options: defaultProfileOptions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update profile options
|
||||||
|
for (const profile of profiles) {
|
||||||
|
profile.options = profileOptionsUpdateVersion(profile.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic updates
|
||||||
|
return optionsGenericApplyUpdates(options, optionsVersionUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
function optionsLoad() {
|
function optionsLoad() {
|
||||||
@ -338,7 +397,11 @@ function optionsLoad() {
|
|||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
return {};
|
return {};
|
||||||
}).then(options => {
|
}).then(options => {
|
||||||
return optionsVersion(options);
|
return (
|
||||||
|
Array.isArray(options.profiles) ?
|
||||||
|
optionsUpdateVersion(options, {}) :
|
||||||
|
optionsUpdateVersion({}, options)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
263
ext/bg/js/settings-profiles.js
Normal file
263
ext/bg/js/settings-profiles.js
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Alex Yatskov <alex@foosoft.net>
|
||||||
|
* Author: Alex Yatskov <alex@foosoft.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let currentProfileIndex = 0;
|
||||||
|
|
||||||
|
function getOptionsContext() {
|
||||||
|
return {
|
||||||
|
index: currentProfileIndex
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function profileOptionsSetup() {
|
||||||
|
const optionsFull = await apiOptionsGetFull();
|
||||||
|
currentProfileIndex = optionsFull.profileCurrent;
|
||||||
|
|
||||||
|
profileOptionsSetupEventListeners();
|
||||||
|
await profileOptionsUpdateTarget(optionsFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
function profileOptionsSetupEventListeners() {
|
||||||
|
$('#profile-target').change(utilAsync(onTargetProfileChanged));
|
||||||
|
$('#profile-name').change(onProfileNameChanged);
|
||||||
|
$('#profile-add').click(utilAsync(onProfileAdd));
|
||||||
|
$('#profile-remove').click(utilAsync(onProfileRemove));
|
||||||
|
$('#profile-remove-confirm').click(utilAsync(onProfileRemoveConfirm));
|
||||||
|
$('#profile-copy').click(utilAsync(onProfileCopy));
|
||||||
|
$('#profile-copy-confirm').click(utilAsync(onProfileCopyConfirm));
|
||||||
|
$('#profile-move-up').click(() => onProfileMove(-1));
|
||||||
|
$('#profile-move-down').click(() => onProfileMove(1));
|
||||||
|
$('.profile-form').find('input, select, textarea').not('.profile-form-manual').change(utilAsync(onProfileOptionsChanged));
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryGetIntegerValue(selector, min, max) {
|
||||||
|
const value = parseInt($(selector).val(), 10);
|
||||||
|
return (
|
||||||
|
typeof value === 'number' &&
|
||||||
|
Number.isFinite(value) &&
|
||||||
|
Math.floor(value) === value &&
|
||||||
|
value >= min &&
|
||||||
|
value < max
|
||||||
|
) ? value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function profileFormRead(optionsFull) {
|
||||||
|
const profile = optionsFull.profiles[currentProfileIndex];
|
||||||
|
|
||||||
|
// Current profile
|
||||||
|
const index = tryGetIntegerValue('#profile-active', 0, optionsFull.profiles.length);
|
||||||
|
if (index !== null) {
|
||||||
|
optionsFull.profileCurrent = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Profile name
|
||||||
|
profile.name = $('#profile-name').val();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function profileFormWrite(optionsFull) {
|
||||||
|
const profile = optionsFull.profiles[currentProfileIndex];
|
||||||
|
|
||||||
|
profileOptionsPopulateSelect($('#profile-active'), optionsFull.profiles, optionsFull.profileCurrent, null);
|
||||||
|
profileOptionsPopulateSelect($('#profile-target'), optionsFull.profiles, currentProfileIndex, null);
|
||||||
|
$('#profile-remove').prop('disabled', optionsFull.profiles.length <= 1);
|
||||||
|
$('#profile-copy').prop('disabled', optionsFull.profiles.length <= 1);
|
||||||
|
$('#profile-move-up').prop('disabled', currentProfileIndex <= 0);
|
||||||
|
$('#profile-move-down').prop('disabled', currentProfileIndex >= optionsFull.profiles.length - 1);
|
||||||
|
|
||||||
|
$('#profile-name').val(profile.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function profileOptionsPopulateSelect(select, profiles, currentValue, ignoreIndices) {
|
||||||
|
select.empty();
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = 0; i < profiles.length; ++i) {
|
||||||
|
if (ignoreIndices !== null && ignoreIndices.indexOf(i) >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const profile = profiles[i];
|
||||||
|
select.append($(`<option value="${i}">${profile.name}</option>`));
|
||||||
|
}
|
||||||
|
|
||||||
|
select.val(`${currentValue}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function profileOptionsUpdateTarget(optionsFull) {
|
||||||
|
profileFormWrite(optionsFull);
|
||||||
|
|
||||||
|
const optionsContext = getOptionsContext();
|
||||||
|
const options = await apiOptionsGet(optionsContext);
|
||||||
|
await formWrite(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function profileOptionsCreateCopyName(name, profiles, maxUniqueAttempts) {
|
||||||
|
let space, index, prefix, suffix;
|
||||||
|
const match = /^([\w\W]*\(Copy)((\s+)(\d+))?(\)\s*)$/.exec(name);
|
||||||
|
if (match === null) {
|
||||||
|
prefix = `${name} (Copy`;
|
||||||
|
space = '';
|
||||||
|
index = '';
|
||||||
|
suffix = ')';
|
||||||
|
} else {
|
||||||
|
prefix = match[1];
|
||||||
|
suffix = match[5];
|
||||||
|
if (typeof match[2] === 'string') {
|
||||||
|
space = match[3];
|
||||||
|
index = parseInt(match[4], 10) + 1;
|
||||||
|
} else {
|
||||||
|
space = ' ';
|
||||||
|
index = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
while (true) {
|
||||||
|
const newName = `${prefix}${space}${index}${suffix}`;
|
||||||
|
if (i++ >= maxUniqueAttempts || profiles.findIndex(profile => profile.name === newName) < 0) {
|
||||||
|
return newName;
|
||||||
|
}
|
||||||
|
if (typeof index !== 'number') {
|
||||||
|
index = 2;
|
||||||
|
space = ' ';
|
||||||
|
} else {
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onProfileOptionsChanged(e) {
|
||||||
|
if (!e.originalEvent && !e.isTrigger) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionsFull = await apiOptionsGetFull();
|
||||||
|
await profileFormRead(optionsFull);
|
||||||
|
await apiOptionsSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onTargetProfileChanged() {
|
||||||
|
const optionsFull = await apiOptionsGetFull();
|
||||||
|
const index = tryGetIntegerValue('#profile-target', 0, optionsFull.profiles.length);
|
||||||
|
if (index === null || currentProfileIndex === index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentProfileIndex = index;
|
||||||
|
|
||||||
|
await profileOptionsUpdateTarget(optionsFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onProfileAdd() {
|
||||||
|
const optionsFull = await apiOptionsGetFull();
|
||||||
|
const profile = utilBackgroundIsolate(optionsFull.profiles[currentProfileIndex]);
|
||||||
|
profile.name = profileOptionsCreateCopyName(profile.name, optionsFull.profiles, 100);
|
||||||
|
optionsFull.profiles.push(profile);
|
||||||
|
currentProfileIndex = optionsFull.profiles.length - 1;
|
||||||
|
await profileOptionsUpdateTarget(optionsFull);
|
||||||
|
await apiOptionsSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onProfileRemove(e) {
|
||||||
|
if (e.shiftKey) {
|
||||||
|
return await onProfileRemoveConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionsFull = await apiOptionsGetFull();
|
||||||
|
if (optionsFull.profiles.length <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profile = optionsFull.profiles[currentProfileIndex];
|
||||||
|
|
||||||
|
$('#profile-remove-modal-profile-name').text(profile.name);
|
||||||
|
$('#profile-remove-modal').modal('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onProfileRemoveConfirm() {
|
||||||
|
$('#profile-remove-modal').modal('hide');
|
||||||
|
|
||||||
|
const optionsFull = await apiOptionsGetFull();
|
||||||
|
if (optionsFull.profiles.length <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsFull.profiles.splice(currentProfileIndex, 1);
|
||||||
|
|
||||||
|
if (currentProfileIndex >= optionsFull.profiles.length) {
|
||||||
|
--currentProfileIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionsFull.profileCurrent >= optionsFull.profiles.length) {
|
||||||
|
optionsFull.profileCurrent = optionsFull.profiles.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
await profileOptionsUpdateTarget(optionsFull);
|
||||||
|
await apiOptionsSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onProfileNameChanged() {
|
||||||
|
$('#profile-active, #profile-target').find(`[value="${currentProfileIndex}"]`).text(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onProfileMove(offset) {
|
||||||
|
const optionsFull = await apiOptionsGetFull();
|
||||||
|
const index = currentProfileIndex + offset;
|
||||||
|
if (index < 0 || index >= optionsFull.profiles.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profile = optionsFull.profiles[currentProfileIndex];
|
||||||
|
optionsFull.profiles.splice(currentProfileIndex, 1);
|
||||||
|
optionsFull.profiles.splice(index, 0, profile);
|
||||||
|
|
||||||
|
if (optionsFull.profileCurrent === currentProfileIndex) {
|
||||||
|
optionsFull.profileCurrent = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentProfileIndex = index;
|
||||||
|
|
||||||
|
await profileOptionsUpdateTarget(optionsFull);
|
||||||
|
await settingsSaveOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onProfileCopy() {
|
||||||
|
const optionsFull = await apiOptionsGetFull();
|
||||||
|
if (optionsFull.profiles.length <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
profileOptionsPopulateSelect($('#profile-copy-source'), optionsFull.profiles, currentProfileIndex === 0 ? 1 : 0, [currentProfileIndex]);
|
||||||
|
$('#profile-copy-modal').modal('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onProfileCopyConfirm() {
|
||||||
|
$('#profile-copy-modal').modal('hide');
|
||||||
|
|
||||||
|
const optionsFull = await apiOptionsGetFull();
|
||||||
|
const index = tryGetIntegerValue('#profile-copy-source', 0, optionsFull.profiles.length);
|
||||||
|
if (index === null || index === currentProfileIndex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profileOptions = utilBackgroundIsolate(optionsFull.profiles[index].options);
|
||||||
|
optionsFull.profiles[currentProfileIndex].options = profileOptions;
|
||||||
|
|
||||||
|
await profileOptionsUpdateTarget(optionsFull);
|
||||||
|
await settingsSaveOptions();
|
||||||
|
}
|
@ -16,10 +16,9 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function getOptionsContext() {
|
async function getOptionsArray() {
|
||||||
return {
|
const optionsFull = await apiOptionsGetFull();
|
||||||
depth: 0
|
return optionsFull.profiles.map(profile => profile.options);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function formRead(options) {
|
async function formRead(options) {
|
||||||
@ -239,11 +238,8 @@ async function onFormOptionsChanged(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onReady() {
|
async function onReady() {
|
||||||
const optionsContext = getOptionsContext();
|
|
||||||
const options = await apiOptionsGet(optionsContext);
|
|
||||||
|
|
||||||
formSetupEventListeners();
|
formSetupEventListeners();
|
||||||
await formWrite(options);
|
await profileOptionsSetup();
|
||||||
|
|
||||||
storageInfoInitialize();
|
storageInfoInitialize();
|
||||||
|
|
||||||
@ -424,12 +420,14 @@ async function onDictionaryPurge(e) {
|
|||||||
dictionarySpinnerShow(true);
|
dictionarySpinnerShow(true);
|
||||||
|
|
||||||
await utilDatabasePurge();
|
await utilDatabasePurge();
|
||||||
const optionsContext = getOptionsContext();
|
for (const options of await getOptionsArray()) {
|
||||||
const options = await apiOptionsGet(optionsContext);
|
options.dictionaries = utilBackgroundIsolate({});
|
||||||
options.dictionaries = utilBackgroundIsolate({});
|
options.general.mainDictionary = '';
|
||||||
options.general.mainDictionary = '';
|
}
|
||||||
await settingsSaveOptions();
|
await settingsSaveOptions();
|
||||||
|
|
||||||
|
const optionsContext = getOptionsContext();
|
||||||
|
const options = await apiOptionsGet(optionsContext);
|
||||||
await dictionaryGroupsPopulate(options);
|
await dictionaryGroupsPopulate(options);
|
||||||
await formMainDictionaryOptionsPopulate(options);
|
await formMainDictionaryOptionsPopulate(options);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -466,24 +464,25 @@ async function onDictionaryImport(e) {
|
|||||||
|
|
||||||
const exceptions = [];
|
const exceptions = [];
|
||||||
const summary = await utilDatabaseImport(e.target.files[0], updateProgress, exceptions);
|
const summary = await utilDatabaseImport(e.target.files[0], updateProgress, exceptions);
|
||||||
const optionsContext = getOptionsContext();
|
for (const options of await getOptionsArray()) {
|
||||||
const options = await apiOptionsGet(optionsContext);
|
options.dictionaries[summary.title] = utilBackgroundIsolate({
|
||||||
options.dictionaries[summary.title] = utilBackgroundIsolate({
|
enabled: true,
|
||||||
enabled: true,
|
priority: 0,
|
||||||
priority: 0,
|
allowSecondarySearches: false
|
||||||
allowSecondarySearches: false
|
});
|
||||||
});
|
if (summary.sequenced && options.general.mainDictionary === '') {
|
||||||
if (summary.sequenced && options.general.mainDictionary === '') {
|
options.general.mainDictionary = summary.title;
|
||||||
options.general.mainDictionary = summary.title;
|
}
|
||||||
}
|
}
|
||||||
|
await settingsSaveOptions();
|
||||||
|
|
||||||
if (exceptions.length > 0) {
|
if (exceptions.length > 0) {
|
||||||
exceptions.push(`Dictionary may not have been imported properly: ${exceptions.length} error${exceptions.length === 1 ? '' : 's'} reported.`);
|
exceptions.push(`Dictionary may not have been imported properly: ${exceptions.length} error${exceptions.length === 1 ? '' : 's'} reported.`);
|
||||||
dictionaryErrorsShow(exceptions);
|
dictionaryErrorsShow(exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
await settingsSaveOptions();
|
const optionsContext = getOptionsContext();
|
||||||
|
const options = await apiOptionsGet(optionsContext);
|
||||||
await dictionaryGroupsPopulate(options);
|
await dictionaryGroupsPopulate(options);
|
||||||
await formMainDictionaryOptionsPopulate(options);
|
await formMainDictionaryOptionsPopulate(options);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -643,7 +642,7 @@ async function onAnkiFieldTemplatesReset(e) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const optionsContext = getOptionsContext();
|
const optionsContext = getOptionsContext();
|
||||||
const options = await apiOptionsGet(optionsContext);
|
const options = await apiOptionsGet(optionsContext);
|
||||||
const fieldTemplates = optionsFieldTemplates();
|
const fieldTemplates = profileOptionsGetDefaultFieldTemplates();
|
||||||
options.anki.fieldTemplates = fieldTemplates;
|
options.anki.fieldTemplates = fieldTemplates;
|
||||||
$('#field-templates').val(fieldTemplates);
|
$('#field-templates').val(fieldTemplates);
|
||||||
await settingsSaveOptions();
|
await settingsSaveOptions();
|
||||||
|
@ -67,6 +67,77 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
|
<div class="profile-form">
|
||||||
|
<h3>Profiles</h3>
|
||||||
|
|
||||||
|
<p class="help-block">
|
||||||
|
Profiles allow you to create multiple configurations and quickly switch between them.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="profile-active">Active profile</label>
|
||||||
|
<select class="form-control" id="profile-active"></select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="profile-target">Modifying profile</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button class="btn btn-default" id="profile-add" title="Add"><span class="glyphicon glyphicon-plus"></span></button>
|
||||||
|
<button class="btn btn-default" id="profile-move-up" title="Move up"><span class="glyphicon glyphicon-arrow-up"></span></button>
|
||||||
|
<button class="btn btn-default" id="profile-move-down" title="Move down"><span class="glyphicon glyphicon-arrow-down"></span></button>
|
||||||
|
<button class="btn btn-default" id="profile-copy" title="Copy"><span class="glyphicon glyphicon-copy"></span></button>
|
||||||
|
</div>
|
||||||
|
<select class="form-control profile-form-manual" id="profile-target"></select>
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button class="btn btn-danger" id="profile-remove" title="Remove"><span class="glyphicon glyphicon-remove"></span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="profile-name">Profile name</label>
|
||||||
|
<input type="text" id="profile-name" class="form-control">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" tabindex="-1" role="dialog" id="profile-copy-modal">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
|
<h4 class="modal-title">Copy Profile</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Select which profile to copy options from:</p>
|
||||||
|
<select class="form-control" id="profile-copy-source"></select>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="profile-copy-confirm">Copy Profile</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" tabindex="-1" role="dialog" id="profile-remove-modal">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
|
<h4 class="modal-title">Confirm profile removal</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Are you sure you want to delete the profile <em id="profile-remove-modal-profile-name"></em>?
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||||
|
<button type="button" class="btn btn-danger" id="profile-remove-confirm">Remove Profile</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3>General Options</h3>
|
<h3>General Options</h3>
|
||||||
|
|
||||||
@ -498,6 +569,7 @@
|
|||||||
<script src="/bg/js/templates.js"></script>
|
<script src="/bg/js/templates.js"></script>
|
||||||
<script src="/bg/js/util.js"></script>
|
<script src="/bg/js/util.js"></script>
|
||||||
|
|
||||||
|
<script src="/bg/js/settings-profiles.js"></script>
|
||||||
<script src="/bg/js/settings.js"></script>
|
<script src="/bg/js/settings.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user