Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: options.js

Issue 6088024630755328: issue 1526 - Implement new options page design for Chrome/Opera/Safari (Closed)
Patch Set: Comment about solution being temprorary is added to subscriptions.xml Created June 13, 2015, 12:59 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « options.html ('k') | skin/options.css » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 = [];
+ }
+
+ 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);
+})();
« no previous file with comments | « options.html ('k') | skin/options.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld