Merge pull request #218 from toasted-nutbread/settings-profile-conditions
Settings profile conditions
This commit is contained in:
commit
ba2858309e
@ -16,11 +16,13 @@
|
|||||||
<script src="/bg/js/api.js"></script>
|
<script src="/bg/js/api.js"></script>
|
||||||
<script src="/bg/js/audio.js"></script>
|
<script src="/bg/js/audio.js"></script>
|
||||||
<script src="/bg/js/backend-api-forwarder.js"></script>
|
<script src="/bg/js/backend-api-forwarder.js"></script>
|
||||||
|
<script src="/bg/js/conditions.js"></script>
|
||||||
<script src="/bg/js/database.js"></script>
|
<script src="/bg/js/database.js"></script>
|
||||||
<script src="/bg/js/deinflector.js"></script>
|
<script src="/bg/js/deinflector.js"></script>
|
||||||
<script src="/bg/js/dictionary.js"></script>
|
<script src="/bg/js/dictionary.js"></script>
|
||||||
<script src="/bg/js/handlebars.js"></script>
|
<script src="/bg/js/handlebars.js"></script>
|
||||||
<script src="/bg/js/options.js"></script>
|
<script src="/bg/js/options.js"></script>
|
||||||
|
<script src="/bg/js/profile-conditions.js"></script>
|
||||||
<script src="/bg/js/request.js"></script>
|
<script src="/bg/js/request.js"></script>
|
||||||
<script src="/bg/js/templates.js"></script>
|
<script src="/bg/js/templates.js"></script>
|
||||||
<script src="/bg/js/translator.js"></script>
|
<script src="/bg/js/translator.js"></script>
|
||||||
|
@ -140,7 +140,10 @@ async function apiCommandExec(command) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
toggle: async () => {
|
toggle: async () => {
|
||||||
const optionsContext = {depth: 0};
|
const optionsContext = {
|
||||||
|
depth: 0,
|
||||||
|
url: window.location.href
|
||||||
|
};
|
||||||
const options = await apiOptionsGet(optionsContext);
|
const options = await apiOptionsGet(optionsContext);
|
||||||
options.general.enable = !options.general.enable;
|
options.general.enable = !options.general.enable;
|
||||||
await apiOptionsSave('popup');
|
await apiOptionsSave('popup');
|
||||||
|
@ -23,7 +23,8 @@ class Backend {
|
|||||||
this.anki = new AnkiNull();
|
this.anki = new AnkiNull();
|
||||||
this.options = null;
|
this.options = null;
|
||||||
this.optionsContext = {
|
this.optionsContext = {
|
||||||
depth: 0
|
depth: 0,
|
||||||
|
url: window.location.href
|
||||||
};
|
};
|
||||||
|
|
||||||
this.isPreparedResolve = null;
|
this.isPreparedResolve = null;
|
||||||
@ -173,7 +174,40 @@ class Backend {
|
|||||||
if (typeof optionsContext.index === 'number') {
|
if (typeof optionsContext.index === 'number') {
|
||||||
return profiles[optionsContext.index];
|
return profiles[optionsContext.index];
|
||||||
}
|
}
|
||||||
return this.options.profiles[this.options.profileCurrent];
|
const profile = this.getProfileFromContext(optionsContext);
|
||||||
|
return profile !== null ? profile : this.options.profiles[this.options.profileCurrent];
|
||||||
|
}
|
||||||
|
|
||||||
|
getProfileFromContext(optionsContext) {
|
||||||
|
for (const profile of this.options.profiles) {
|
||||||
|
const conditionGroups = profile.conditionGroups;
|
||||||
|
if (conditionGroups.length > 0 && Backend.testConditionGroups(conditionGroups, optionsContext)) {
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static testConditionGroups(conditionGroups, data) {
|
||||||
|
if (conditionGroups.length === 0) { return false; }
|
||||||
|
|
||||||
|
for (const conditionGroup of conditionGroups) {
|
||||||
|
const conditions = conditionGroup.conditions;
|
||||||
|
if (conditions.length > 0 && Backend.testConditions(conditions, data)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static testConditions(conditions, data) {
|
||||||
|
for (const condition of conditions) {
|
||||||
|
if (!conditionsTestValue(profileConditionsDescriptor, condition.type, condition.operator, condition.value, data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
setExtensionBadgeBackgroundColor(color) {
|
setExtensionBadgeBackgroundColor(color) {
|
||||||
|
326
ext/bg/js/conditions-ui.js
Normal file
326
ext/bg/js/conditions-ui.js
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class ConditionsUI {
|
||||||
|
static instantiateTemplate(templateSelector) {
|
||||||
|
const template = document.querySelector(templateSelector);
|
||||||
|
const content = document.importNode(template.content, true);
|
||||||
|
return $(content.firstChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConditionsUI.Container = class Container {
|
||||||
|
constructor(conditionDescriptors, conditionNameDefault, conditionGroups, container, addButton) {
|
||||||
|
this.children = [];
|
||||||
|
this.conditionDescriptors = conditionDescriptors;
|
||||||
|
this.conditionNameDefault = conditionNameDefault;
|
||||||
|
this.conditionGroups = conditionGroups;
|
||||||
|
this.container = container;
|
||||||
|
this.addButton = addButton;
|
||||||
|
|
||||||
|
this.container.empty();
|
||||||
|
|
||||||
|
for (const conditionGroup of conditionGroups) {
|
||||||
|
this.children.push(new ConditionsUI.ConditionGroup(this, conditionGroup));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addButton.on('click', () => this.onAddConditionGroup());
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
for (const child of this.children) {
|
||||||
|
child.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addButton.off('click');
|
||||||
|
this.container.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
// Override
|
||||||
|
}
|
||||||
|
|
||||||
|
isolate(object) {
|
||||||
|
// Override
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(child) {
|
||||||
|
const index = this.children.indexOf(child);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
child.cleanup();
|
||||||
|
this.children.splice(index, 1);
|
||||||
|
this.conditionGroups.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddConditionGroup() {
|
||||||
|
const conditionGroup = this.isolate({
|
||||||
|
conditions: [this.createDefaultCondition(this.conditionNameDefault)]
|
||||||
|
});
|
||||||
|
this.conditionGroups.push(conditionGroup);
|
||||||
|
this.save();
|
||||||
|
this.children.push(new ConditionsUI.ConditionGroup(this, conditionGroup));
|
||||||
|
}
|
||||||
|
|
||||||
|
createDefaultCondition(type) {
|
||||||
|
let operator = '';
|
||||||
|
let value = '';
|
||||||
|
if (this.conditionDescriptors.hasOwnProperty(type)) {
|
||||||
|
const conditionDescriptor = this.conditionDescriptors[type];
|
||||||
|
operator = conditionDescriptor.defaultOperator;
|
||||||
|
({value} = this.getOperatorDefaultValue(type, operator));
|
||||||
|
if (typeof value === 'undefined') {
|
||||||
|
value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {type, operator, value};
|
||||||
|
}
|
||||||
|
|
||||||
|
getOperatorDefaultValue(type, operator) {
|
||||||
|
if (this.conditionDescriptors.hasOwnProperty(type)) {
|
||||||
|
const conditionDescriptor = this.conditionDescriptors[type];
|
||||||
|
if (conditionDescriptor.operators.hasOwnProperty(operator)) {
|
||||||
|
const operatorDescriptor = conditionDescriptor.operators[operator];
|
||||||
|
if (operatorDescriptor.hasOwnProperty('defaultValue')) {
|
||||||
|
return {value: operatorDescriptor.defaultValue, fromOperator: true};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conditionDescriptor.hasOwnProperty('defaultValue')) {
|
||||||
|
return {value: conditionDescriptor.defaultValue, fromOperator: false};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {fromOperator: false};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ConditionsUI.ConditionGroup = class ConditionGroup {
|
||||||
|
constructor(parent, conditionGroup) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.children = [];
|
||||||
|
this.conditionGroup = conditionGroup;
|
||||||
|
this.container = $('<div>').addClass('condition-group').appendTo(parent.container);
|
||||||
|
this.options = ConditionsUI.instantiateTemplate('#condition-group-options-template').appendTo(parent.container);
|
||||||
|
this.separator = ConditionsUI.instantiateTemplate('#condition-group-separator-template').appendTo(parent.container);
|
||||||
|
this.addButton = this.options.find('.condition-add');
|
||||||
|
|
||||||
|
for (const condition of conditionGroup.conditions) {
|
||||||
|
this.children.push(new ConditionsUI.Condition(this, condition));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addButton.on('click', () => this.onAddCondition());
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
for (const child of this.children) {
|
||||||
|
child.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addButton.off('click');
|
||||||
|
this.container.remove();
|
||||||
|
this.options.remove();
|
||||||
|
this.separator.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.parent.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
isolate(object) {
|
||||||
|
return this.parent.isolate(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(child) {
|
||||||
|
const index = this.children.indexOf(child);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
child.cleanup();
|
||||||
|
this.children.splice(index, 1);
|
||||||
|
this.conditionGroup.conditions.splice(index, 1);
|
||||||
|
|
||||||
|
if (this.children.length === 0) {
|
||||||
|
this.parent.remove(this, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddCondition() {
|
||||||
|
const condition = this.isolate(this.parent.createDefaultCondition(this.parent.conditionNameDefault));
|
||||||
|
this.conditionGroup.conditions.push(condition);
|
||||||
|
this.children.push(new ConditionsUI.Condition(this, condition));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ConditionsUI.Condition = class Condition {
|
||||||
|
constructor(parent, condition) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.condition = condition;
|
||||||
|
this.container = ConditionsUI.instantiateTemplate('#condition-template').appendTo(parent.container);
|
||||||
|
this.input = this.container.find('input');
|
||||||
|
this.typeSelect = this.container.find('.condition-type');
|
||||||
|
this.operatorSelect = this.container.find('.condition-operator');
|
||||||
|
this.removeButton = this.container.find('.condition-remove');
|
||||||
|
|
||||||
|
this.updateTypes();
|
||||||
|
this.updateOperators();
|
||||||
|
this.updateInput();
|
||||||
|
|
||||||
|
this.input.on('change', () => this.onInputChanged());
|
||||||
|
this.typeSelect.on('change', () => this.onConditionTypeChanged());
|
||||||
|
this.operatorSelect.on('change', () => this.onConditionOperatorChanged());
|
||||||
|
this.removeButton.on('click', () => this.onRemoveClicked());
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
this.input.off('change');
|
||||||
|
this.typeSelect.off('change');
|
||||||
|
this.operatorSelect.off('change');
|
||||||
|
this.removeButton.off('click');
|
||||||
|
this.container.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.parent.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTypes() {
|
||||||
|
const conditionDescriptors = this.parent.parent.conditionDescriptors;
|
||||||
|
const optionGroup = this.typeSelect.find('optgroup');
|
||||||
|
optionGroup.empty();
|
||||||
|
for (const type of Object.keys(conditionDescriptors)) {
|
||||||
|
const conditionDescriptor = conditionDescriptors[type];
|
||||||
|
$('<option>').val(type).text(conditionDescriptor.name).appendTo(optionGroup);
|
||||||
|
}
|
||||||
|
this.typeSelect.val(this.condition.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOperators() {
|
||||||
|
const conditionDescriptors = this.parent.parent.conditionDescriptors;
|
||||||
|
const optionGroup = this.operatorSelect.find('optgroup');
|
||||||
|
optionGroup.empty();
|
||||||
|
|
||||||
|
const type = this.condition.type;
|
||||||
|
if (conditionDescriptors.hasOwnProperty(type)) {
|
||||||
|
const conditionDescriptor = conditionDescriptors[type];
|
||||||
|
const operators = conditionDescriptor.operators;
|
||||||
|
for (const operatorName of Object.keys(operators)) {
|
||||||
|
const operatorDescriptor = operators[operatorName];
|
||||||
|
$('<option>').val(operatorName).text(operatorDescriptor.name).appendTo(optionGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.operatorSelect.val(this.condition.operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInput() {
|
||||||
|
const conditionDescriptors = this.parent.parent.conditionDescriptors;
|
||||||
|
const {type, operator} = this.condition;
|
||||||
|
const props = {
|
||||||
|
placeholder: '',
|
||||||
|
type: 'text'
|
||||||
|
};
|
||||||
|
|
||||||
|
const objects = [];
|
||||||
|
if (conditionDescriptors.hasOwnProperty(type)) {
|
||||||
|
const conditionDescriptor = conditionDescriptors[type];
|
||||||
|
objects.push(conditionDescriptor);
|
||||||
|
if (conditionDescriptor.operators.hasOwnProperty(operator)) {
|
||||||
|
const operatorDescriptor = conditionDescriptor.operators[operator];
|
||||||
|
objects.push(operatorDescriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const object of objects) {
|
||||||
|
if (object.hasOwnProperty('placeholder')) {
|
||||||
|
props.placeholder = object.placeholder;
|
||||||
|
}
|
||||||
|
if (object.type === 'number') {
|
||||||
|
props.type = 'number';
|
||||||
|
for (const prop of ['step', 'min', 'max']) {
|
||||||
|
if (object.hasOwnProperty(prop)) {
|
||||||
|
props[prop] = object[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const prop in props) {
|
||||||
|
this.input.prop(prop, props[prop]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const {valid} = this.validateValue(this.condition.value);
|
||||||
|
this.input.toggleClass('is-invalid', !valid);
|
||||||
|
this.input.val(this.condition.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
validateValue(value) {
|
||||||
|
const conditionDescriptors = this.parent.parent.conditionDescriptors;
|
||||||
|
let valid = true;
|
||||||
|
try {
|
||||||
|
value = conditionsNormalizeOptionValue(
|
||||||
|
conditionDescriptors,
|
||||||
|
this.condition.type,
|
||||||
|
this.condition.operator,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
return {valid, value};
|
||||||
|
}
|
||||||
|
|
||||||
|
onInputChanged() {
|
||||||
|
const {valid, value} = this.validateValue(this.input.val());
|
||||||
|
this.input.toggleClass('is-invalid', !valid);
|
||||||
|
this.input.val(value);
|
||||||
|
this.condition.value = value;
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
onConditionTypeChanged() {
|
||||||
|
const type = this.typeSelect.val();
|
||||||
|
const {operator, value} = this.parent.parent.createDefaultCondition(type);
|
||||||
|
this.condition.type = type;
|
||||||
|
this.condition.operator = operator;
|
||||||
|
this.condition.value = value;
|
||||||
|
this.save();
|
||||||
|
this.updateOperators();
|
||||||
|
this.updateInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
onConditionOperatorChanged() {
|
||||||
|
const type = this.condition.type;
|
||||||
|
const operator = this.operatorSelect.val();
|
||||||
|
const {value, fromOperator} = this.parent.parent.getOperatorDefaultValue(type, operator);
|
||||||
|
this.condition.operator = operator;
|
||||||
|
if (fromOperator) {
|
||||||
|
this.condition.value = value;
|
||||||
|
}
|
||||||
|
this.save();
|
||||||
|
this.updateInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemoveClicked() {
|
||||||
|
this.parent.remove(this);
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
|
};
|
117
ext/bg/js/conditions.js
Normal file
117
ext/bg/js/conditions.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
function conditionsValidateOptionValue(object, value) {
|
||||||
|
if (object.hasOwnProperty('validate') && !object.validate(value)) {
|
||||||
|
throw new Error('Invalid value for condition');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object.hasOwnProperty('transform')) {
|
||||||
|
value = object.transform(value);
|
||||||
|
|
||||||
|
if (object.hasOwnProperty('validateTransformed') && !object.validateTransformed(value)) {
|
||||||
|
throw new Error('Invalid value for condition');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function conditionsNormalizeOptionValue(descriptors, type, operator, optionValue) {
|
||||||
|
if (!descriptors.hasOwnProperty(type)) {
|
||||||
|
throw new Error('Invalid type');
|
||||||
|
}
|
||||||
|
|
||||||
|
const conditionDescriptor = descriptors[type];
|
||||||
|
if (!conditionDescriptor.operators.hasOwnProperty(operator)) {
|
||||||
|
throw new Error('Invalid operator');
|
||||||
|
}
|
||||||
|
|
||||||
|
const operatorDescriptor = conditionDescriptor.operators[operator];
|
||||||
|
|
||||||
|
let transformedValue = conditionsValidateOptionValue(conditionDescriptor, optionValue);
|
||||||
|
transformedValue = conditionsValidateOptionValue(operatorDescriptor, transformedValue);
|
||||||
|
|
||||||
|
if (operatorDescriptor.hasOwnProperty('transformReverse')) {
|
||||||
|
transformedValue = operatorDescriptor.transformReverse(transformedValue);
|
||||||
|
}
|
||||||
|
return transformedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
function conditionsTestValueThrowing(descriptors, type, operator, optionValue, value) {
|
||||||
|
if (!descriptors.hasOwnProperty(type)) {
|
||||||
|
throw new Error('Invalid type');
|
||||||
|
}
|
||||||
|
|
||||||
|
const conditionDescriptor = descriptors[type];
|
||||||
|
if (!conditionDescriptor.operators.hasOwnProperty(operator)) {
|
||||||
|
throw new Error('Invalid operator');
|
||||||
|
}
|
||||||
|
|
||||||
|
const operatorDescriptor = conditionDescriptor.operators[operator];
|
||||||
|
if (operatorDescriptor.hasOwnProperty('transform')) {
|
||||||
|
if (operatorDescriptor.hasOwnProperty('transformCache')) {
|
||||||
|
const key = `${optionValue}`;
|
||||||
|
const transformCache = operatorDescriptor.transformCache;
|
||||||
|
if (transformCache.hasOwnProperty(key)) {
|
||||||
|
optionValue = transformCache[key];
|
||||||
|
} else {
|
||||||
|
optionValue = operatorDescriptor.transform(optionValue);
|
||||||
|
transformCache[key] = optionValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
optionValue = operatorDescriptor.transform(optionValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return operatorDescriptor.test(value, optionValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function conditionsTestValue(descriptors, type, operator, optionValue, value) {
|
||||||
|
try {
|
||||||
|
return conditionsTestValueThrowing(descriptors, type, operator, optionValue, value);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function conditionsClearCaches(descriptors) {
|
||||||
|
for (const type in descriptors) {
|
||||||
|
if (!descriptors.hasOwnProperty(type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conditionDescriptor = descriptors[type];
|
||||||
|
if (conditionDescriptor.hasOwnProperty('transformCache')) {
|
||||||
|
conditionDescriptor.transformCache = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const operatorDescriptors = conditionDescriptor.operators;
|
||||||
|
for (const operator in operatorDescriptors) {
|
||||||
|
if (!operatorDescriptors.hasOwnProperty(operator)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const operatorDescriptor = operatorDescriptors[operator];
|
||||||
|
if (operatorDescriptor.hasOwnProperty('transformCache')) {
|
||||||
|
operatorDescriptor.transformCache = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,10 @@ $(document).ready(utilAsync(() => {
|
|||||||
$('#open-options').click(() => apiCommandExec('options'));
|
$('#open-options').click(() => apiCommandExec('options'));
|
||||||
$('#open-help').click(() => apiCommandExec('help'));
|
$('#open-help').click(() => apiCommandExec('help'));
|
||||||
|
|
||||||
const optionsContext = {depth: 0};
|
const optionsContext = {
|
||||||
|
depth: 0,
|
||||||
|
url: window.location.href
|
||||||
|
};
|
||||||
apiOptionsGet(optionsContext).then(options => {
|
apiOptionsGet(optionsContext).then(options => {
|
||||||
const toggle = $('#enable-search');
|
const toggle = $('#enable-search');
|
||||||
toggle.prop('checked', options.general.enable).change();
|
toggle.prop('checked', options.general.enable).change();
|
||||||
|
@ -329,6 +329,22 @@ function profileOptionsUpdateVersion(options) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Global options
|
* 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 = [];
|
const optionsVersionUpdates = [];
|
||||||
@ -351,7 +367,8 @@ function optionsUpdateVersion(options, defaultProfileOptions) {
|
|||||||
if (profiles.length === 0) {
|
if (profiles.length === 0) {
|
||||||
profiles.push({
|
profiles.push({
|
||||||
name: 'Default',
|
name: 'Default',
|
||||||
options: defaultProfileOptions
|
options: defaultProfileOptions,
|
||||||
|
conditionGroups: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,6 +386,9 @@ function optionsUpdateVersion(options, defaultProfileOptions) {
|
|||||||
|
|
||||||
// Update profile options
|
// Update profile options
|
||||||
for (const profile of profiles) {
|
for (const profile of profiles) {
|
||||||
|
if (!Array.isArray(profile.conditionGroups)) {
|
||||||
|
profile.conditionGroups = [];
|
||||||
|
}
|
||||||
profile.options = profileOptionsUpdateVersion(profile.options);
|
profile.options = profileOptionsUpdateVersion(profile.options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
85
ext/bg/js/profile-conditions.js
Normal file
85
ext/bg/js/profile-conditions.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
const profileConditionsDescriptor = {
|
||||||
|
popupLevel: {
|
||||||
|
name: 'Popup Level',
|
||||||
|
description: 'Use profile depending on the level of the popup.',
|
||||||
|
placeholder: 'Number',
|
||||||
|
type: 'number',
|
||||||
|
step: 1,
|
||||||
|
defaultValue: 0,
|
||||||
|
defaultOperator: 'equal',
|
||||||
|
transform: (optionValue) => parseInt(optionValue, 10),
|
||||||
|
transformReverse: (transformedOptionValue) => `${transformedOptionValue}`,
|
||||||
|
validateTransformed: (transformedOptionValue) => Number.isFinite(transformedOptionValue),
|
||||||
|
operators: {
|
||||||
|
equal: {
|
||||||
|
name: '=',
|
||||||
|
test: ({depth}, optionValue) => (depth === optionValue)
|
||||||
|
},
|
||||||
|
notEqual: {
|
||||||
|
name: '\u2260',
|
||||||
|
test: ({depth}, optionValue) => (depth !== optionValue)
|
||||||
|
},
|
||||||
|
lessThan: {
|
||||||
|
name: '<',
|
||||||
|
test: ({depth}, optionValue) => (depth < optionValue)
|
||||||
|
},
|
||||||
|
greaterThan: {
|
||||||
|
name: '>',
|
||||||
|
test: ({depth}, optionValue) => (depth > optionValue)
|
||||||
|
},
|
||||||
|
lessThanOrEqual: {
|
||||||
|
name: '\u2264',
|
||||||
|
test: ({depth}, optionValue) => (depth <= optionValue)
|
||||||
|
},
|
||||||
|
greaterThanOrEqual: {
|
||||||
|
name: '\u2265',
|
||||||
|
test: ({depth}, optionValue) => (depth >= optionValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
name: 'URL',
|
||||||
|
description: 'Use profile depending on the URL of the current website.',
|
||||||
|
defaultOperator: 'matchDomain',
|
||||||
|
operators: {
|
||||||
|
matchDomain: {
|
||||||
|
name: 'Matches Domain',
|
||||||
|
placeholder: 'Comma separated list of domains',
|
||||||
|
defaultValue: 'example.com',
|
||||||
|
transformCache: {},
|
||||||
|
transform: (optionValue) => optionValue.split(/[,;\s]+/).map(v => v.trim().toLowerCase()).filter(v => v.length > 0),
|
||||||
|
transformReverse: (transformedOptionValue) => transformedOptionValue.join(', '),
|
||||||
|
validateTransformed: (transformedOptionValue) => (transformedOptionValue.length > 0),
|
||||||
|
test: ({url}, transformedOptionValue) => (transformedOptionValue.indexOf(new URL(url).hostname.toLowerCase()) >= 0)
|
||||||
|
},
|
||||||
|
matchRegExp: {
|
||||||
|
name: 'Matches RegExp',
|
||||||
|
placeholder: 'Regular expression',
|
||||||
|
defaultValue: 'example\\.com',
|
||||||
|
transformCache: {},
|
||||||
|
transform: (optionValue) => new RegExp(optionValue, 'i'),
|
||||||
|
transformReverse: (transformedOptionValue) => transformedOptionValue.source,
|
||||||
|
test: ({url}, transformedOptionValue) => (transformedOptionValue !== null && transformedOptionValue.test(url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -18,7 +18,10 @@
|
|||||||
|
|
||||||
|
|
||||||
async function searchFrontendSetup() {
|
async function searchFrontendSetup() {
|
||||||
const optionsContext = {depth: 0};
|
const optionsContext = {
|
||||||
|
depth: 0,
|
||||||
|
url: window.location.href
|
||||||
|
};
|
||||||
const options = await apiOptionsGet(optionsContext);
|
const options = await apiOptionsGet(optionsContext);
|
||||||
if (!options.scanning.enableOnSearchPage) { return; }
|
if (!options.scanning.enableOnSearchPage) { return; }
|
||||||
|
|
||||||
|
@ -22,7 +22,8 @@ class DisplaySearch extends Display {
|
|||||||
super($('#spinner'), $('#content'));
|
super($('#spinner'), $('#content'));
|
||||||
|
|
||||||
this.optionsContext = {
|
this.optionsContext = {
|
||||||
depth: 0
|
depth: 0,
|
||||||
|
url: window.location.href
|
||||||
};
|
};
|
||||||
|
|
||||||
this.search = $('#search').click(this.onSearch.bind(this));
|
this.search = $('#search').click(this.onSearch.bind(this));
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
let currentProfileIndex = 0;
|
let currentProfileIndex = 0;
|
||||||
|
let profileConditionsContainer = null;
|
||||||
|
|
||||||
function getOptionsContext() {
|
function getOptionsContext() {
|
||||||
return {
|
return {
|
||||||
@ -81,6 +82,23 @@ async function profileFormWrite(optionsFull) {
|
|||||||
$('#profile-move-down').prop('disabled', currentProfileIndex >= optionsFull.profiles.length - 1);
|
$('#profile-move-down').prop('disabled', currentProfileIndex >= optionsFull.profiles.length - 1);
|
||||||
|
|
||||||
$('#profile-name').val(profile.name);
|
$('#profile-name').val(profile.name);
|
||||||
|
|
||||||
|
if (profileConditionsContainer !== null) {
|
||||||
|
profileConditionsContainer.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
profileConditionsContainer = new ConditionsUI.Container(
|
||||||
|
profileConditionsDescriptor,
|
||||||
|
'popupLevel',
|
||||||
|
profile.conditionGroups,
|
||||||
|
$('#profile-condition-groups'),
|
||||||
|
$('#profile-add-condition-group')
|
||||||
|
);
|
||||||
|
profileConditionsContainer.save = () => {
|
||||||
|
apiOptionsSave();
|
||||||
|
conditionsClearCaches(profileConditionsDescriptor);
|
||||||
|
};
|
||||||
|
profileConditionsContainer.isolate = utilBackgroundIsolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
function profileOptionsPopulateSelect(select, profiles, currentValue, ignoreIndices) {
|
function profileOptionsPopulateSelect(select, profiles, currentValue, ignoreIndices) {
|
||||||
|
@ -34,6 +34,55 @@
|
|||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-control.is-invalid {
|
||||||
|
border-color: #f00000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition>.condition-prefix:after {
|
||||||
|
content: "IF";
|
||||||
|
}
|
||||||
|
.condition:nth-child(n+2)>.condition-prefix:after {
|
||||||
|
content: "AND";
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group .condition-prefix,
|
||||||
|
.input-group .condition-group-separator-label {
|
||||||
|
width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.input-group .condition-group-separator-label {
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.input-group .condition-type,
|
||||||
|
.input-group .condition-operator {
|
||||||
|
width: auto;
|
||||||
|
text-align: center;
|
||||||
|
text-align-last: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-group>.condition>div:first-child {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
.condition-group>.condition:nth-child(n+2)>div:first-child {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
}
|
||||||
|
.condition-group>.condition:nth-child(n+2)>div:last-child>button {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
.condition-group>.condition:nth-last-child(n+2)>div:last-child>button {
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
.condition-group-options>.condition-add {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-groups>*:last-of-type {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#custom-popup-css {
|
#custom-popup-css {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 34px;
|
min-height: 34px;
|
||||||
@ -71,7 +120,7 @@
|
|||||||
<h3>Profiles</h3>
|
<h3>Profiles</h3>
|
||||||
|
|
||||||
<p class="help-block">
|
<p class="help-block">
|
||||||
Profiles allow you to create multiple configurations and quickly switch between them.
|
Profiles allow you to create multiple configurations and quickly switch between them or use them in different contexts.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -100,6 +149,27 @@
|
|||||||
<input type="text" id="profile-name" class="form-control">
|
<input type="text" id="profile-name" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Usage conditions</label>
|
||||||
|
|
||||||
|
<p class="help-block">
|
||||||
|
Usage conditions can be assigned such that certain profiles are automatically used in different contexts.
|
||||||
|
For example, when <a href="#popup-content-scanning">Popup Content Scanning</a> is enabled, different profiles can be used
|
||||||
|
depending on the level of the popup.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="help-block">
|
||||||
|
Conditions are organized into groups which represent how the conditions are checked.
|
||||||
|
If all of the conditions in any group are met, then the profile will automatically be used for that context.
|
||||||
|
If no conditions are specified, the profile will only be used if it is selected as the <strong>Active profile</strong>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="condition-groups" id="profile-condition-groups"></div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-default" id="profile-add-condition-group">Add Condition Group</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal fade" tabindex="-1" role="dialog" id="profile-copy-modal">
|
<div class="modal fade" tabindex="-1" role="dialog" id="profile-copy-modal">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@ -136,6 +206,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<template id="condition-template"><div class="input-group condition">
|
||||||
|
<div class="input-group-addon condition-prefix"></div>
|
||||||
|
<div class="input-group-btn"><select class="form-control btn btn-default condition-type"><optgroup label="Type"></optgroup></select></div>
|
||||||
|
<div class="input-group-btn"><select class="form-control btn btn-default condition-operator"><optgroup label="Operator"></optgroup></select></div>
|
||||||
|
<input type="text" class="form-control" />
|
||||||
|
<div class="input-group-btn"><button class="btn btn-danger condition-remove" title="Remove"><span class="glyphicon glyphicon-remove"></span></button></div>
|
||||||
|
</div></template>
|
||||||
|
<template id="condition-group-separator-template"><div class="input-group">
|
||||||
|
<div class="condition-group-separator-label">OR</div>
|
||||||
|
</div></template>
|
||||||
|
<template id="condition-group-options-template"><div class="condition-group-options">
|
||||||
|
<button class="btn btn-default condition-add"><span class="glyphicon glyphicon-plus"></span></button>
|
||||||
|
</div></template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -563,9 +647,12 @@
|
|||||||
|
|
||||||
<script src="/bg/js/anki.js"></script>
|
<script src="/bg/js/anki.js"></script>
|
||||||
<script src="/bg/js/api.js"></script>
|
<script src="/bg/js/api.js"></script>
|
||||||
|
<script src="/bg/js/conditions.js"></script>
|
||||||
|
<script src="/bg/js/conditions-ui.js"></script>
|
||||||
<script src="/bg/js/dictionary.js"></script>
|
<script src="/bg/js/dictionary.js"></script>
|
||||||
<script src="/bg/js/handlebars.js"></script>
|
<script src="/bg/js/handlebars.js"></script>
|
||||||
<script src="/bg/js/options.js"></script>
|
<script src="/bg/js/options.js"></script>
|
||||||
|
<script src="/bg/js/profile-conditions.js"></script>
|
||||||
<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>
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ class DisplayFloat extends Display {
|
|||||||
this.styleNode = null;
|
this.styleNode = null;
|
||||||
|
|
||||||
this.optionsContext = {
|
this.optionsContext = {
|
||||||
depth: 0
|
depth: 0,
|
||||||
|
url: window.location.href
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dependencies = Object.assign({}, this.dependencies, {docRangeFromPoint, docSentenceExtract});
|
this.dependencies = Object.assign({}, this.dependencies, {docRangeFromPoint, docSentenceExtract});
|
||||||
@ -78,9 +79,10 @@ class DisplayFloat extends Display {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
popupNestedInitialize: ({id, depth, parentFrameId}) => {
|
popupNestedInitialize: ({id, depth, parentFrameId, url}) => {
|
||||||
this.optionsContext.depth = depth;
|
this.optionsContext.depth = depth;
|
||||||
popupNestedInitialize(id, depth, parentFrameId);
|
this.optionsContext.url = url;
|
||||||
|
popupNestedInitialize(id, depth, parentFrameId, url);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@ class Frontend {
|
|||||||
this.ignoreNodes = (Array.isArray(ignoreNodes) && ignoreNodes.length > 0 ? ignoreNodes.join(',') : null);
|
this.ignoreNodes = (Array.isArray(ignoreNodes) && ignoreNodes.length > 0 ? ignoreNodes.join(',') : null);
|
||||||
|
|
||||||
this.optionsContext = {
|
this.optionsContext = {
|
||||||
depth: popup.depth
|
depth: popup.depth,
|
||||||
|
url: popup.url
|
||||||
};
|
};
|
||||||
|
|
||||||
this.primaryTouchIdentifier = null;
|
this.primaryTouchIdentifier = null;
|
||||||
@ -42,9 +43,9 @@ class Frontend {
|
|||||||
static create() {
|
static create() {
|
||||||
const initializationData = window.frontendInitializationData;
|
const initializationData = window.frontendInitializationData;
|
||||||
const isNested = (initializationData !== null && typeof initializationData === 'object');
|
const isNested = (initializationData !== null && typeof initializationData === 'object');
|
||||||
const {id, depth, parentFrameId, ignoreNodes} = isNested ? initializationData : {};
|
const {id, depth, parentFrameId, ignoreNodes, url} = isNested ? initializationData : {};
|
||||||
|
|
||||||
const popup = isNested ? new PopupProxy(depth + 1, id, parentFrameId) : PopupProxyHost.instance.createPopup(null);
|
const popup = isNested ? new PopupProxy(depth + 1, id, parentFrameId, url) : PopupProxyHost.instance.createPopup(null);
|
||||||
const frontend = new Frontend(popup, ignoreNodes);
|
const frontend = new Frontend(popup, ignoreNodes);
|
||||||
frontend.prepare();
|
frontend.prepare();
|
||||||
return frontend;
|
return frontend;
|
||||||
@ -52,7 +53,7 @@ class Frontend {
|
|||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
try {
|
try {
|
||||||
this.options = await apiOptionsGet(this.optionsContext);
|
this.options = await apiOptionsGet(this.getOptionsContext());
|
||||||
|
|
||||||
window.addEventListener('message', this.onFrameMessage.bind(this));
|
window.addEventListener('message', this.onFrameMessage.bind(this));
|
||||||
window.addEventListener('mousedown', this.onMouseDown.bind(this));
|
window.addEventListener('mousedown', this.onMouseDown.bind(this));
|
||||||
@ -262,7 +263,7 @@ class Frontend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateOptions() {
|
async updateOptions() {
|
||||||
this.options = await apiOptionsGet(this.optionsContext);
|
this.options = await apiOptionsGet(this.getOptionsContext());
|
||||||
if (!this.options.enable) {
|
if (!this.options.enable) {
|
||||||
this.searchClear();
|
this.searchClear();
|
||||||
}
|
}
|
||||||
@ -335,7 +336,7 @@ class Frontend {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {definitions, length} = await apiTermsFind(searchText, this.optionsContext);
|
const {definitions, length} = await apiTermsFind(searchText, this.getOptionsContext());
|
||||||
if (definitions.length === 0) {
|
if (definitions.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -368,7 +369,7 @@ class Frontend {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const definitions = await apiKanjiFind(searchText, this.optionsContext);
|
const definitions = await apiKanjiFind(searchText, this.getOptionsContext());
|
||||||
if (definitions.length === 0) {
|
if (definitions.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -512,6 +513,11 @@ class Frontend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOptionsContext() {
|
||||||
|
this.optionsContext.url = this.popup.url;
|
||||||
|
return this.optionsContext;
|
||||||
|
}
|
||||||
|
|
||||||
static isScanningModifierPressed(scanningModifier, mouseEvent) {
|
static isScanningModifierPressed(scanningModifier, mouseEvent) {
|
||||||
switch (scanningModifier) {
|
switch (scanningModifier) {
|
||||||
case 'alt': return mouseEvent.altKey;
|
case 'alt': return mouseEvent.altKey;
|
||||||
|
@ -19,13 +19,13 @@
|
|||||||
|
|
||||||
let popupNestedInitialized = false;
|
let popupNestedInitialized = false;
|
||||||
|
|
||||||
async function popupNestedInitialize(id, depth, parentFrameId) {
|
async function popupNestedInitialize(id, depth, parentFrameId, url) {
|
||||||
if (popupNestedInitialized) {
|
if (popupNestedInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
popupNestedInitialized = true;
|
popupNestedInitialized = true;
|
||||||
|
|
||||||
const optionsContext = {depth};
|
const optionsContext = {depth, url};
|
||||||
const options = await apiOptionsGet(optionsContext);
|
const options = await apiOptionsGet(optionsContext);
|
||||||
const popupNestingMaxDepth = options.scanning.popupNestingMaxDepth;
|
const popupNestingMaxDepth = options.scanning.popupNestingMaxDepth;
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ async function popupNestedInitialize(id, depth, parentFrameId) {
|
|||||||
|
|
||||||
const ignoreNodes = options.scanning.enableOnPopupExpressions ? [] : [ '.expression', '.expression *' ];
|
const ignoreNodes = options.scanning.enableOnPopupExpressions ? [] : [ '.expression', '.expression *' ];
|
||||||
|
|
||||||
window.frontendInitializationData = {id, depth, parentFrameId, ignoreNodes};
|
window.frontendInitializationData = {id, depth, parentFrameId, ignoreNodes, url};
|
||||||
|
|
||||||
const scriptSrcs = [
|
const scriptSrcs = [
|
||||||
'/fg/js/frontend-api-sender.js',
|
'/fg/js/frontend-api-sender.js',
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
|
|
||||||
class PopupProxy {
|
class PopupProxy {
|
||||||
constructor(depth, parentId, parentFrameId) {
|
constructor(depth, parentId, parentFrameId, url) {
|
||||||
this.parentId = parentId;
|
this.parentId = parentId;
|
||||||
this.parentFrameId = parentFrameId;
|
this.parentFrameId = parentFrameId;
|
||||||
this.id = null;
|
this.id = null;
|
||||||
@ -26,6 +26,7 @@ class PopupProxy {
|
|||||||
this.parent = null;
|
this.parent = null;
|
||||||
this.child = null;
|
this.child = null;
|
||||||
this.depth = depth;
|
this.depth = depth;
|
||||||
|
this.url = url;
|
||||||
|
|
||||||
this.container = null;
|
this.container = null;
|
||||||
|
|
||||||
|
@ -59,7 +59,8 @@ class Popup {
|
|||||||
this.invokeApi('popupNestedInitialize', {
|
this.invokeApi('popupNestedInitialize', {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
depth: this.depth,
|
depth: this.depth,
|
||||||
parentFrameId
|
parentFrameId,
|
||||||
|
url: this.url
|
||||||
});
|
});
|
||||||
this.invokeApi('setOptions', {
|
this.invokeApi('setOptions', {
|
||||||
general: {
|
general: {
|
||||||
@ -311,4 +312,8 @@ class Popup {
|
|||||||
parent.appendChild(this.container);
|
parent.appendChild(this.container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get url() {
|
||||||
|
return window.location.href;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user