| 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 = []; |
| let filterErrors = new Map([ |
| ["synchronize_invalid_url", |
| "options_filterList_lastDownload_invalidURL"], |
| @@ -38,6 +39,11 @@ |
| "options_filterList_lastDownload_checksumMismatch"] |
| ]); |
| const timestampUI = Symbol(); |
| + const whitelistedDomainRegexp = /^@@\|\|([^/:]+)\^\$document$/; |
| + // Period of time in milliseconds |
| + const minuteInMs = 60000; |
| + const hourInMs = 3600000; |
| + const fullDayInMs = 86400000; |
| function Collection(details) |
| { |
| @@ -119,7 +125,8 @@ |
| this._sortItems(); |
| 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)); |
| @@ -145,13 +152,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; |
| @@ -221,9 +226,8 @@ |
| control.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) |
| @@ -243,9 +247,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); |
| + } |
| + 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"); |
| } |
| } |
| @@ -340,12 +374,6 @@ |
| emptyText: ["options_whitelist_empty_1", "options_whitelist_empty_2"] |
| } |
| ]); |
| - collections.customFilters = new Collection([ |
| - { |
| - id: "custom-filters-table", |
| - emptyText: ["options_customFilters_empty"] |
| - } |
| - ]); |
| collections.filterLists = new Collection([ |
| { |
| id: "all-filter-lists-table", |
| @@ -403,18 +431,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) |
| + updateCustomFiltersUi(); |
| + } |
| filtersMap[filter.text] = filter; |
| } |
| + function removeCustomFilter(text) |
| + { |
| + let index = customFiltersArray.indexOf(text); |
| + if (index >= 0) |
| + customFiltersArray.splice(index, 1); |
| + |
| + updateCustomFiltersUi(); |
| + } |
| + |
| + function updateCustomFiltersUi() |
| + { |
| + let customFiltersListElement = E("custom-filters-raw"); |
| + customFiltersListElement.value = ""; |
| + customFiltersListElement.value = customFiltersArray.join("\n"); |
| + } |
| + |
| function loadRecommendations() |
| { |
| fetch("subscriptions.xml") |
| @@ -499,7 +547,7 @@ |
| location.hash = id; |
| } |
| - function execAction(action, element) |
| + function execAction(action, element, event) |
| { |
| switch (action) |
| { |
| @@ -518,13 +566,14 @@ |
| break; |
| } |
| case "cancel-custom-filters": |
| - E("custom-filters").classList.remove("mode-edit"); |
| + updateCustomFiltersUi(); |
| + setCustomFiltersView("read"); |
| break; |
| case "close-dialog": |
| closeDialog(); |
| break; |
| case "edit-custom-filters": |
| - editCustomFilters(); |
| + setCustomFiltersView("write"); |
| break; |
| case "import-subscription": { |
| let url = E("blockingList-textbox").value; |
| @@ -568,7 +617,7 @@ |
| }, |
| () => |
| { |
| - E("custom-filters").classList.remove("mode-edit"); |
| + setCustomFiltersView("read"); |
| }); |
| break; |
| case "switch-tab": |
| @@ -614,6 +663,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(); |
| + customFiltersListElement.disabled = false; |
| + } |
| + |
| + customFilters.dataset.mode = mode; |
| + } |
| + |
| function onClick(e) |
| { |
| let context = document.querySelector(".show-context-menu"); |
| @@ -627,7 +698,7 @@ |
| actions = actions.split(","); |
| for (let action of actions) |
| { |
| - execAction(action, e.target); |
| + execAction(action, e.target, e); |
| } |
| } |
| @@ -674,7 +745,7 @@ |
| let actions = container.getAttribute("data-action").split(","); |
| for (let action of actions) |
| { |
| - execAction(action, element); |
| + execAction(action, element, e); |
| } |
| } |
| @@ -699,10 +770,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(); |
| @@ -774,12 +841,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) => |
| { |
| @@ -795,26 +862,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) => |
| @@ -947,6 +1006,8 @@ |
| updateFilter(filter); |
| isCustomFiltersLoaded = true; |
| + updateCustomFiltersUi(); |
| + setCustomFiltersView("read"); |
| }); |
| } |
| }); |
| @@ -1051,8 +1112,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; |