diff options
Diffstat (limited to 'ext/bg/js/settings/conditions-ui.js')
-rw-r--r-- | ext/bg/js/settings/conditions-ui.js | 449 |
1 files changed, 0 insertions, 449 deletions
diff --git a/ext/bg/js/settings/conditions-ui.js b/ext/bg/js/settings/conditions-ui.js deleted file mode 100644 index 98b3d432..00000000 --- a/ext/bg/js/settings/conditions-ui.js +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Copyright (C) 2019-2020 Yomichan Authors - * - * 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 <https://www.gnu.org/licenses/>. - */ - -/* global - * DocumentUtil - * conditionsNormalizeOptionValue - */ - -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 toIterable(conditionGroups)) { - this.children.push(new ConditionsUI.ConditionGroup(this, conditionGroup)); - } - - this.addButton.on('click', this.onAddConditionGroup.bind(this)); - } - - 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 (hasOwn(this.conditionDescriptors, 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 (hasOwn(this.conditionDescriptors, type)) { - const conditionDescriptor = this.conditionDescriptors[type]; - if (hasOwn(conditionDescriptor.operators, operator)) { - const operatorDescriptor = conditionDescriptor.operators[operator]; - if (hasOwn(operatorDescriptor, 'defaultValue')) { - return {value: this.isolate(operatorDescriptor.defaultValue), fromOperator: true}; - } - } - if (hasOwn(conditionDescriptor, 'defaultValue')) { - return {value: this.isolate(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 toIterable(conditionGroup.conditions)) { - this.children.push(new ConditionsUI.Condition(this, condition)); - } - - this.addButton.on('click', this.onAddCondition.bind(this)); - } - - 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('.condition-input'); - this.inputInner = null; - 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.typeSelect.on('change', this.onConditionTypeChanged.bind(this)); - this.operatorSelect.on('change', this.onConditionOperatorChanged.bind(this)); - this.removeButton.on('click', this.onRemoveClicked.bind(this)); - } - - cleanup() { - this.inputInner.off('change'); - this.typeSelect.off('change'); - this.operatorSelect.off('change'); - this.removeButton.off('click'); - this.container.remove(); - } - - save() { - this.parent.save(); - } - - isolate(object) { - return this.parent.isolate(object); - } - - 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 (hasOwn(conditionDescriptors, 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 objects = []; - let inputType = null; - if (hasOwn(conditionDescriptors, type)) { - const conditionDescriptor = conditionDescriptors[type]; - objects.push(conditionDescriptor); - if (hasOwn(conditionDescriptor, 'type')) { - inputType = conditionDescriptor.type; - } - if (hasOwn(conditionDescriptor.operators, operator)) { - const operatorDescriptor = conditionDescriptor.operators[operator]; - objects.push(operatorDescriptor); - if (hasOwn(operatorDescriptor, 'type')) { - inputType = operatorDescriptor.type; - } - } - } - - this.input.empty(); - if (inputType === 'select') { - this.inputInner = this.createSelectElement(objects); - } else if (inputType === 'keyMulti') { - this.inputInner = this.createInputKeyMultiElement(objects); - } else { - this.inputInner = this.createInputElement(objects); - } - this.inputInner.appendTo(this.input); - this.inputInner.on('change', this.onInputChanged.bind(this)); - - const {valid, value} = this.validateValue(this.condition.value); - this.inputInner.toggleClass('is-invalid', !valid); - this.inputInner.val(value); - } - - createInputElement(objects) { - const inputInner = ConditionsUI.instantiateTemplate('#condition-input-text-template'); - - const props = new Map([ - ['placeholder', ''], - ['type', 'text'] - ]); - - for (const object of objects) { - if (hasOwn(object, 'placeholder')) { - props.set('placeholder', object.placeholder); - } - if (object.type === 'number') { - props.set('type', 'number'); - for (const prop of ['step', 'min', 'max']) { - if (hasOwn(object, prop)) { - props.set(prop, object[prop]); - } - } - } - } - - for (const [prop, value] of props.entries()) { - inputInner.prop(prop, value); - } - - return inputInner; - } - - createInputKeyMultiElement(objects) { - const inputInner = this.createInputElement(objects); - - inputInner.prop('readonly', true); - - let values = []; - let keySeparator = ' + '; - for (const object of objects) { - if (hasOwn(object, 'values')) { - values = object.values; - } - if (hasOwn(object, 'keySeparator')) { - keySeparator = object.keySeparator; - } - } - - const pressedKeyIndices = new Set(); - - const onKeyDown = ({originalEvent}) => { - const pressedKeyEventName = DocumentUtil.getKeyFromEvent(originalEvent); - if (pressedKeyEventName === 'Escape' || pressedKeyEventName === 'Backspace') { - pressedKeyIndices.clear(); - inputInner.val(''); - inputInner.change(); - return; - } - - const pressedModifiers = DocumentUtil.getActiveModifiers(originalEvent); - // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey - // https://askubuntu.com/questions/567731/why-is-shift-alt-being-mapped-to-meta - // It works with mouse events on some platforms, so try to determine if metaKey is pressed - // hack; only works when Shift and Alt are not pressed - const isMetaKeyChrome = ( - pressedKeyEventName === 'Meta' && - getSetDifference(new Set(['shift', 'alt']), pressedModifiers).size !== 0 - ); - if (isMetaKeyChrome) { - pressedModifiers.add('meta'); - } - - for (const modifier of pressedModifiers) { - const foundIndex = values.findIndex(({optionValue}) => optionValue === modifier); - if (foundIndex !== -1) { - pressedKeyIndices.add(foundIndex); - } - } - - const inputValue = [...pressedKeyIndices].map((i) => values[i].name).join(keySeparator); - inputInner.val(inputValue); - inputInner.change(); - }; - - inputInner.on('keydown', onKeyDown); - - return inputInner; - } - - createSelectElement(objects) { - const inputInner = ConditionsUI.instantiateTemplate('#condition-input-select-template'); - - const data = new Map([ - ['values', []], - ['defaultValue', null] - ]); - - for (const object of objects) { - if (hasOwn(object, 'values')) { - data.set('values', object.values); - } - if (hasOwn(object, 'defaultValue')) { - data.set('defaultValue', this.isolate(object.defaultValue)); - } - } - - for (const {optionValue, name} of data.get('values')) { - const option = ConditionsUI.instantiateTemplate('#condition-input-option-template'); - option.attr('value', optionValue); - option.text(name); - option.appendTo(inputInner); - } - - const defaultValue = data.get('defaultValue'); - if (defaultValue !== null) { - inputInner.val(this.isolate(defaultValue)); - } - - return inputInner; - } - - validateValue(value, isInput=false) { - const conditionDescriptors = this.parent.parent.conditionDescriptors; - let valid = true; - let inputTransformedValue = null; - try { - [value, inputTransformedValue] = conditionsNormalizeOptionValue( - conditionDescriptors, - this.condition.type, - this.condition.operator, - value, - isInput - ); - } catch (e) { - valid = false; - } - return {valid, value, inputTransformedValue}; - } - - onInputChanged() { - const {valid, value, inputTransformedValue} = this.validateValue(this.inputInner.val(), true); - this.inputInner.toggleClass('is-invalid', !valid); - this.inputInner.val(value); - this.condition.value = inputTransformedValue !== null ? inputTransformedValue : 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(); - } -}; |