| Index: new-options.js |
| =================================================================== |
| --- a/new-options.js |
| +++ b/new-options.js |
| @@ -27,6 +27,7 @@ |
| let acceptableAdsUrl = null; |
| let isCustomFiltersLoaded = false; |
| let {getMessage} = ext.i18n; |
| + let customFiltersArray = []; |
|
Thomas Greiner
2017/07/14 12:26:13
Detail: I'd recommend not to include the value typ
saroyanm
2017/07/14 16:17:25
Acknowledged.
saroyanm
2017/07/14 17:11:06
Done.
|
| let filterErrors = new Map([ |
| ["synchronize_invalid_url", |
| "options_filterList_lastDownload_invalidURL"], |
| @@ -38,6 +39,12 @@ |
| "options_filterList_lastDownload_checksumMismatch"] |
| ]); |
| + const whitelistedDomainRegexp = /^@@\|\|([^/:]+)\^\$document$/; |
| + // Period of time in milliseconds |
| + const minuteInMs = 60000; |
| + const hourInMs = 3600000; |
| + const fullDayInMs = 86400000; |
| + |
| function Collection(details) |
| { |
| this.details = details; |
| @@ -100,7 +107,8 @@ |
| for (let j = 0; j < this.details.length; j++) |
| { |
| - let table = E(this.details[j].id); |
| + let detail = this.details[j]; |
| + let table = E(detail.id); |
| let template = table.querySelector("template"); |
| let listItem = document.createElement("li"); |
| listItem.appendChild(document.importNode(template.content, true)); |
| @@ -126,13 +134,11 @@ |
| } |
| this._setEmpty(table, null); |
| - if (table.hasChildNodes()) |
| - { |
| - table.insertBefore(listItem, |
| - table.childNodes[this.items.indexOf(item)]); |
| - } |
| + if (table.children.length > 0) |
| + table.insertBefore(listItem, table.children[this.items.indexOf(item)]); |
| else |
| table.appendChild(listItem); |
| + |
| this.updateItem(item); |
| } |
| return length; |
| @@ -200,9 +206,8 @@ |
| control.setAttribute("disabled", true); |
| } |
| - let dateElement = element.querySelector(".date"); |
| - let timeElement = element.querySelector(".time"); |
| - if (dateElement && timeElement) |
| + let lastUpdateElement = element.querySelector(".last-update"); |
| + if (lastUpdateElement) |
| { |
| let message = element.querySelector(".message"); |
| if (item.isDownloading) |
| @@ -222,9 +227,39 @@ |
| } |
| else if (item.lastDownload > 0) |
| { |
| - let dateTime = i18nFormatDateTime(item.lastDownload * 1000); |
| - dateElement.textContent = dateTime[0]; |
| - timeElement.textContent = dateTime[1]; |
| + let lastUpdate = item.lastDownload * 1000; |
| + let lastUpdateLive = function() |
| + { |
| + let sinceUpdate = Date.now() - lastUpdate; |
| + if (sinceUpdate > fullDayInMs) |
| + { |
| + let lastUpdateDate = new Date(item.lastDownload * 1000); |
| + let monthName = lastUpdateDate.toLocaleString(undefined, |
| + {month: "short"}); |
| + let day = lastUpdateDate.getDate(); |
| + day = day < 10 ? "0" + day : day; |
| + lastUpdateElement.textContent = day + " " + monthName + " " + |
| + lastUpdateDate.getFullYear(); |
| + } |
| + else if (sinceUpdate > hourInMs) |
| + { |
| + let placeholder = [Math.round(sinceUpdate / hourInMs)]; |
| + lastUpdateElement.textContent = |
| + getMessage("options_filterList_hours", placeholder); |
|
Thomas Greiner
2017/07/14 12:26:12
This placeholder isn't defined in new-options.json
saroyanm
2017/07/14 16:17:24
Well spotted, will remove it.
saroyanm
2017/07/14 17:11:06
Done.
|
| + } |
| + else if (sinceUpdate > minuteInMs) |
| + { |
| + let placeholder = [Math.round(sinceUpdate / minuteInMs)]; |
| + lastUpdateElement.textContent = |
| + getMessage("options_filterList_minutes", placeholder); |
| + } |
| + else |
| + { |
| + lastUpdateElement.textContent = |
| + getMessage("options_filterList_now"); |
| + } |
| + }; |
| + lastUpdateLive(); |
| element.classList.remove("show-message"); |
| } |
| } |
| @@ -315,12 +350,6 @@ |
| emptyText: "options_whitelisted_empty" |
| } |
| ]); |
| - collections.customFilters = new Collection([ |
| - { |
| - id: "custom-filters-table", |
| - emptyText: "options_customFilters_empty" |
| - } |
| - ]); |
| collections.filterLists = new Collection([ |
| { |
| id: "all-filter-lists-table", |
| @@ -378,18 +407,38 @@ |
| function updateFilter(filter) |
| { |
| - let match = filter.text.match(/^@@\|\|([^/:]+)\^\$document$/); |
| + let match = filter.text.match(whitelistedDomainRegexp); |
| if (match && !filtersMap[filter.text]) |
| { |
| filter.title = match[1]; |
| collections.whitelist.addItem(filter); |
| } |
| else |
| - collections.customFilters.addItem(filter); |
| + { |
| + customFiltersArray.push(filter.text); |
| + if (isCustomFiltersLoaded) |
|
Thomas Greiner
2017/07/14 12:26:12
Detail: What is this check for? It seems that `upd
saroyanm
2017/07/14 16:17:25
Shouldn't we wait only for the time when all filte
Thomas Greiner
2017/07/14 16:37:42
Right, I remember and I see that this has actually
saroyanm
2017/07/14 16:41:34
I agree, I'll create a separate issue for that, wh
|
| + updateCustomFiltersUi(); |
| + } |
| filtersMap[filter.text] = filter; |
| } |
| + function removeCustomFilter(text) |
| + { |
| + let index = customFiltersArray.indexOf(text); |
| + if (index >= 0) |
| + customFiltersArray.splice(index, 1); |
|
Thomas Greiner
2017/07/14 12:26:12
What if there are multiple instances of the same f
saroyanm
2017/07/14 16:17:25
There shouldn't be multiple instances of same filt
Thomas Greiner
2017/07/14 16:37:42
My thinking was that a pre-existing filter could a
saroyanm
2017/07/14 16:41:34
Acknowledged.
|
| + |
| + updateCustomFiltersUi(); |
| + } |
| + |
| + function updateCustomFiltersUi() |
| + { |
| + let customFiltersListElement = E("custom-filters-raw"); |
| + customFiltersListElement.value = ""; |
|
Thomas Greiner
2017/07/14 12:26:12
Detail: This is redundant because the line below w
saroyanm
2017/07/14 16:17:24
Thanks for noticing. Will update.
saroyanm
2017/07/14 17:11:06
Done.
|
| + customFiltersListElement.value = customFiltersArray.join("\n"); |
| + } |
| + |
| function loadRecommendations() |
| { |
| fetch("subscriptions.xml") |
| @@ -474,7 +523,7 @@ |
| location.hash = id; |
| } |
| - function execAction(action, element) |
| + function execAction(action, element, event) |
| { |
| switch (action) |
| { |
| @@ -493,7 +542,8 @@ |
| break; |
| } |
| case "cancel-custom-filters": |
| - E("custom-filters").classList.remove("mode-edit"); |
| + updateCustomFiltersUi(); |
| + setCustomFiltersView("read"); |
| break; |
| case "cancel-domain-exception": |
| E("whitelisting-textbox").value = ""; |
| @@ -504,7 +554,7 @@ |
| closeDialog(); |
| break; |
| case "edit-custom-filters": |
| - editCustomFilters(); |
| + setCustomFiltersView("write"); |
| break; |
| case "edit-domain-exception": |
| document.querySelector("#whitelisting .controls").classList |
| @@ -553,7 +603,7 @@ |
| }, |
| () => |
| { |
| - E("custom-filters").classList.remove("mode-edit"); |
| + setCustomFiltersView("read"); |
| }); |
| break; |
| case "switch-tab": |
| @@ -599,6 +649,28 @@ |
| } |
| } |
| + function setCustomFiltersView(mode) |
| + { |
| + let customFilters = E("custom-filters"); |
| + let customFiltersListElement = E("custom-filters-raw"); |
| + if (mode == "read") |
| + { |
| + customFiltersListElement.disabled = true; |
| + if (!customFiltersListElement.value) |
| + { |
| + setCustomFiltersView("empty"); |
| + return; |
| + } |
| + } |
| + else if (mode == "write") |
| + { |
| + updateCustomFiltersUi(); |
|
Thomas Greiner
2017/07/14 12:26:13
Interesting that you're calling `updateCustomFilte
saroyanm
2017/07/14 16:17:25
You are right, I'm not sure why I did this.. I'll
saroyanm
2017/07/14 17:11:06
Done.
|
| + customFiltersListElement.disabled = false; |
| + } |
| + |
| + customFilters.dataset.mode = mode; |
| + } |
| + |
| function onClick(e) |
| { |
| let context = document.querySelector(".show-context-menu"); |
| @@ -612,7 +684,7 @@ |
| actions = actions.split(","); |
| for (let action of actions) |
| { |
| - execAction(action, e.target); |
| + execAction(action, e.target, e); |
| } |
| } |
| @@ -659,7 +731,7 @@ |
| let actions = container.getAttribute("data-action").split(","); |
| for (let action of actions) |
| { |
| - execAction(action, element); |
| + execAction(action, element, e); |
| } |
| } |
| @@ -684,10 +756,6 @@ |
| let tabContentId = tab.getAttribute("aria-controls"); |
| let tabContent = document.getElementById(tabContentId); |
| - // Select sub tabs |
| - if (tab.hasAttribute("data-subtab")) |
| - selectTabItem(tab.getAttribute("data-subtab"), tabContent, false); |
| - |
| if (tab && focus) |
| tab.focus(); |
| @@ -757,12 +825,12 @@ |
| }, false); |
| // Advanced tab |
| - let tweaks = document.querySelectorAll("#tweaks li[data-pref]"); |
| - tweaks = Array.prototype.map.call(tweaks, (checkbox) => |
| + let customize = document.querySelectorAll("#customize li[data-pref]"); |
| + customize = Array.prototype.map.call(customize, (checkbox) => |
| { |
| return checkbox.getAttribute("data-pref"); |
| }); |
| - for (let key of tweaks) |
| + for (let key of customize) |
| { |
| getPref(key, (value) => |
| { |
| @@ -778,26 +846,18 @@ |
| hidePref("show_devtools_panel", !features.devToolsPanel); |
| }); |
| - let filterTextbox = document.querySelector("#custom-filters-add input"); |
| - placeholderValue = getMessage("options_customFilters_textbox_placeholder"); |
| - filterTextbox.setAttribute("placeholder", placeholderValue); |
| - function addCustomFilters() |
| + getDocLink("filterdoc", (link) => |
| { |
| - let filterText = filterTextbox.value; |
| - sendMessageHandleErrors({ |
| - type: "filters.add", |
| - text: filterText |
| - }, |
| - () => |
| - { |
| - filterTextbox.value = ""; |
| - }); |
| - } |
| - E("custom-filters-add").addEventListener("submit", (e) => |
| + E("link-filters").setAttribute("href", link); |
| + }); |
| + |
| + getDocLink("subscriptions", (link) => |
| { |
| - e.preventDefault(); |
| - addCustomFilters(); |
| - }, false); |
| + setLinks("filter-lists-description", link); |
| + }); |
| + |
| + E("custom-filters-raw").setAttribute("placeholder", |
| + getMessage("options_customFilters_edit_placeholder", ["/ads/track/*"])); |
| // Help tab |
| getDocLink("faq", (link) => |
| @@ -930,6 +990,8 @@ |
| updateFilter(filter); |
| isCustomFiltersLoaded = true; |
| + updateCustomFiltersUi(); |
| + setCustomFiltersView("read"); |
| }); |
| } |
| }); |
| @@ -975,21 +1037,6 @@ |
| .classList.remove("mode-edit"); |
| } |
| - function editCustomFilters() |
| - { |
| - if (!isCustomFiltersLoaded) |
| - { |
| - console.error("Custom filters are not loaded"); |
| - return; |
| - } |
| - |
| - E("custom-filters").classList.add("mode-edit"); |
| - let filterTexts = []; |
| - for (let customFilterItem of collections.customFilters.items) |
| - filterTexts.push(customFilterItem.text); |
| - E("custom-filters-raw").value = filterTexts.join("\n"); |
| - } |
| - |
| function addEnableSubscription(url, title, homepage) |
| { |
| let messageType = null; |
| @@ -1024,8 +1071,11 @@ |
| break; |
| case "removed": |
| let knownFilter = filtersMap[filter.text]; |
| - collections.whitelist.removeItem(knownFilter); |
| - collections.customFilters.removeItem(knownFilter); |
| + if (whitelistedDomainRegexp.test(knownFilter.text)) |
| + collections.whitelist.removeItem(knownFilter); |
| + else |
| + removeCustomFilter(filter.text); |
| + |
| delete filtersMap[filter.text]; |
| updateShareLink(); |
| break; |