Modifier key profile condition (#487)
* update Frontend options on modifier change * add modifier key profile condition * use select element for modifier condition value * support "is" and "is not" modifier key conditions * use plural * remove dead null check it's never null in that function * pass element on rather than assigning to this * rename event * remove Firefox OS key to Meta detection * hide Meta from dropdown on Firefox * move input type
This commit is contained in:
parent
acfdaa4f48
commit
77b744e675
@ -97,6 +97,7 @@
|
|||||||
"parseUrl": "readonly",
|
"parseUrl": "readonly",
|
||||||
"areSetsEqual": "readonly",
|
"areSetsEqual": "readonly",
|
||||||
"getSetIntersection": "readonly",
|
"getSetIntersection": "readonly",
|
||||||
|
"getSetDifference": "readonly",
|
||||||
"EventDispatcher": "readonly",
|
"EventDispatcher": "readonly",
|
||||||
"EventListenerCollection": "readonly",
|
"EventListenerCollection": "readonly",
|
||||||
"EXTENSION_IS_BROWSER_EDGE": "readonly"
|
"EXTENSION_IS_BROWSER_EDGE": "readonly"
|
||||||
|
@ -36,6 +36,24 @@ function _profileConditionTestDomainList(url, domainList) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _profileModifierKeys = [
|
||||||
|
{optionValue: 'alt', name: 'Alt'},
|
||||||
|
{optionValue: 'ctrl', name: 'Ctrl'},
|
||||||
|
{optionValue: 'shift', name: 'Shift'}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!hasOwn(window, 'netscape')) {
|
||||||
|
_profileModifierKeys.push({optionValue: 'meta', name: 'Meta'});
|
||||||
|
}
|
||||||
|
|
||||||
|
const _profileModifierValueToName = new Map(
|
||||||
|
_profileModifierKeys.map(({optionValue, name}) => [optionValue, name])
|
||||||
|
);
|
||||||
|
|
||||||
|
const _profileModifierNameToValue = new Map(
|
||||||
|
_profileModifierKeys.map(({optionValue, name}) => [name, optionValue])
|
||||||
|
);
|
||||||
|
|
||||||
const profileConditionsDescriptor = {
|
const profileConditionsDescriptor = {
|
||||||
popupLevel: {
|
popupLevel: {
|
||||||
name: 'Popup Level',
|
name: 'Popup Level',
|
||||||
@ -100,5 +118,53 @@ const profileConditionsDescriptor = {
|
|||||||
test: ({url}, transformedOptionValue) => (transformedOptionValue !== null && transformedOptionValue.test(url))
|
test: ({url}, transformedOptionValue) => (transformedOptionValue !== null && transformedOptionValue.test(url))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
modifierKeys: {
|
||||||
|
name: 'Modifier Keys',
|
||||||
|
description: 'Use profile depending on the active modifier keys.',
|
||||||
|
values: _profileModifierKeys,
|
||||||
|
defaultOperator: 'are',
|
||||||
|
operators: {
|
||||||
|
are: {
|
||||||
|
name: 'are',
|
||||||
|
placeholder: 'Press one or more modifier keys here',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'keyMulti',
|
||||||
|
transform: (optionValue) => optionValue
|
||||||
|
.split(' + ')
|
||||||
|
.filter((v) => v.length > 0)
|
||||||
|
.map((v) => _profileModifierNameToValue.get(v)),
|
||||||
|
transformReverse: (transformedOptionValue) => transformedOptionValue
|
||||||
|
.map((v) => _profileModifierValueToName.get(v))
|
||||||
|
.join(' + '),
|
||||||
|
test: ({modifierKeys}, optionValue) => areSetsEqual(new Set(modifierKeys), new Set(optionValue))
|
||||||
|
},
|
||||||
|
areNot: {
|
||||||
|
name: 'are not',
|
||||||
|
placeholder: 'Press one or more modifier keys here',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'keyMulti',
|
||||||
|
transform: (optionValue) => optionValue
|
||||||
|
.split(' + ')
|
||||||
|
.filter((v) => v.length > 0)
|
||||||
|
.map((v) => _profileModifierNameToValue.get(v)),
|
||||||
|
transformReverse: (transformedOptionValue) => transformedOptionValue
|
||||||
|
.map((v) => _profileModifierValueToName.get(v))
|
||||||
|
.join(' + '),
|
||||||
|
test: ({modifierKeys}, optionValue) => !areSetsEqual(new Set(modifierKeys), new Set(optionValue))
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
name: 'include',
|
||||||
|
type: 'select',
|
||||||
|
defaultValue: 'alt',
|
||||||
|
test: ({modifierKeys}, optionValue) => modifierKeys.includes(optionValue)
|
||||||
|
},
|
||||||
|
notInclude: {
|
||||||
|
name: 'don\'t include',
|
||||||
|
type: 'select',
|
||||||
|
defaultValue: 'alt',
|
||||||
|
test: ({modifierKeys}, optionValue) => !modifierKeys.includes(optionValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
/* global
|
/* global
|
||||||
* ClipboardMonitor
|
* ClipboardMonitor
|
||||||
|
* DOM
|
||||||
* Display
|
* Display
|
||||||
* QueryParser
|
* QueryParser
|
||||||
* apiClipboardGet
|
* apiClipboardGet
|
||||||
@ -178,7 +179,7 @@ class DisplaySearch extends Display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
const key = Display.getKeyFromEvent(e);
|
const key = DOM.getKeyFromEvent(e);
|
||||||
const ignoreKeys = this._onKeyDownIgnoreKeys;
|
const ignoreKeys = this._onKeyDownIgnoreKeys;
|
||||||
|
|
||||||
const activeModifierMap = new Map([
|
const activeModifierMap = new Map([
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* global
|
/* global
|
||||||
|
* DOM
|
||||||
* conditionsNormalizeOptionValue
|
* conditionsNormalizeOptionValue
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -177,7 +178,8 @@ ConditionsUI.Condition = class Condition {
|
|||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.container = ConditionsUI.instantiateTemplate('#condition-template').appendTo(parent.container);
|
this.container = ConditionsUI.instantiateTemplate('#condition-template').appendTo(parent.container);
|
||||||
this.input = this.container.find('input');
|
this.input = this.container.find('.condition-input');
|
||||||
|
this.inputInner = null;
|
||||||
this.typeSelect = this.container.find('.condition-type');
|
this.typeSelect = this.container.find('.condition-type');
|
||||||
this.operatorSelect = this.container.find('.condition-operator');
|
this.operatorSelect = this.container.find('.condition-operator');
|
||||||
this.removeButton = this.container.find('.condition-remove');
|
this.removeButton = this.container.find('.condition-remove');
|
||||||
@ -186,14 +188,13 @@ ConditionsUI.Condition = class Condition {
|
|||||||
this.updateOperators();
|
this.updateOperators();
|
||||||
this.updateInput();
|
this.updateInput();
|
||||||
|
|
||||||
this.input.on('change', this.onInputChanged.bind(this));
|
|
||||||
this.typeSelect.on('change', this.onConditionTypeChanged.bind(this));
|
this.typeSelect.on('change', this.onConditionTypeChanged.bind(this));
|
||||||
this.operatorSelect.on('change', this.onConditionOperatorChanged.bind(this));
|
this.operatorSelect.on('change', this.onConditionOperatorChanged.bind(this));
|
||||||
this.removeButton.on('click', this.onRemoveClicked.bind(this));
|
this.removeButton.on('click', this.onRemoveClicked.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
this.input.off('change');
|
this.inputInner.off('change');
|
||||||
this.typeSelect.off('change');
|
this.typeSelect.off('change');
|
||||||
this.operatorSelect.off('change');
|
this.operatorSelect.off('change');
|
||||||
this.removeButton.off('click');
|
this.removeButton.off('click');
|
||||||
@ -236,21 +237,48 @@ ConditionsUI.Condition = class Condition {
|
|||||||
updateInput() {
|
updateInput() {
|
||||||
const conditionDescriptors = this.parent.parent.conditionDescriptors;
|
const conditionDescriptors = this.parent.parent.conditionDescriptors;
|
||||||
const {type, operator} = this.condition;
|
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} = this.validateValue(this.condition.value);
|
||||||
|
this.inputInner.toggleClass('is-invalid', !valid);
|
||||||
|
this.inputInner.val(this.condition.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
createInputElement(objects) {
|
||||||
|
const inputInner = ConditionsUI.instantiateTemplate('#condition-input-text-template');
|
||||||
|
|
||||||
const props = new Map([
|
const props = new Map([
|
||||||
['placeholder', ''],
|
['placeholder', ''],
|
||||||
['type', 'text']
|
['type', 'text']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const objects = [];
|
|
||||||
if (hasOwn(conditionDescriptors, type)) {
|
|
||||||
const conditionDescriptor = conditionDescriptors[type];
|
|
||||||
objects.push(conditionDescriptor);
|
|
||||||
if (hasOwn(conditionDescriptor.operators, operator)) {
|
|
||||||
const operatorDescriptor = conditionDescriptor.operators[operator];
|
|
||||||
objects.push(operatorDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const object of objects) {
|
for (const object of objects) {
|
||||||
if (hasOwn(object, 'placeholder')) {
|
if (hasOwn(object, 'placeholder')) {
|
||||||
props.set('placeholder', object.placeholder);
|
props.set('placeholder', object.placeholder);
|
||||||
@ -266,12 +294,95 @@ ConditionsUI.Condition = class Condition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const [prop, value] of props.entries()) {
|
for (const [prop, value] of props.entries()) {
|
||||||
this.input.prop(prop, value);
|
inputInner.prop(prop, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {valid} = this.validateValue(this.condition.value);
|
return inputInner;
|
||||||
this.input.toggleClass('is-invalid', !valid);
|
}
|
||||||
this.input.val(this.condition.value);
|
|
||||||
|
createInputKeyMultiElement(objects) {
|
||||||
|
const inputInner = this.createInputElement(objects);
|
||||||
|
|
||||||
|
inputInner.prop('readonly', true);
|
||||||
|
|
||||||
|
let values = [];
|
||||||
|
for (const object of objects) {
|
||||||
|
if (hasOwn(object, 'values')) {
|
||||||
|
values = object.values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pressedKeyIndices = new Set();
|
||||||
|
|
||||||
|
const onKeyDown = ({originalEvent}) => {
|
||||||
|
const pressedKeyEventName = DOM.getKeyFromEvent(originalEvent);
|
||||||
|
if (pressedKeyEventName === 'Escape' || pressedKeyEventName === 'Backspace') {
|
||||||
|
pressedKeyIndices.clear();
|
||||||
|
inputInner.val('');
|
||||||
|
inputInner.change();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pressedModifiers = DOM.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(' + ');
|
||||||
|
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', 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(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputInner;
|
||||||
}
|
}
|
||||||
|
|
||||||
validateValue(value) {
|
validateValue(value) {
|
||||||
@ -291,9 +402,9 @@ ConditionsUI.Condition = class Condition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onInputChanged() {
|
onInputChanged() {
|
||||||
const {valid, value} = this.validateValue(this.input.val());
|
const {valid, value} = this.validateValue(this.inputInner.val());
|
||||||
this.input.toggleClass('is-invalid', !valid);
|
this.inputInner.toggleClass('is-invalid', !valid);
|
||||||
this.input.val(value);
|
this.inputInner.val(value);
|
||||||
this.condition.value = value;
|
this.condition.value = value;
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@
|
|||||||
<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-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>
|
<div class="input-group-btn"><select class="form-control btn btn-default condition-operator"><optgroup label="Operator"></optgroup></select></div>
|
||||||
<div class="condition-line-break"></div>
|
<div class="condition-line-break"></div>
|
||||||
<div class="condition-input"><input type="text" class="form-control" /></div>
|
<div class="condition-input"></div>
|
||||||
<div class="input-group-btn"><button class="btn btn-danger condition-remove" title="Remove"><span class="glyphicon glyphicon-remove"></span></button></div>
|
<div class="input-group-btn"><button class="btn btn-danger condition-remove" title="Remove"><span class="glyphicon glyphicon-remove"></span></button></div>
|
||||||
</div></template>
|
</div></template>
|
||||||
<template id="condition-group-separator-template"><div class="input-group">
|
<template id="condition-group-separator-template"><div class="input-group">
|
||||||
@ -126,6 +126,9 @@
|
|||||||
<template id="condition-group-options-template"><div class="condition-group-options">
|
<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>
|
<button class="btn btn-default condition-add"><span class="glyphicon glyphicon-plus"></span></button>
|
||||||
</div></template>
|
</div></template>
|
||||||
|
<template id="condition-input-text-template"><input type="text" class="form-control condition-input-inner" /></template>
|
||||||
|
<template id="condition-input-select-template"><select class="form-control condition-input-inner"></select></template>
|
||||||
|
<template id="condition-input-option-template"><option></option></template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -50,6 +50,9 @@ class Frontend {
|
|||||||
);
|
);
|
||||||
this._textScanner.onSearchSource = this.onSearchSource.bind(this);
|
this._textScanner.onSearchSource = this.onSearchSource.bind(this);
|
||||||
|
|
||||||
|
this._activeModifiers = new Set();
|
||||||
|
this._optionsUpdatePending = false;
|
||||||
|
|
||||||
this._windowMessageHandlers = new Map([
|
this._windowMessageHandlers = new Map([
|
||||||
['popupClose', () => this._textScanner.clearSelection(false)],
|
['popupClose', () => this._textScanner.clearSelection(false)],
|
||||||
['selectionCopy', () => document.execCommand('copy')]
|
['selectionCopy', () => document.execCommand('copy')]
|
||||||
@ -90,6 +93,7 @@ class Frontend {
|
|||||||
chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this));
|
chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this));
|
||||||
|
|
||||||
this._textScanner.on('clearSelection', this.onClearSelection.bind(this));
|
this._textScanner.on('clearSelection', this.onClearSelection.bind(this));
|
||||||
|
this._textScanner.on('activeModifiersChanged', this.onActiveModifiersChanged.bind(this));
|
||||||
|
|
||||||
this._updateContentScale();
|
this._updateContentScale();
|
||||||
this._broadcastRootPopupInformation();
|
this._broadcastRootPopupInformation();
|
||||||
@ -173,12 +177,21 @@ class Frontend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updatePendingOptions() {
|
||||||
|
if (this._optionsUpdatePending) {
|
||||||
|
this._optionsUpdatePending = false;
|
||||||
|
await this.updateOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async setTextSource(textSource) {
|
async setTextSource(textSource) {
|
||||||
await this.onSearchSource(textSource, 'script');
|
await this.onSearchSource(textSource, 'script');
|
||||||
this._textScanner.setCurrentTextSource(textSource);
|
this._textScanner.setCurrentTextSource(textSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onSearchSource(textSource, cause) {
|
async onSearchSource(textSource, cause) {
|
||||||
|
await this.updatePendingOptions();
|
||||||
|
|
||||||
let results = null;
|
let results = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -254,12 +267,24 @@ class Frontend {
|
|||||||
onClearSelection({passive}) {
|
onClearSelection({passive}) {
|
||||||
this.popup.hide(!passive);
|
this.popup.hide(!passive);
|
||||||
this.popup.clearAutoPlayTimer();
|
this.popup.clearAutoPlayTimer();
|
||||||
|
this.updatePendingOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onActiveModifiersChanged({modifiers}) {
|
||||||
|
if (areSetsEqual(modifiers, this._activeModifiers)) { return; }
|
||||||
|
this._activeModifiers = modifiers;
|
||||||
|
if (await this.popup.isVisible()) {
|
||||||
|
this._optionsUpdatePending = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.updateOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOptionsContext() {
|
async getOptionsContext() {
|
||||||
const url = this._getUrl !== null ? await this._getUrl() : window.location.href;
|
const url = this._getUrl !== null ? await this._getUrl() : window.location.href;
|
||||||
const depth = this.popup.depth;
|
const depth = this.popup.depth;
|
||||||
return {depth, url};
|
const modifierKeys = [...this._activeModifiers];
|
||||||
|
return {depth, url, modifierKeys};
|
||||||
}
|
}
|
||||||
|
|
||||||
_showPopupContent(textSource, optionsContext, type=null, details=null) {
|
_showPopupContent(textSource, optionsContext, type=null, details=null) {
|
||||||
|
@ -146,6 +146,12 @@ function getSetIntersection(set1, set2) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSetDifference(set1, set2) {
|
||||||
|
return new Set(
|
||||||
|
[...set1].filter((value) => !set2.has(value))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Async utilities
|
* Async utilities
|
||||||
|
@ -338,7 +338,7 @@ class Display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
const key = Display.getKeyFromEvent(e);
|
const key = DOM.getKeyFromEvent(e);
|
||||||
const handler = this._onKeyDownHandlers.get(key);
|
const handler = this._onKeyDownHandlers.get(key);
|
||||||
if (typeof handler === 'function') {
|
if (typeof handler === 'function') {
|
||||||
if (handler(e)) {
|
if (handler(e)) {
|
||||||
@ -964,11 +964,6 @@ class Display {
|
|||||||
return elementRect.top - documentRect.top;
|
return elementRect.top - documentRect.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getKeyFromEvent(event) {
|
|
||||||
const key = event.key;
|
|
||||||
return (typeof key === 'string' ? (key.length === 1 ? key.toUpperCase() : key) : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
async _getNoteContext() {
|
async _getNoteContext() {
|
||||||
const documentTitle = await this.getDocumentTitle();
|
const documentTitle = await this.getDocumentTitle();
|
||||||
return {
|
return {
|
||||||
|
@ -63,6 +63,20 @@ class DOM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getActiveModifiers(event) {
|
||||||
|
const modifiers = new Set();
|
||||||
|
if (event.altKey) { modifiers.add('alt'); }
|
||||||
|
if (event.ctrlKey) { modifiers.add('ctrl'); }
|
||||||
|
if (event.metaKey) { modifiers.add('meta'); }
|
||||||
|
if (event.shiftKey) { modifiers.add('shift'); }
|
||||||
|
return modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getKeyFromEvent(event) {
|
||||||
|
const key = event.key;
|
||||||
|
return (typeof key === 'string' ? (key.length === 1 ? key.toUpperCase() : key) : '');
|
||||||
|
}
|
||||||
|
|
||||||
static getFullscreenElement() {
|
static getFullscreenElement() {
|
||||||
return (
|
return (
|
||||||
document.fullscreenElement ||
|
document.fullscreenElement ||
|
||||||
|
@ -70,6 +70,9 @@ class TextScanner extends EventDispatcher {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const modifiers = DOM.getActiveModifiers(e);
|
||||||
|
this.trigger('activeModifiersChanged', {modifiers});
|
||||||
|
|
||||||
const scanningOptions = this.options.scanning;
|
const scanningOptions = this.options.scanning;
|
||||||
const scanningModifier = scanningOptions.modifier;
|
const scanningModifier = scanningOptions.modifier;
|
||||||
if (!(
|
if (!(
|
||||||
|
Loading…
Reference in New Issue
Block a user