summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2021-05-01 15:54:31 -0400
committerGitHub <noreply@github.com>2021-05-01 15:54:31 -0400
commitc514bbc4fbe39f611d7d9cfd3a48681bacbaf559 (patch)
tree275c792977b0c925fca67a324b036e1a81ba25dc
parent8bf6ff92f9e318554139d3f21f1dcdb98ce59036 (diff)
Flags profile conditions (#1647)
* Generalize modifier keys * Optimize bindings * Add support for flags * Add clipboard flag * Update tests * Add tests
-rw-r--r--ext/js/background/profile-conditions-util.js67
-rw-r--r--ext/js/display/search-display-controller.js14
-rw-r--r--ext/js/pages/settings/profile-conditions-ui.js46
-rw-r--r--test/test-profile-conditions-util.js270
4 files changed, 366 insertions, 31 deletions
diff --git a/ext/js/background/profile-conditions-util.js b/ext/js/background/profile-conditions-util.js
index 928026e0..dcd60796 100644
--- a/ext/js/background/profile-conditions-util.js
+++ b/ext/js/background/profile-conditions-util.js
@@ -57,6 +57,17 @@ class ProfileConditionsUtil {
['notInclude', this._createSchemaModifierKeysNotInclude.bind(this)]
])
}
+ ],
+ [
+ 'flags',
+ {
+ operators: new Map([
+ ['are', this._createSchemaFlagsAre.bind(this)],
+ ['areNot', this._createSchemaFlagsAreNot.bind(this)],
+ ['include', this._createSchemaFlagsInclude.bind(this)],
+ ['notInclude', this._createSchemaFlagsNotInclude.bind(this)]
+ ])
+ }
]
]);
}
@@ -121,6 +132,10 @@ class ProfileConditionsUtil {
// NOP
}
}
+ const {flags} = normalizedContext;
+ if (!Array.isArray(flags)) {
+ normalizedContext.flags = [];
+ }
return normalizedContext;
}
@@ -222,54 +237,76 @@ class ProfileConditionsUtil {
// modifierKeys schema creation functions
_createSchemaModifierKeysAre(value) {
- return this._createSchemaModifierKeysGeneric(value, true, false);
+ return this._createSchemaArrayCheck('modifierKeys', value, true, false);
}
_createSchemaModifierKeysAreNot(value) {
return {
- not: [this._createSchemaModifierKeysGeneric(value, true, false)]
+ not: [this._createSchemaArrayCheck('modifierKeys', value, true, false)]
};
}
_createSchemaModifierKeysInclude(value) {
- return this._createSchemaModifierKeysGeneric(value, false, false);
+ return this._createSchemaArrayCheck('modifierKeys', value, false, false);
}
_createSchemaModifierKeysNotInclude(value) {
- return this._createSchemaModifierKeysGeneric(value, false, true);
+ return this._createSchemaArrayCheck('modifierKeys', value, false, true);
+ }
+
+ // modifierKeys schema creation functions
+
+ _createSchemaFlagsAre(value) {
+ return this._createSchemaArrayCheck('flags', value, true, false);
+ }
+
+ _createSchemaFlagsAreNot(value) {
+ return {
+ not: [this._createSchemaArrayCheck('flags', value, true, false)]
+ };
}
- _createSchemaModifierKeysGeneric(value, exact, none) {
+ _createSchemaFlagsInclude(value) {
+ return this._createSchemaArrayCheck('flags', value, false, false);
+ }
+
+ _createSchemaFlagsNotInclude(value) {
+ return this._createSchemaArrayCheck('flags', value, false, true);
+ }
+
+ // Generic
+
+ _createSchemaArrayCheck(key, value, exact, none) {
const containsList = [];
- for (const modifierKey of this._split(value)) {
- if (modifierKey.length === 0) { continue; }
+ for (const item of this._split(value)) {
+ if (item.length === 0) { continue; }
containsList.push({
contains: {
- const: modifierKey
+ const: item
}
});
}
const containsListCount = containsList.length;
- const modifierKeysSchema = {
+ const schema = {
type: 'array'
};
if (exact) {
- modifierKeysSchema.maxItems = containsListCount;
+ schema.maxItems = containsListCount;
}
if (none) {
if (containsListCount > 0) {
- modifierKeysSchema.not = containsList;
+ schema.not = containsList;
}
} else {
- modifierKeysSchema.minItems = containsListCount;
+ schema.minItems = containsListCount;
if (containsListCount > 0) {
- modifierKeysSchema.allOf = containsList;
+ schema.allOf = containsList;
}
}
return {
- required: ['modifierKeys'],
+ required: [key],
properties: {
- modifierKeys: modifierKeysSchema
+ [key]: schema
}
};
}
diff --git a/ext/js/display/search-display-controller.js b/ext/js/display/search-display-controller.js
index f672909e..e60de796 100644
--- a/ext/js/display/search-display-controller.js
+++ b/ext/js/display/search-display-controller.js
@@ -179,12 +179,12 @@ class SearchDisplayController {
e.preventDefault();
e.stopImmediatePropagation();
this._display.blurElement(e.currentTarget);
- this._search(true, true, true);
+ this._search(true, true, true, null);
}
_onSearch(e) {
e.preventDefault();
- this._search(true, true, true);
+ this._search(true, true, true, null);
}
_onCopy() {
@@ -199,7 +199,7 @@ class SearchDisplayController {
}
this._queryInput.value = text;
this._updateSearchHeight(true);
- this._search(animate, false, autoSearchContent);
+ this._search(animate, false, autoSearchContent, ['clipboard']);
}
_onWanakanaEnableChange(e) {
@@ -342,11 +342,15 @@ class SearchDisplayController {
});
}
- _search(animate, history, lookup) {
+ _search(animate, history, lookup, flags) {
const query = this._queryInput.value;
const depth = this._display.depth;
const url = window.location.href;
const documentTitle = document.title;
+ const optionsContext = {depth, url};
+ if (flags !== null) {
+ optionsContext.flags = flags;
+ }
const details = {
focus: false,
history,
@@ -355,7 +359,7 @@ class SearchDisplayController {
},
state: {
focusEntry: 0,
- optionsContext: {depth, url},
+ optionsContext,
url,
sentence: {text: query, offset: 0},
documentTitle
diff --git a/ext/js/pages/settings/profile-conditions-ui.js b/ext/js/pages/settings/profile-conditions-ui.js
index 5fda1dc0..0a598693 100644
--- a/ext/js/pages/settings/profile-conditions-ui.js
+++ b/ext/js/pages/settings/profile-conditions-ui.js
@@ -30,6 +30,10 @@ class ProfileConditionsUI extends EventDispatcher {
this._eventListeners = new EventListenerCollection();
this._defaultType = 'popupLevel';
this._profileIndex = 0;
+ const validateInteger = this._validateInteger.bind(this);
+ const normalizeInteger = this._normalizeInteger.bind(this);
+ const validateFlags = this._validateFlags.bind(this);
+ const normalizeFlags = this._normalizeFlags.bind(this);
this._descriptors = new Map([
[
'popupLevel',
@@ -37,12 +41,12 @@ class ProfileConditionsUI extends EventDispatcher {
displayName: 'Popup Level',
defaultOperator: 'equal',
operators: new Map([
- ['equal', {displayName: '=', type: 'integer', defaultValue: '0', validate: this._validateInteger.bind(this), normalize: this._normalizeInteger.bind(this)}],
- ['notEqual', {displayName: '\u2260', type: 'integer', defaultValue: '0', validate: this._validateInteger.bind(this), normalize: this._normalizeInteger.bind(this)}],
- ['lessThan', {displayName: '<', type: 'integer', defaultValue: '0', validate: this._validateInteger.bind(this), normalize: this._normalizeInteger.bind(this)}],
- ['greaterThan', {displayName: '>', type: 'integer', defaultValue: '0', validate: this._validateInteger.bind(this), normalize: this._normalizeInteger.bind(this)}],
- ['lessThanOrEqual', {displayName: '\u2264', type: 'integer', defaultValue: '0', validate: this._validateInteger.bind(this), normalize: this._normalizeInteger.bind(this)}],
- ['greaterThanOrEqual', {displayName: '\u2265', type: 'integer', defaultValue: '0', validate: this._validateInteger.bind(this), normalize: this._normalizeInteger.bind(this)}]
+ ['equal', {displayName: '=', type: 'integer', defaultValue: '0', validate: validateInteger, normalize: normalizeInteger}],
+ ['notEqual', {displayName: '\u2260', type: 'integer', defaultValue: '0', validate: validateInteger, normalize: normalizeInteger}],
+ ['lessThan', {displayName: '<', type: 'integer', defaultValue: '0', validate: validateInteger, normalize: normalizeInteger}],
+ ['greaterThan', {displayName: '>', type: 'integer', defaultValue: '0', validate: validateInteger, normalize: normalizeInteger}],
+ ['lessThanOrEqual', {displayName: '\u2264', type: 'integer', defaultValue: '0', validate: validateInteger, normalize: normalizeInteger}],
+ ['greaterThanOrEqual', {displayName: '\u2265', type: 'integer', defaultValue: '0', validate: validateInteger, normalize: normalizeInteger}]
])
}
],
@@ -69,8 +73,24 @@ class ProfileConditionsUI extends EventDispatcher {
['notInclude', {displayName: 'Don\'t Include', type: 'modifierKeys', defaultValue: ''}]
])
}
+ ],
+ [
+ 'flags',
+ {
+ displayName: 'Flags',
+ defaultOperator: 'are',
+ operators: new Map([
+ ['are', {displayName: 'Are', type: 'string', defaultValue: '', validate: validateFlags, normalize: normalizeFlags}],
+ ['areNot', {displayName: 'Are Not', type: 'string', defaultValue: '', validate: validateFlags, normalize: normalizeFlags}],
+ ['include', {displayName: 'Include', type: 'string', defaultValue: '', validate: validateFlags, normalize: normalizeFlags}],
+ ['notInclude', {displayName: 'Don\'t Include', type: 'string', defaultValue: '', validate: validateFlags, normalize: normalizeFlags}]
+ ])
+ }
]
]);
+ this._validFlags = new Set([
+ 'clipboard'
+ ]);
}
get settingsController() {
@@ -280,6 +300,20 @@ class ProfileConditionsUI extends EventDispatcher {
return this.splitValue(value).join(', ');
}
+ _validateFlags(value) {
+ const flags = this.splitValue(value);
+ for (const flag of flags) {
+ if (!this._validFlags.has(flag)) {
+ return false;
+ }
+ }
+ return flags.length > 0;
+ }
+
+ _normalizeFlags(value) {
+ return [...new Set(this.splitValue(value))].join(', ');
+ }
+
_triggerConditionGroupCountChanged(count) {
this.trigger('conditionGroupCountChanged', {count, profileIndex: this._profileIndex});
}
diff --git a/test/test-profile-conditions-util.js b/test/test-profile-conditions-util.js
index ebc72be7..b77578de 100644
--- a/test/test-profile-conditions-util.js
+++ b/test/test-profile-conditions-util.js
@@ -40,25 +40,25 @@ function testNormalizeContext() {
// Empty
{
context: {},
- expected: {}
+ expected: {flags: []}
},
// Domain normalization
{
context: {url: ''},
- expected: {url: ''}
+ expected: {url: '', flags: []}
},
{
context: {url: 'http://example.com/'},
- expected: {url: 'http://example.com/', domain: 'example.com'}
+ expected: {url: 'http://example.com/', domain: 'example.com', flags: []}
},
{
context: {url: 'http://example.com:1234/'},
- expected: {url: 'http://example.com:1234/', domain: 'example.com'}
+ expected: {url: 'http://example.com:1234/', domain: 'example.com', flags: []}
},
{
context: {url: 'http://user@example.com:1234/'},
- expected: {url: 'http://user@example.com:1234/', domain: 'example.com'}
+ expected: {url: 'http://user@example.com:1234/', domain: 'example.com', flags: []}
}
];
@@ -611,6 +611,266 @@ function testSchemas() {
]
},
+ // flags tests
+ {
+ conditionGroups: [
+ {
+ conditions: [
+ {
+ type: 'flags',
+ operator: 'are',
+ value: ''
+ }
+ ]
+ }
+ ],
+ expectedSchema: {
+ required: ['flags'],
+ properties: {
+ flags: {
+ type: 'array',
+ maxItems: 0,
+ minItems: 0
+ }
+ }
+ },
+ inputs: [
+ {expected: true, context: {}},
+ {expected: true, context: {flags: []}},
+ {expected: false, context: {flags: ['test1']}},
+ {expected: false, context: {flags: ['test1', 'test2']}},
+ {expected: false, context: {flags: ['test1', 'test2', 'test3']}}
+ ]
+ },
+ {
+ conditionGroups: [
+ {
+ conditions: [
+ {
+ type: 'flags',
+ operator: 'are',
+ value: 'test1, test2'
+ }
+ ]
+ }
+ ],
+ expectedSchema: {
+ required: ['flags'],
+ properties: {
+ flags: {
+ type: 'array',
+ maxItems: 2,
+ minItems: 2,
+ allOf: [
+ {contains: {const: 'test1'}},
+ {contains: {const: 'test2'}}
+ ]
+ }
+ }
+ },
+ inputs: [
+ {expected: false, context: {}},
+ {expected: false, context: {flags: []}},
+ {expected: false, context: {flags: ['test1']}},
+ {expected: true, context: {flags: ['test1', 'test2']}},
+ {expected: false, context: {flags: ['test1', 'test2', 'test3']}}
+ ]
+ },
+ {
+ conditionGroups: [
+ {
+ conditions: [
+ {
+ type: 'flags',
+ operator: 'areNot',
+ value: ''
+ }
+ ]
+ }
+ ],
+ expectedSchema: {
+ not: [
+ {
+ required: ['flags'],
+ properties: {
+ flags: {
+ type: 'array',
+ maxItems: 0,
+ minItems: 0
+ }
+ }
+ }
+ ]
+ },
+ inputs: [
+ {expected: false, context: {}},
+ {expected: false, context: {flags: []}},
+ {expected: true, context: {flags: ['test1']}},
+ {expected: true, context: {flags: ['test1', 'test2']}},
+ {expected: true, context: {flags: ['test1', 'test2', 'test3']}}
+ ]
+ },
+ {
+ conditionGroups: [
+ {
+ conditions: [
+ {
+ type: 'flags',
+ operator: 'areNot',
+ value: 'test1, test2'
+ }
+ ]
+ }
+ ],
+ expectedSchema: {
+ not: [
+ {
+ required: ['flags'],
+ properties: {
+ flags: {
+ type: 'array',
+ maxItems: 2,
+ minItems: 2,
+ allOf: [
+ {contains: {const: 'test1'}},
+ {contains: {const: 'test2'}}
+ ]
+ }
+ }
+ }
+ ]
+ },
+ inputs: [
+ {expected: true, context: {}},
+ {expected: true, context: {flags: []}},
+ {expected: true, context: {flags: ['test1']}},
+ {expected: false, context: {flags: ['test1', 'test2']}},
+ {expected: true, context: {flags: ['test1', 'test2', 'test3']}}
+ ]
+ },
+ {
+ conditionGroups: [
+ {
+ conditions: [
+ {
+ type: 'flags',
+ operator: 'include',
+ value: ''
+ }
+ ]
+ }
+ ],
+ expectedSchema: {
+ required: ['flags'],
+ properties: {
+ flags: {
+ type: 'array',
+ minItems: 0
+ }
+ }
+ },
+ inputs: [
+ {expected: true, context: {}},
+ {expected: true, context: {flags: []}},
+ {expected: true, context: {flags: ['test1']}},
+ {expected: true, context: {flags: ['test1', 'test2']}},
+ {expected: true, context: {flags: ['test1', 'test2', 'test3']}}
+ ]
+ },
+ {
+ conditionGroups: [
+ {
+ conditions: [
+ {
+ type: 'flags',
+ operator: 'include',
+ value: 'test1, test2'
+ }
+ ]
+ }
+ ],
+ expectedSchema: {
+ required: ['flags'],
+ properties: {
+ flags: {
+ type: 'array',
+ minItems: 2,
+ allOf: [
+ {contains: {const: 'test1'}},
+ {contains: {const: 'test2'}}
+ ]
+ }
+ }
+ },
+ inputs: [
+ {expected: false, context: {}},
+ {expected: false, context: {flags: []}},
+ {expected: false, context: {flags: ['test1']}},
+ {expected: true, context: {flags: ['test1', 'test2']}},
+ {expected: true, context: {flags: ['test1', 'test2', 'test3']}}
+ ]
+ },
+ {
+ conditionGroups: [
+ {
+ conditions: [
+ {
+ type: 'flags',
+ operator: 'notInclude',
+ value: ''
+ }
+ ]
+ }
+ ],
+ expectedSchema: {
+ required: ['flags'],
+ properties: {
+ flags: {
+ type: 'array'
+ }
+ }
+ },
+ inputs: [
+ {expected: true, context: {}},
+ {expected: true, context: {flags: []}},
+ {expected: true, context: {flags: ['test1']}},
+ {expected: true, context: {flags: ['test1', 'test2']}},
+ {expected: true, context: {flags: ['test1', 'test2', 'test3']}}
+ ]
+ },
+ {
+ conditionGroups: [
+ {
+ conditions: [
+ {
+ type: 'flags',
+ operator: 'notInclude',
+ value: 'test1, test2'
+ }
+ ]
+ }
+ ],
+ expectedSchema: {
+ required: ['flags'],
+ properties: {
+ flags: {
+ type: 'array',
+ not: [
+ {contains: {const: 'test1'}},
+ {contains: {const: 'test2'}}
+ ]
+ }
+ }
+ },
+ inputs: [
+ {expected: true, context: {}},
+ {expected: true, context: {flags: []}},
+ {expected: false, context: {flags: ['test1']}},
+ {expected: false, context: {flags: ['test1', 'test2']}},
+ {expected: false, context: {flags: ['test1', 'test2', 'test3']}}
+ ]
+ },
+
// Multiple conditions tests
{
conditionGroups: [