Commit fba4fdd2 authored by Andrea Giammarchi's avatar Andrea Giammarchi

Issue 7231 - Cumulative fixes for io-filter-search

Cumulative patches for #308 #302 #293 #291 #289 #283 #280
parent 2f47e087
......@@ -48,17 +48,11 @@ io-filter-table > io-filter-list > table
io-filter-table .footer
{
display: flex;
visibility: hidden;
flex-direction: row;
margin-top: 16px;
align-items: center;
}
io-filter-table .footer.visible
{
visibility: visible;
}
io-filter-table .footer button
{
display: inline-flex;
......@@ -68,6 +62,11 @@ io-filter-table .footer button
align-content: center;
}
io-filter-table .footer button[disabled]
{
visibility: hidden;
}
io-filter-table .footer button:not(:last-child)
{
text-transform: uppercase;
......
......@@ -684,14 +684,14 @@ function setupPort()
{
if (message.type === "filters.respond" && message.action === "disabled")
{
const [beFilter, value] = message.args;
const filter = this.filters.find(f => f.text === beFilter.text);
if (value !== filter.disabled)
const {text, disabled} = message.args[0];
const filter = this.filters.find(f => f.text === text);
if (disabled !== filter.disabled)
{
dispatchError.call(this, "filter.disabled", filter);
filter.disabled = value;
this.render();
filter.disabled = disabled;
}
this.render();
}
});
}
......
......@@ -108,7 +108,7 @@ class IOFilterSearch extends IOElement
onclick()
{
if (this.value)
dispatch.call(this, "filter:add", this.value);
addFilter.call(this, this.value);
}
ondrop(event)
......@@ -139,10 +139,16 @@ class IOFilterSearch extends IOElement
onkeyup()
{
const {match, value} = this;
// clear on backspace
if (!value.length)
{
this.value = "";
return;
}
// no match means don't validate
// but also multi line (paste on old browsers)
// shouldn't pass through this logic (filtered later on)
if (!match || !value || value.includes("\n"))
if (!match || value.includes("\n"))
return;
clearTimeout(this._timer);
// debounce the search to avoid degrading
......@@ -159,7 +165,10 @@ class IOFilterSearch extends IOElement
onpaste(event)
{
const clipboardData = event.clipboardData || window.clipboardData;
addFilter.call(this, clipboardData.getData("text"));
const data = clipboardData.getData("text").trim();
// do not automatically paste on single line
if (isMultiLine(data))
addFilter.call(this, data);
}
render()
......@@ -186,14 +195,34 @@ module.exports = IOFilterSearch;
function addFilter(data)
{
const value = data.trim();
let value = data.trim();
if (!value)
return;
const result = search.call(this, value);
if (result.accuracy < 1)
if (value[0] === "[")
{
const message = browser.i18n.getMessage("unexpected_filter_list_header");
dispatch.call(this, "filter:error", {
errors: [message]
});
value = value.replace(/^\S+/, "").trim();
if (!value)
return;
}
// in case of multi line don't bother the search
if (isMultiLine(value))
{
dispatch.call(this, "filter:add", value);
else if (result.accuracy)
dispatch.call(this, "filter:match", result);
}
else
{
const result = search.call(this, value);
if (result.accuracy < 1)
dispatch.call(this, "filter:add", value);
else if (result.accuracy)
dispatch.call(this, "filter:match", result);
}
}
function dispatch(type, detail)
......@@ -206,6 +235,11 @@ function hasValue(filter)
return filter.text == this;
}
function isMultiLine(data)
{
return /[\r\n]/.test(data.trim());
}
function search(value)
{
let accuracy = 0;
......
......@@ -56,6 +56,10 @@ class IOFilterTable extends IOElement
"filter:match",
event => this.onFilterMatch(event)
);
this.search.addEventListener(
"filter:error",
event => this.onerror(event)
);
this.list = this.appendChild(new IOFilterList());
this.footer = this.appendChild(wire()`<div class="footer" />`);
this.addEventListener("click", this);
......@@ -94,12 +98,15 @@ class IOFilterTable extends IOElement
{
if (event.target.closest("io-checkbox"))
{
this.updateFooter();
cleanErrors.call(this);
}
}
onerror(event)
{
// force the footer to be visible since errors are shown there
this.updateFooter();
this.footer.classList.add("visible");
const {filter, errors} = event.detail;
const node = this.querySelector(".footer .error");
if (filter)
......@@ -142,7 +149,7 @@ class IOFilterTable extends IOElement
() => updateList(this.list),
(errors) => this.onerror({detail: {errors}})
);
this.updateFooter();
cleanErrors.call(this);
break;
case classList.contains("copy"):
const filters = [];
......@@ -157,41 +164,24 @@ class IOFilterTable extends IOElement
onFilterAdd(event)
{
const unknown = new WeakSet();
const filtersBefore = this.filters;
const filters = event.detail
.split(/(?:\r\n|\n)/)
.map(text =>
{
let value = filtersBefore.find(
filter => filter.text === text
);
if (!value)
{
value = {text};
unknown.add(value);
}
return value;
});
.reverse();
browser.runtime.sendMessage({
type: "filters.importRaw",
text: filters.map(filter => filter.text).join("\n")
text: filters.join("\n")
})
.then(errors =>
{
if (!errors.length)
{
// this is false only when the extension sets filters right away,
// as example the first time custom filters are created.
// in that case, the order should be the one the extension decided.
if (filtersBefore === this.filters)
cleanErrors.call(this);
for (const text of filters)
{
for (const filter of filters)
{
if (!unknown.has(filter))
this.filters.splice(this.filters.indexOf(filter), 1);
this.filters.unshift(filter);
}
const i = this.filters.findIndex(flt => flt.text === text);
const [filter] = i < 0 ? [{text}] : this.filters.splice(i, 1);
this.filters.unshift(filter);
}
// needed in case there were no filters whatsoever
// and the table never got a chance to initialize
......@@ -232,20 +222,23 @@ class IOFilterTable extends IOElement
if (this.list.filters !== filters)
this.list.filters = filters;
this.renderFooter();
this.updateFooter();
}
renderFooter()
updateFooter()
{
const disabled = !this.list.selected.size;
bind(this.footer)`
<button
class="delete"
onclick="${this}"
disabled="${disabled}"
data-call="onfooterclick"
>${{i18n: "delete"}}</button>
<button
class="copy"
onclick="${this}"
disabled="${disabled}"
data-call="onfooterclick"
>${{i18n: "copy_selected"}}</button>
<button
......@@ -254,17 +247,19 @@ class IOFilterTable extends IOElement
data-call="onerrorclick"
></button>
`;
this.updateFooter();
}
updateFooter()
{
this.footer.classList.toggle("visible", !!this.filters.length);
}
}
IOFilterTable.define("io-filter-table");
function cleanErrors()
{
const error = this.querySelector(".footer .error");
if (error)
error.textContent = "";
this.updateFooter();
}
function updateList(list)
{
list.render();
......
......@@ -117,7 +117,7 @@
},
"options_filters_search_or_add": {
"description": "The filter search placeholder.",
"message": "Search / add filter (e.g. $filter$)",
"message": "Search or add filter(s) (e.g. $filter$)",
"placeholders": {
"filter": {
"content": "/ads/track/*"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment