| Index: options.js |
| =================================================================== |
| new file mode 100644 |
| --- /dev/null |
| +++ b/options.js |
| @@ -0,0 +1,657 @@ |
| +/* |
| + * This file is part of Adblock Plus <https://adblockplus.org/>, |
| + * Copyright (C) 2006-2015 Eyeo GmbH |
| + * |
| + * Adblock Plus is free software: you can redistribute it and/or modify |
| + * it under the terms of the GNU General Public License version 3 as |
| + * published by the Free Software Foundation. |
| + * |
| + * Adblock Plus is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| + */ |
| + |
| +"use strict"; |
| + |
| +(function() |
| +{ |
| + var subscriptionsMap = Object.create(null); |
| + var recommendationsMap = Object.create(null); |
| + var filtersMap = Object.create(null); |
| + var collections = Object.create(null); |
| + |
| + function Collection(details) |
| + { |
| + this.details = details; |
| + this.items = Object.create(Array.prototype); |
|
Felix Dahlke
2015/06/12 12:30:47
Wouldn't `this.items = []` do?
|
| + } |
| + |
| + Collection.prototype.addItems = function() |
| + { |
| + var length = Array.prototype.push.apply(this.items, arguments); |
| + if (length == 0) |
| + return; |
| + |
| + this.items.sort(function(a, b) |
| + { |
| + var aValue = (a.title || a.url || a.text).toLowerCase(); |
| + var bValue = (b.title || b.url || a.text).toLowerCase(); |
| + return aValue.localeCompare(bValue); |
| + }); |
| + |
| + for (var j = 0; j < this.details.length; j++) |
| + { |
| + var table = E(this.details[j].id); |
| + var template = table.querySelector("template"); |
| + for (var i = 0; i < arguments.length; i++) |
| + { |
| + var item = arguments[i]; |
| + var text = item.title || item.url || item.text; |
| + var listItem = document.createElement("li"); |
| + listItem.appendChild(document.importNode(template.content, true)); |
| + listItem.dataset.access = item.url || item.text; |
| + listItem.querySelector(".display").textContent = text; |
| + if (text) |
| + listItem.dataset.search = text.toLowerCase(); |
| + |
| + var control = listItem.querySelector(".control"); |
| + if (control) |
| + { |
| + control.addEventListener("click", this.details[j].onClick, false); |
| + control.checked = item.disabled == false; |
| + } |
| + |
| + if (table.hasChildNodes()) |
| + table.insertBefore(listItem, table.childNodes[this.items.indexOf(item)]); |
| + else |
| + table.appendChild(listItem); |
| + } |
| + } |
| + return length; |
| + }; |
| + |
| + Collection.prototype.removeItem = function(item) |
| + { |
| + var index = this.items.indexOf(item); |
| + if (index == -1) |
| + return; |
| + |
| + this.items.splice(index, 1); |
| + var access = (item.url || item.text).replace(/'/g, "\\'"); |
| + for (var i = 0; i < this.details.length; i++) |
| + { |
| + var table = E(this.details[i].id); |
| + var element = table.querySelector("[data-access='" + access + "']"); |
| + element.parentElement.removeChild(element); |
| + } |
| + }; |
| + |
| + Collection.prototype.clearAll = function() |
| + { |
| + for (var i = 0; i < this.details.length; i++) |
| + { |
| + var table = E(this.details[i].id); |
| + var template = table.querySelector("template"); |
| + table.innerHTML = ""; |
| + table.appendChild(template); |
| + } |
| + this.items.length = 0; |
| + }; |
| + |
| + function onToggleSubscriptionClick(e) |
| + { |
| + e.preventDefault(); |
| + var subscriptionUrl = e.target.parentNode.dataset.access; |
| + if (!e.target.checked) |
| + removeSubscription(subscriptionUrl); |
| + else |
| + addEnableSubscription(subscriptionUrl); |
| + } |
| + |
| + function onAddLanguageSubscriptionClick(e) |
| + { |
| + e.preventDefault(); |
| + var url = this.parentNode.dataset.access; |
| + addEnableSubscription(url); |
| + } |
| + |
| + function onRemoveFilterClick() |
| + { |
| + var filter = this.parentNode.dataset.access; |
| + removeFilter(filter); |
| + } |
| + |
| + collections.popular = new Collection( |
| + [ |
| + { |
| + id: "recommend-list-table", |
| + onClick: onToggleSubscriptionClick |
| + } |
| + ]); |
| + collections.langs = new Collection( |
| + [ |
| + { |
| + id: "blocking-languages-table", |
| + onClick: onToggleSubscriptionClick |
| + }, |
| + { |
| + id: "blocking-languages-dialog-table" |
| + } |
| + ]); |
| + collections.allLangs = new Collection( |
| + [ |
| + { |
| + id: "all-lang-table", |
| + onClick: onAddLanguageSubscriptionClick |
| + } |
| + ]); |
| + collections.acceptableAds = new Collection( |
| + [ |
| + { |
| + id: "acceptableads-table", |
| + onClick: onToggleSubscriptionClick |
| + } |
| + ]); |
| + collections.custom = new Collection( |
| + [ |
| + { |
| + id: "custom-list-table", |
| + onClick: onToggleSubscriptionClick |
| + } |
| + ]); |
| + collections.whitelist = new Collection( |
| + [ |
| + { |
| + id: "whitelisting-table", |
| + onClick: onRemoveFilterClick |
| + } |
| + ]); |
| + |
| + function updateSubscription(subscription) |
| + { |
| + var subscriptionUrl = subscription.url; |
| + var knownSubscription = subscriptionsMap[subscriptionUrl]; |
| + if (knownSubscription) |
| + knownSubscription.disabled = subscription.disabled; |
| + else |
| + { |
| + getAcceptableAdsURL(function(acceptableAdsUrl) |
| + { |
| + function onObjectChanged() |
| + { |
| + var access = (subscriptionUrl || subscription.text).replace(/'/g, "\\'"); |
| + var elements = document.querySelectorAll("[data-access='" + access + "']"); |
| + for (var i = 0; i < elements.length; i++) |
| + { |
| + var element = elements[i]; |
| + var control = element.querySelector(".control"); |
| + if (control.localName == "input") |
| + control.checked = subscription.disabled == false; |
| + if (subscriptionUrl in recommendationsMap) |
| + { |
| + var recommendation = recommendationsMap[subscriptionUrl]; |
| + if (recommendation.isAdsType) |
| + { |
| + if (subscription.disabled == false) |
| + { |
| + collections.allLangs.removeItem(subscription); |
| + collections.langs.addItems(subscription); |
| + } |
| + else |
| + { |
| + collections.allLangs.addItems(subscription); |
| + collections.langs.removeItem(subscription); |
| + } |
| + } |
| + } |
| + } |
| + } |
| + |
| + if (!Object.observe) |
| + { |
| + // Currently only "disabled" property of subscription used for observation |
| + // but with Advanced tab implementation we should also add more properties. |
| + ["disabled"].forEach(function(property) |
| + { |
| + subscription["$" + property] = subscription[property]; |
| + Object.defineProperty(subscription, property, |
| + { |
| + get: function() |
| + { |
| + return this["$" + property]; |
| + }, |
| + set: function(value) |
| + { |
| + this["$" + property] = value; |
| + onObjectChanged(); |
| + } |
| + }); |
| + }); |
| + } |
| + else |
| + { |
| + Object.observe(subscription, onObjectChanged); |
| + } |
| + |
| + var collection = null; |
| + if (subscriptionUrl in recommendationsMap) |
| + { |
| + var recommendation = recommendationsMap[subscriptionUrl]; |
| + if (recommendation.isPopular) |
| + collection = collections.popular; |
| + else if (recommendation.isAdsType && subscription.disabled == false) |
| + collection = collections.langs; |
| + else |
| + collection = collections.allLangs; |
| + } |
| + else if (subscriptionUrl == acceptableAdsUrl) |
| + collection = collections.acceptableAds; |
| + else |
| + collection = collections.custom; |
| + |
| + collection.addItems(subscription); |
| + subscriptionsMap[subscriptionUrl] = subscription; |
| + }); |
| + } |
| + } |
| + |
| + function updateFilter(filter) |
| + { |
| + var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/); |
| + if (match && !filtersMap[filter.text]) |
| + { |
| + filter.title = match[1]; |
| + collections.whitelist.addItems(filter); |
| + filtersMap[filter.text] = filter |
| + } |
| + else |
| + { |
| + // TODO: add `filters[i].text` to list of custom filters |
| + } |
| + } |
| + |
| + function loadRecommendations() |
| + { |
| + var request = new XMLHttpRequest(); |
| + request.open("GET", "subscriptions.xml", false); |
| + request.addEventListener("load", function() |
| + { |
| + var list = document.getElementById("subscriptionSelector"); |
| + var docElem = request.responseXML.documentElement; |
| + var elements = docElem.getElementsByTagName("subscription"); |
| + for (var i = 0; i < elements.length; i++) |
| + { |
| + var element = elements[i]; |
| + var subscription = Object.create(null); |
| + subscription.title = element.getAttribute("title"); |
| + subscription.url = element.getAttribute("url"); |
| + subscription.disabled = null; |
| + subscription.downloadStatus = null; |
| + subscription.homepage = null; |
| + subscription.lastSuccess = null; |
| + var recommendation = Object.create(null); |
| + recommendation.isAdsType = false; |
| + recommendation.isPopular = false; |
| + var prefix = element.getAttribute("prefixes"); |
| + if (prefix) |
| + { |
| + var prefix = element.getAttribute("prefixes").replace(/,/g, "_"); |
| + subscription.title = ext.i18n.getMessage("options_language_" + prefix); |
| + recommendation.isAdsType = true; |
| + } |
| + else |
| + subscription.title = element.getAttribute("specialization"); |
| + |
| + if (element.getAttribute("popular")) |
| + recommendation.isPopular = true; |
| + |
| + recommendationsMap[subscription.url] = recommendation; |
| + updateSubscription(subscription); |
| + } |
| + }, false); |
| + request.send(null); |
| + } |
| + |
| + function onDOMLoaded() |
| + { |
| + var recommendationTemplate = document.querySelector("#recommend-list-table template"); |
| + var popularText = ext.i18n.getMessage("options_popular"); |
| + recommendationTemplate.content.querySelector(".popular").textContent = popularText; |
| + var languagesTemplate = document.querySelector("#all-lang-table template"); |
| + var buttonText = ext.i18n.getMessage("options_button_add"); |
| + languagesTemplate.content.querySelector(".button-add span").textContent = buttonText; |
| + |
| + updateShareLink(); |
| + populateLists(); |
| + |
| + var tabList = document.querySelectorAll("#main-navigation-tabs li"); |
| + for (var i = 0; i < tabList.length; i++) |
| + { |
| + tabList[i].addEventListener("click", function(e) |
| + { |
| + document.body.dataset.tab = e.currentTarget.dataset.show; |
| + }, false); |
| + } |
| + |
| + function onFindLanguageKeyUp() |
| + { |
| + var searchStyle = E("search-style"); |
| + if (!this.value) |
| + searchStyle.innerHTML = ""; |
| + else |
| + searchStyle.innerHTML = "#all-lang-table li:not([data-search*=\"" + this.value.toLowerCase() + "\"]) { display: none; }"; |
| + } |
| + |
| + // Update version number in navigation sidebar |
| + ext.backgroundPage.sendMessage( |
| + { |
| + method: "app.get", |
| + what: "addonVersion" |
| + }, |
| + function(addonVersion) |
| + { |
| + E("abp-version").textContent = addonVersion; |
| + }); |
| + |
| + var placeholderValue = ext.i18n.getMessage("options_dialog_language_find"); |
| + E("find-language").setAttribute("placeholder", placeholderValue); |
| + E("add-blocking-list").addEventListener("click", function() |
| + { |
| + openDialog("customlist"); |
| + }, false); |
| + E("add-website-language").addEventListener("click", function() |
| + { |
| + openDialog("language"); |
| + }, false); |
| + E("dialog-close").addEventListener("click", function() |
| + { |
| + delete document.body.dataset.dialog; |
| + }, false); |
| + E("edit-ownBlockingList-button").addEventListener("click", editCustomFilters, false); |
| + E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); |
| + E("whitelisting").addEventListener("click", function(e) |
| + { |
| + var id = e.target.id; |
| + if (id == "whitelisting-add-icon" || id == "whitelisting-enter-icon") |
| + addWhitelistedDomain(); |
| + else if (id == "whitelisting-cancel-button") |
| + E("whitelisting-textbox").value = ""; |
| + }, false); |
| + E("whitelisting-add-button").addEventListener("click", addWhitelistedDomain, false); |
| + E("whitelisting-textbox").addEventListener("keypress", function(e) |
| + { |
| + // e.keyCode has been deprecated so we attempt to use e.key |
| + // keyCode "13" corresponds to "Enter" |
| + if ((e.key && e.key == "Enter") || (!e.key && e.keyCode == 13)) |
| + addWhitelistedDomain(); |
| + }, false); |
| + E("import-blockingList-button").addEventListener("click", function() |
| + { |
| + var url = E("blockingList-textbox").value; |
| + addEnableSubscription(url); |
| + delete document.body.dataset.dialog; |
| + }, false); |
| + } |
| + |
| + function openDialog(name) |
| + { |
| + document.body.dataset.dialog = name; |
| + } |
| + |
| + function populateLists() |
| + { |
| + subscriptionsMap = Object.create(null); |
| + filtersMap = Object.create(null); |
| + recommendationsMap = Object.create(null); |
| + |
| + // Empty collections and lists |
| + for (var property in collections) |
| + collections[property].clearAll(); |
| + |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "subscriptions.get", |
| + special: true |
| + }, |
| + function(subscriptions) |
| + { |
| + // Load filters |
| + for (var i = 0; i < subscriptions.length; i++) |
| + { |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "filters.get", |
| + subscriptionUrl: subscriptions[i].url |
| + }, |
| + function(filters) |
| + { |
| + for (var i = 0; i < filters.length; i++) |
| + updateFilter(filters[i]); |
| + }); |
| + } |
| + }); |
| + loadRecommendations(); |
| + getAcceptableAdsURL(function(acceptableAdsUrl) |
| + { |
| + var subscription = Object.create(null); |
| + subscription.url = acceptableAdsUrl; |
| + subscription.disabled = true; |
| + subscription.title = ext.i18n.getMessage("options_acceptableAds_description"); |
| + updateSubscription(subscription); |
| + |
| + // Load user subscriptions |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "subscriptions.get", |
| + downloadable: true |
| + }, |
| + function(subscriptions) |
| + { |
| + for (var i = 0; i < subscriptions.length; i++) |
| + onSubscriptionMessage("added", subscriptions[i]); |
| + }); |
| + }); |
| + } |
| + |
| + function addWhitelistedDomain() |
| + { |
| + var domain = E("whitelisting-textbox"); |
| + if (domain.value) |
| + { |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "filters.add", |
| + text: "@@||" + domain.value.toLowerCase() + "^$document" |
| + }); |
| + } |
| + |
| + domain.value = ""; |
| + } |
| + |
| + function editCustomFilters() |
| + { |
| + //TODO: NYI |
| + } |
| + |
| + function getAcceptableAdsURL(callback) |
| + { |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "prefs.get", |
| + key: "subscriptions_exceptionsurl" |
| + }, |
| + function(value) |
| + { |
| + getAcceptableAdsURL = function(callback) |
| + { |
| + callback(value); |
| + } |
| + getAcceptableAdsURL(callback); |
| + }); |
| + } |
| + |
| + function addEnableSubscription(url, title, homepage) |
| + { |
| + var messageType = null; |
| + var knownSubscription = subscriptionsMap[url]; |
| + if (knownSubscription && knownSubscription.disabled == true) |
| + messageType = "subscriptions.toggle" |
| + else |
| + messageType = "subscriptions.add" |
| + |
| + var message = { |
| + type: messageType, |
| + url: url |
| + }; |
| + if (title) |
| + message.title = title; |
| + if (homepage) |
| + message.homepage = homepage; |
| + |
| + ext.backgroundPage.sendMessage(message); |
| + } |
| + |
| + function removeSubscription(url) |
| + { |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "subscriptions.remove", |
| + url: url |
| + }); |
| + } |
| + |
| + function removeFilter(filter) |
| + { |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "filters.remove", |
| + text: filter |
| + }); |
| + } |
| + |
| + function onFilterMessage(action, filter) |
| + { |
| + switch (action) |
| + { |
| + case "added": |
| + updateFilter(filter); |
| + updateShareLink(); |
| + break; |
| + case "loaded": |
| + populateLists(); |
| + break; |
| + case "removed": |
| + var knownFilter = filtersMap[filter.text]; |
| + collections.whitelist.removeItem(knownFilter); |
| + delete filtersMap[filter.text]; |
| + updateShareLink(); |
| + break; |
| + } |
| + } |
| + |
| + function onSubscriptionMessage(action, subscription) |
| + { |
| + switch (action) |
| + { |
| + case "added": |
| + case "disabled": |
| + updateSubscription(subscription); |
| + updateShareLink(); |
| + break; |
| + case "homepage": |
| + // TODO: NYI |
| + break; |
| + case "removed": |
| + getAcceptableAdsURL(function(acceptableAdsUrl) |
| + { |
| + if (subscription.url == acceptableAdsUrl) |
| + { |
| + subscription.disabled = true; |
| + updateSubscription(subscription); |
| + } |
| + else |
| + { |
| + var knownSubscription = subscriptionsMap[subscription.url]; |
| + if (subscription.url in recommendationsMap) |
| + knownSubscription.disabled = true; |
| + else |
| + { |
| + collections.custom.removeItem(knownSubscription); |
| + delete subscriptionsMap[subscription.url]; |
| + } |
| + } |
| + updateShareLink(); |
| + }); |
| + break; |
| + case "title": |
| + // TODO: NYI |
| + break; |
| + } |
| + } |
| + |
| + function showAddSubscriptionDialog(subscription) |
| + { |
| + E("blockingList-textbox").value = subscription.url; |
| + openDialog("customlist"); |
| + } |
| + |
| + function updateShareLink() |
| + { |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "filters.blocked", |
| + url: "https://platform.twitter.com/widgets/", |
| + requestType: "SCRIPT", |
| + docDomain: "adblockplus.org", |
| + thirdParty: true |
| + }, |
| + function(blocked) |
| + { |
| + // TODO: modify "share" link accordingly |
| + }); |
| + } |
| + |
| + function E(id) |
| + { |
| + return document.getElementById(id); |
| + } |
| + |
| + ext.onMessage.addListener(function(message) |
| + { |
| + switch (message.type) |
| + { |
| + case "app.listen": |
| + if (message.action == "addSubscription") |
| + showAddSubscriptionDialog(message.args[0]); |
| + break; |
| + case "filters.listen": |
| + onFilterMessage(message.action, message.args[0]); |
| + break; |
| + case "subscriptions.listen": |
| + onSubscriptionMessage(message.action, message.args[0]); |
| + break; |
| + } |
| + }); |
| + |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "app.listen", |
| + filter: ["addSubscription"] |
| + }); |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "filters.listen", |
| + filter: ["added", "loaded", "removed"] |
| + }); |
| + ext.backgroundPage.sendMessage( |
| + { |
| + type: "subscriptions.listen", |
| + filter: ["added", "disabled", "homepage", "removed", "title"] |
| + }); |
| + |
| + window.addEventListener("DOMContentLoaded", onDOMLoaded, false); |
| +})(); |