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

Side by Side Diff: options.js

Issue 29339314: Issue 3870 - Rewrite legacy options page to use async messages (Closed)
Patch Set: Created April 3, 2016, 10:51 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « dependencies ('k') | safari/ext/background.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
Sebastian Noack 2016/04/03 15:05:14 This rewrite is a good opportunity to turn on stri
kzar 2016/04/03 15:50:19 Done.
16 */ 16 */
17 17
18 var backgroundPage = ext.backgroundPage.getWindow(); 18 /**
19 var require = backgroundPage.require; 19 * Creates a wrapping function used to conveniently send a type of message.
20 *
21 * @param {Object} baseMessage The part of the message that's always sent
22 * @param {..string} paramKeys Any message keys that have dynamic values. The
23 * returned function will take the corresponding
24 * values as arguments.
25 * @return The generated messaging function, optionally taking any values as
26 * specified by the paramKeys and finally an optional callback.
27 * (Although the value arguments are optional their index must be
28 * maintained. E.g. if you omit the first value you must omit the
29 * second too.)
30 */
31 function wrapper(baseMessage /* , [paramKeys] */)
32 {
33 var paramKeys = [];
34 for (var i = 1; i < arguments.length; i++)
35 paramKeys.push(arguments[i]);
20 36
21 with(require("filterClasses")) 37 return function(/* [paramValues], callback */)
22 { 38 {
23 this.Filter = Filter; 39 var message = Object.create(null);
24 this.WhitelistFilter = WhitelistFilter; 40 for (var key in baseMessage)
41 if (baseMessage.hasOwnProperty(key))
42 message[key] = baseMessage[key];
43
44 var paramValues = [];
45 var callback;
46
47 if (arguments.length > 0)
48 {
49 var lastArg = arguments[arguments.length - 1];
50 if (typeof lastArg == "function")
51 callback = lastArg;
52
53 for (var i = 0; i < arguments.length - (callback ? 1 : 0); i++)
54 message[paramKeys[i]] = arguments[i];
55 }
56
57 ext.backgroundPage.sendMessage(message, callback);
kzar 2016/04/03 10:55:06 (I considered having this function return a promis
58 };
25 } 59 }
26 with(require("subscriptionClasses"))
27 {
28 this.Subscription = Subscription;
29 this.SpecialSubscription = SpecialSubscription;
30 this.DownloadableSubscription = DownloadableSubscription;
31 }
32 with(require("filterValidation"))
33 {
34 this.parseFilter = parseFilter;
35 this.parseFilters = parseFilters;
36 }
37 var FilterStorage = require("filterStorage").FilterStorage;
38 var FilterNotifier = require("filterNotifier").FilterNotifier;
39 var Prefs = require("prefs").Prefs;
40 var Synchronizer = require("synchronizer").Synchronizer;
41 var Utils = require("utils").Utils;
42 var NotificationStorage = require("notification").Notification;
43 var info = require("info");
44 60
45 // Loads options from localStorage and sets UI elements accordingly 61 var getDocLink = wrapper({type: "app.get", what: "doclink"}, "link");
Sebastian Noack 2016/04/03 15:05:14 This is redundant with getDocLink() from common.js
kzar 2016/04/03 15:50:19 I don't think adblockplusui/common.js is included
Sebastian Noack 2016/04/03 17:18:03 Given that common.js is already used by the first
kzar 2016/04/05 11:10:14 I think I prefer to leave this as it is.
62 var getInfo = wrapper({type: "app.get"}, "what");
63 var getPref = wrapper({type: "prefs.get"}, "key");
64 var togglePref = wrapper({type: "prefs.toggle"}, "key");
65 var getSubscriptions = wrapper({type: "subscriptions.get"},
66 "downloadable", "special");
67 var removeSubscription = wrapper({type: "subscriptions.remove"}, "url");
68 var addSubscription = wrapper({type: "subscriptions.add"},
69 "url", "title", "homepage");
70 var toggleSubscription = wrapper({type: "subscriptions.toggle"},
71 "url", "keepInstalled");
72 var updateSubscription = wrapper({type: "subscriptions.update"}, "url");
73 var isSubscriptionDownloading = wrapper({type: "subscriptions.isDownloading"},
74 "url");
75 var importRawFilters = wrapper({type: "filters.importRaw"}, "text");
76 var addFilter = wrapper({type: "filters.add"}, "text");
77 var getFilters = wrapper({type: "filters.get"}, "subscriptionUrl");
78 var removeFilter = wrapper({type: "filters.remove"}, "text");
79
80 var i18n = ext.i18n;
81 var whitelistedDomainRegexp = /^@@\|\|([^\/:]+)\^\$document$/;
82 var delayedSubscriptionSelection = null;
83
84 // Loads options and sets UI elements accordingly
46 function loadOptions() 85 function loadOptions()
47 { 86 {
48 // Set page title to i18n version of "Adblock Plus Options" 87 // Set page title to i18n version of "Adblock Plus Options"
49 document.title = i18n.getMessage("options"); 88 document.title = i18n.getMessage("options");
50 89
51 // Set links 90 // Set links
52 $("#acceptableAdsLink").attr("href", Prefs.subscriptions_exceptionsurl); 91 getPref("subscriptions_exceptionsurl", function (url)
53 $("#acceptableAdsDocs").attr("href", Utils.getDocLink("acceptable_ads")); 92 {
54 setLinks("filter-must-follow-syntax", Utils.getDocLink("filterdoc")); 93 $("#acceptableAdsLink").attr("href", url);
55 setLinks("found-a-bug", Utils.getDocLink(info.application + "_support")); 94 });
95 getDocLink("acceptable_ads", function (url)
96 {
97 $("#acceptableAdsDocs").attr("href", url);
98 });
99 getDocLink("filterdoc", function (url)
100 {
101 setLinks("filter-must-follow-syntax", url);
102 });
103 getInfo("application", function (application)
104 {
105 getInfo("platform", function (platform)
106 {
107 if (platform == "chromium" && application != "opera")
108 application = "chrome";
109
110 getDocLink(application + "_support", function (url)
111 {
112 setLinks("found-a-bug", url);
113 });
114 });
115 });
56 116
57 // Add event listeners 117 // Add event listeners
58 window.addEventListener("unload", unloadOptions, false);
59 $("#updateFilterLists").click(updateFilterLists); 118 $("#updateFilterLists").click(updateFilterLists);
60 $("#startSubscriptionSelection").click(startSubscriptionSelection); 119 $("#startSubscriptionSelection").click(startSubscriptionSelection);
61 $("#subscriptionSelector").change(updateSubscriptionSelection); 120 $("#subscriptionSelector").change(updateSubscriptionSelection);
62 $("#addSubscription").click(addSubscription); 121 $("#addSubscription").click(addSubscriptionClicked);
63 $("#acceptableAds").click(allowAcceptableAds); 122 $("#acceptableAds").click(toggleAcceptableAds);
64 $("#whitelistForm").submit(addWhitelistDomain); 123 $("#whitelistForm").submit(addWhitelistDomain);
65 $("#removeWhitelist").click(removeSelectedExcludedDomain); 124 $("#removeWhitelist").click(removeSelectedExcludedDomain);
66 $("#customFilterForm").submit(addTypedFilter); 125 $("#customFilterForm").submit(addTypedFilter);
67 $("#removeCustomFilter").click(removeSelectedFilters); 126 $("#removeCustomFilter").click(removeSelectedFilters);
68 $("#rawFiltersButton").click(toggleFiltersInRawFormat); 127 $("#rawFiltersButton").click(toggleFiltersInRawFormat);
69 $("#importRawFilters").click(importRawFiltersText); 128 $("#importRawFilters").click(importRawFiltersText);
70 129
71 FilterNotifier.on("load", reloadFilters);
72 FilterNotifier.on("subscription.title", onSubscriptionChange);
73 FilterNotifier.on("subscription.disabled", onSubscriptionChange);
74 FilterNotifier.on("subscription.homepage", onSubscriptionChange);
75 FilterNotifier.on("subscription.lastDownload", onSubscriptionChange);
76 FilterNotifier.on("subscription.downloadStatus", onSubscriptionChange);
77 FilterNotifier.on("subscription.added", onSubscriptionAdded);
78 FilterNotifier.on("subscription.removed", onSubscriptionRemoved);
79 FilterNotifier.on("filter.added", onFilterAdded);
80 FilterNotifier.on("filter.removed", onFilterRemoved);
81
82 // Display jQuery UI elements 130 // Display jQuery UI elements
83 $("#tabs").tabs(); 131 $("#tabs").tabs();
84 $("button").button(); 132 $("button").button();
85 $(".refreshButton").button("option", "icons", {primary: "ui-icon-refresh"}); 133 $(".refreshButton").button("option", "icons", {primary: "ui-icon-refresh"});
86 $(".addButton").button("option", "icons", {primary: "ui-icon-plus"}); 134 $(".addButton").button("option", "icons", {primary: "ui-icon-plus"});
87 $(".removeButton").button("option", "icons", {primary: "ui-icon-minus"}); 135 $(".removeButton").button("option", "icons", {primary: "ui-icon-minus"});
88 136
89 // Popuplate option checkboxes 137 // Popuplate option checkboxes
90 initCheckbox("shouldShowBlockElementMenu"); 138 initCheckbox("shouldShowBlockElementMenu");
91 initCheckbox("show_devtools_panel"); 139 initCheckbox("show_devtools_panel");
92 initCheckbox("shouldShowNotifications", { 140 initCheckbox("shouldShowNotifications", {
93 get: function() 141 key: "notifications_ignoredcategories",
142 get: function(ignoredcategories)
94 { 143 {
95 return Prefs.notifications_ignoredcategories.indexOf("*") == -1; 144 return ignoredcategories.indexOf("*") == -1;
96 },
97 toggle: function()
98 {
99 NotificationStorage.toggleIgnoreCategory("*");
100 return this.get();
101 } 145 }
102 }); 146 });
103 147
104 if (info.platform != "chromium") 148 getInfo("platform", function (platform)
Sebastian Noack 2016/04/03 15:05:14 Please use app.get[features] like the new options
kzar 2016/04/03 15:50:19 Done.
105 document.getElementById("showDevtoolsPanelContainer").hidden = true; 149 {
106 if (!Prefs.notifications_showui) 150 if (platform != "chromium")
107 document.getElementById("shouldShowNotificationsContainer").hidden = true; 151 document.getElementById("showDevtoolsPanelContainer").hidden = true;
152 });
153 getPref("notifications_showui", function (notifications_showui)
154 {
155 if (!notifications_showui)
156 document.getElementById("shouldShowNotificationsContainer").hidden = true;
157 });
108 158
109 ext.onMessage.addListener(onMessage); 159 // Register listeners in the background message responder
110 ext.backgroundPage.sendMessage({ 160 ext.backgroundPage.sendMessage({
111 type: "app.listen", 161 type: "app.listen",
112 filter: ["addSubscription"] 162 filter: ["addSubscription"]
113 }); 163 });
164 ext.backgroundPage.sendMessage(
165 {
166 type: "filters.listen",
167 filter: ["added", "loaded", "removed"]
168 });
169 ext.backgroundPage.sendMessage(
170 {
171 type: "prefs.listen",
172 filter: ["notifications_ignoredcategories", "notifications_showui",
173 "safari_contentblocker", "show_devtools_panel",
174 "shouldShowBlockElementMenu"]
175 });
176 ext.backgroundPage.sendMessage(
177 {
178 type: "subscriptions.listen",
179 filter: ["added", "disabled", "homepage", "lastDownload", "removed",
180 "title"]
181 });
114 182
115 // Load recommended subscriptions 183 // Load recommended subscriptions
116 loadRecommendations(); 184 loadRecommendations();
117 185
118 // Show user's filters 186 // Show user's filters
119 reloadFilters(); 187 reloadFilters();
120 } 188 }
121 $(loadOptions); 189 $(loadOptions);
122 190
123 function onMessage(msg)
124 {
125 if (msg.type == "app.listen")
126 {
127 if (msg.action == "addSubscription")
128 {
129 var subscription = msg.args[0];
130 startSubscriptionSelection(subscription.title, subscription.url);
131 }
132 }
133 else if (msg.type == "focus-section")
134 {
135 var tabs = document.getElementsByClassName("ui-tabs-panel");
136 for (var i = 0; i < tabs.length; i++)
137 {
138 var found = tabs[i].querySelector("[data-section='" + msg.section + "']");
139 if (!found)
140 continue;
141
142 var previous = document.getElementsByClassName("focused");
143 if (previous.length > 0)
144 previous[0].classList.remove("focused");
145
146 var tab = $("[href='#" + tabs[i].id + "']");
147 $("#tabs").tabs("select", tab.parent().index());
148 found.classList.add("focused");
149 }
150 }
151 };
152
153 // Reloads the displayed subscriptions and filters 191 // Reloads the displayed subscriptions and filters
154 function reloadFilters() 192 function reloadFilters()
155 { 193 {
156 // Load user filter URLs 194 // Load user filter URLs
157 var container = document.getElementById("filterLists"); 195 var container = document.getElementById("filterLists");
158 while (container.lastChild) 196 while (container.lastChild)
159 container.removeChild(container.lastChild); 197 container.removeChild(container.lastChild);
160 198
161 var hasAcceptable = false; 199 getPref("subscriptions_exceptionsurl", function (acceptableAdsUrl)
162 for (var i = 0; i < FilterStorage.subscriptions.length; i++)
163 { 200 {
164 var subscription = FilterStorage.subscriptions[i]; 201 getSubscriptions(true, false, function(subscriptions)
165 if (subscription instanceof SpecialSubscription)
166 continue;
167
168 if (subscription.url == Prefs.subscriptions_exceptionsurl)
169 { 202 {
170 hasAcceptable = true; 203 for (var i = 0; i < subscriptions.length; i++)
171 continue; 204 {
172 } 205 var subscription = subscriptions[i];
173 206 if (subscription.url == acceptableAdsUrl)
174 addSubscriptionEntry(subscription); 207 $("#acceptableAds").prop("checked", !subscription.disabled);
175 } 208 else
176 209 addSubscriptionEntry(subscription);
177 $("#acceptableAds").prop("checked", hasAcceptable); 210 }
211 });
212 });
178 213
179 // User-entered filters 214 // User-entered filters
180 var userFilters = backgroundPage.getUserFilters(); 215 getSubscriptions(false, true, function(subscriptions)
Sebastian Noack 2016/04/03 15:05:14 How about removing this function from background.j
kzar 2016/04/03 15:50:19 Done.
181 populateList("userFiltersBox", userFilters.filters); 216 {
182 populateList("excludedDomainsBox", userFilters.exceptions); 217 clearListBox("userFiltersBox");
183 } 218 clearListBox("excludedDomainsBox");
184 219
185 // Cleans up when the options window is closed 220 for (var i = 0; i < subscriptions.length; i++)
186 function unloadOptions() 221 getFilters(subscriptions[i].url, function(filters)
187 { 222 {
188 FilterNotifier.off("load", reloadFilters); 223 for (var j = 0; j < filters.length; j++)
189 FilterNotifier.off("subscription.title", onSubscriptionChange); 224 {
190 FilterNotifier.off("subscription.disabled", onSubscriptionChange); 225 var filter = filters[j].text;
191 FilterNotifier.off("subscription.homepage", onSubscriptionChange); 226 if (whitelistedDomainRegexp.test(filter))
192 FilterNotifier.off("subscription.lastDownload", onSubscriptionChange); 227 appendToListBox("excludedDomainsBox", RegExp.$1);
193 FilterNotifier.off("subscription.downloadStatus", onSubscriptionChange); 228 else
194 FilterNotifier.off("subscription.added", onSubscriptionAdded); 229 appendToListBox("userFiltersBox", filter);
195 FilterNotifier.off("subscription.removed", onSubscriptionRemoved); 230 }
196 FilterNotifier.off("filter.added", onFilterAdded); 231 });
197 FilterNotifier.off("filter.removed", onFilterRemoved); 232 });
198 } 233 }
199 234
200 function initCheckbox(id, descriptor) 235 function initCheckbox(id, descriptor)
201 { 236 {
202 var checkbox = document.getElementById(id); 237 var checkbox = document.getElementById(id);
203 if (descriptor && descriptor.get) 238 var key = descriptor && descriptor.key || id;
204 checkbox.checked = descriptor.get(); 239 getPref(key, function (value)
205 else 240 {
206 checkbox.checked = Prefs[id]; 241 if (descriptor && descriptor.get)
242 checkbox.checked = descriptor.get(value);
243 else
244 checkbox.checked = value;
245 });
207 246
208 checkbox.addEventListener("click", function() 247 checkbox.addEventListener("click", function()
209 { 248 {
210 if (descriptor && descriptor.toggle) 249 if (descriptor && descriptor.toggle)
211 checkbox.checked = descriptor.toggle(); 250 checkbox.checked = descriptor.toggle();
212 251 togglePref(key);
213 Prefs[id] = checkbox.checked;
214 }, false); 252 }, false);
215 } 253 }
216 254
217 var delayedSubscriptionSelection = null;
218
219 function loadRecommendations() 255 function loadRecommendations()
220 { 256 {
221 fetch("subscriptions.xml") 257 fetch("subscriptions.xml")
222 .then(function(response) 258 .then(function(response)
223 { 259 {
224 return response.text(); 260 return response.text();
225 }) 261 })
226 .then(function(text) 262 .then(function(text)
227 { 263 {
228 var selectedIndex = 0; 264 var selectedIndex = 0;
229 var selectedPrefix = null; 265 var selectedPrefix = null;
230 var matchCount = 0; 266 var matchCount = 0;
231 267
232 var list = document.getElementById("subscriptionSelector"); 268 var list = document.getElementById("subscriptionSelector");
233 var doc = new DOMParser().parseFromString(text, "application/xml"); 269 var doc = new DOMParser().parseFromString(text, "application/xml");
234 var elements = doc.documentElement.getElementsByTagName("subscription"); 270 var elements = doc.documentElement.getElementsByTagName("subscription");
235 271
236 for (var i = 0; i < elements.length; i++) 272 for (var i = 0; i < elements.length; i++)
237 { 273 {
238 var element = elements[i]; 274 var element = elements[i];
239 var option = new Option(); 275 var option = new Option();
240 option.text = element.getAttribute("title") + " (" + 276 option.text = element.getAttribute("title") + " (" +
241 element.getAttribute("specialization") + ")"; 277 element.getAttribute("specialization") + ")";
242 option._data = { 278 option._data = {
243 title: element.getAttribute("title"), 279 title: element.getAttribute("title"),
244 url: element.getAttribute("url"), 280 url: element.getAttribute("url"),
245 homepage: element.getAttribute("homepage") 281 homepage: element.getAttribute("homepage")
246 }; 282 };
247 283
248 var prefixes = element.getAttribute("prefixes"); 284 var prefix = element.getAttribute("prefixes");
249 var prefix = Utils.checkLocalePrefixMatch(prefixes);
250 if (prefix) 285 if (prefix)
251 { 286 {
287 prefix = prefix.replace(/\W/g, "_");
252 option.style.fontWeight = "bold"; 288 option.style.fontWeight = "bold";
253 option.style.backgroundColor = "#E0FFE0"; 289 option.style.backgroundColor = "#E0FFE0";
254 option.style.color = "#000000"; 290 option.style.color = "#000000";
255 if (!selectedPrefix || selectedPrefix.length < prefix.length) 291 if (!selectedPrefix || selectedPrefix.length < prefix.length)
256 { 292 {
257 selectedIndex = i; 293 selectedIndex = i;
258 selectedPrefix = prefix; 294 selectedPrefix = prefix;
259 matchCount = 1; 295 matchCount = 1;
260 } 296 }
261 else if (selectedPrefix && selectedPrefix.length == prefix.length) 297 else if (selectedPrefix && selectedPrefix.length == prefix.length)
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 var data = list.options[list.selectedIndex]._data; 354 var data = list.options[list.selectedIndex]._data;
319 if (data) 355 if (data)
320 $("#customSubscriptionContainer").hide(); 356 $("#customSubscriptionContainer").hide();
321 else 357 else
322 { 358 {
323 $("#customSubscriptionContainer").show(); 359 $("#customSubscriptionContainer").show();
324 $("#customSubscriptionTitle").focus(); 360 $("#customSubscriptionTitle").focus();
325 } 361 }
326 } 362 }
327 363
328 function addSubscription() 364 function addSubscriptionClicked()
329 { 365 {
330 var list = document.getElementById("subscriptionSelector"); 366 var list = document.getElementById("subscriptionSelector");
331 var data = list.options[list.selectedIndex]._data; 367 var data = list.options[list.selectedIndex]._data;
332 if (data) 368 if (data)
333 doAddSubscription(data.url, data.title, data.homepage); 369 addSubscription(data.url, data.title, data.homepage);
334 else 370 else
335 { 371 {
336 var url = document.getElementById("customSubscriptionLocation").value.trim() ; 372 var url = document.getElementById("customSubscriptionLocation").value.trim() ;
337 if (!/^https?:/i.test(url)) 373 if (!/^https?:/i.test(url))
338 { 374 {
339 alert(i18n.getMessage("global_subscription_invalid_location")); 375 alert(i18n.getMessage("global_subscription_invalid_location"));
340 $("#customSubscriptionLocation").focus(); 376 $("#customSubscriptionLocation").focus();
341 return; 377 return;
342 } 378 }
343 379
344 var title = document.getElementById("customSubscriptionTitle").value.trim(); 380 var title = document.getElementById("customSubscriptionTitle").value.trim();
345 if (!title) 381 if (!title)
346 title = url; 382 title = url;
347 383
348 doAddSubscription(url, title, null); 384 addSubscription(url, title, null);
349 } 385 }
350 386
351 $("#addSubscriptionContainer").hide(); 387 $("#addSubscriptionContainer").hide();
352 $("#customSubscriptionContainer").hide(); 388 $("#customSubscriptionContainer").hide();
353 $("#addSubscriptionButton").show(); 389 $("#addSubscriptionButton").show();
354 } 390 }
355 391
356 function doAddSubscription(url, title, homepage) 392 function toggleAcceptableAds()
357 { 393 {
358 if (url in FilterStorage.knownSubscriptions) 394 getPref("subscriptions_exceptionsurl", function (url)
359 return;
360
361 var subscription = Subscription.fromURL(url);
362 if (!subscription)
363 return;
364
365 subscription.title = title;
366 if (homepage)
367 subscription.homepage = homepage;
368 FilterStorage.addSubscription(subscription);
369
370 if (subscription instanceof DownloadableSubscription && !subscription.lastDown load)
371 Synchronizer.execute(subscription);
372 }
373
374 function allowAcceptableAds(event)
375 {
376 var subscription = Subscription.fromURL(Prefs.subscriptions_exceptionsurl);
377 if (!subscription)
378 return;
379
380 subscription.disabled = false;
381 subscription.title = "Allow non-intrusive advertising";
382 if ($("#acceptableAds").prop("checked"))
383 { 395 {
384 FilterStorage.addSubscription(subscription); 396 toggleSubscription(url, true);
385 if (subscription instanceof DownloadableSubscription && !subscription.lastDo wnload) 397 });
386 Synchronizer.execute(subscription);
387 }
388 else
389 FilterStorage.removeSubscription(subscription);
390 } 398 }
391 399
392 function findSubscriptionElement(subscription) 400 function findSubscriptionElement(subscription)
393 { 401 {
394 var children = document.getElementById("filterLists").childNodes; 402 var children = document.getElementById("filterLists").childNodes;
395 for (var i = 0; i < children.length; i++) 403 for (var i = 0; i < children.length; i++)
396 if (children[i]._subscription == subscription) 404 if (children[i]._subscription.url == subscription.url)
397 return children[i]; 405 return children[i];
398 return null; 406 return null;
399 } 407 }
400 408
401 function updateSubscriptionInfo(element) 409 function updateSubscriptionInfo(element, subscription)
402 { 410 {
403 var subscription = element._subscription; 411 if (subscription)
412 element._subscription = subscription;
413 else
414 subscription = element._subscription;
404 415
405 var title = element.getElementsByClassName("subscriptionTitle")[0]; 416 var title = element.getElementsByClassName("subscriptionTitle")[0];
406 title.textContent = subscription.title; 417 title.textContent = subscription.title;
407 title.setAttribute("title", subscription.url); 418 title.setAttribute("title", subscription.url);
408 if (subscription.homepage) 419 if (subscription.homepage)
409 title.href = subscription.homepage; 420 title.href = subscription.homepage;
410 else 421 else
411 title.href = subscription.url; 422 title.href = subscription.url;
412 423
413 var enabled = element.getElementsByClassName("subscriptionEnabled")[0]; 424 var enabled = element.getElementsByClassName("subscriptionEnabled")[0];
414 enabled.checked = !subscription.disabled; 425 enabled.checked = !subscription.disabled;
415 426
416 var lastUpdate = element.getElementsByClassName("subscriptionUpdate")[0]; 427 var lastUpdate = element.getElementsByClassName("subscriptionUpdate")[0];
417 lastUpdate.classList.remove("error"); 428 lastUpdate.classList.remove("error");
418 if (Synchronizer.isExecuting(subscription.url)) 429
419 lastUpdate.textContent = i18n.getMessage("filters_subscription_lastDownload_ inProgress"); 430 if (subscription.downloadStatus &&
420 else if (subscription.downloadStatus && subscription.downloadStatus != "synchr onize_ok") 431 subscription.downloadStatus != "synchronize_ok")
421 { 432 {
422 var map = 433 var map =
423 { 434 {
424 "synchronize_invalid_url": "filters_subscription_lastDownload_invalidURL", 435 "synchronize_invalid_url": "filters_subscription_lastDownload_invalidURL" ,
425 "synchronize_connection_error": "filters_subscription_lastDownload_connect ionError", 436 "synchronize_connection_error": "filters_subscription_lastDownload_connec tionError",
426 "synchronize_invalid_data": "filters_subscription_lastDownload_invalidData ", 437 "synchronize_invalid_data": "filters_subscription_lastDownload_invalidDat a",
427 "synchronize_checksum_mismatch": "filters_subscription_lastDownload_checks umMismatch" 438 "synchronize_checksum_mismatch": "filters_subscription_lastDownload_check sumMismatch"
428 }; 439 };
429 if (subscription.downloadStatus in map) 440 if (subscription.downloadStatus in map)
430 lastUpdate.textContent = i18n.getMessage(map[subscription.downloadStatus]) ; 441 lastUpdate.textContent = i18n.getMessage(map[subscription.downloadStatus] );
431 else 442 else
432 lastUpdate.textContent = subscription.downloadStatus; 443 lastUpdate.textContent = subscription.downloadStatus;
433 lastUpdate.classList.add("error"); 444 lastUpdate.classList.add("error");
434 } 445 }
435 else if (subscription.lastDownload > 0) 446 else if (subscription.lastDownload > 0)
436 { 447 {
437 var timeDate = i18n_timeDateStrings(subscription.lastDownload * 1000); 448 var timeDate = i18n_timeDateStrings(subscription.lastDownload * 1000);
438 var messageID = (timeDate[1] ? "last_updated_at" : "last_updated_at_today"); 449 var messageID = (timeDate[1] ? "last_updated_at" : "last_updated_at_today");
439 lastUpdate.textContent = i18n.getMessage(messageID, timeDate); 450 lastUpdate.textContent = i18n.getMessage(messageID, timeDate);
440 } 451 }
441 } 452 }
442 453
443 function onSubscriptionChange(subscription) 454 function onSubscriptionMessage(action, subscription)
444 { 455 {
445 var element = findSubscriptionElement(subscription); 456 switch (action)
446 if (element)
447 updateSubscriptionInfo(element);
448 }
449
450 function onSubscriptionAdded(subscription)
451 {
452 if (subscription instanceof SpecialSubscription)
453 { 457 {
454 for (var i = 0; i < subscription.filters.length; i++) 458 case "title":
455 onFilterAdded(subscription.filters[i]); 459 case "disabled":
456 } 460 case "homepage":
457 else if (subscription.url == Prefs.subscriptions_exceptionsurl) 461 case "lastDownload":
458 $("#acceptableAds").prop("checked", true); 462 case "downloadStatus":
459 else if (!findSubscriptionElement(subscription)) 463 var element = findSubscriptionElement(subscription);
460 addSubscriptionEntry(subscription); 464 if (element)
461 } 465 updateSubscriptionInfo(element, subscription);
462 466 break;
463 function onSubscriptionRemoved(subscription) 467 case "added":
464 { 468 getPref("subscriptions_exceptionsurl", function (acceptableAdsUrl)
465 if (subscription instanceof SpecialSubscription) 469 {
466 { 470 if (subscription.url == acceptableAdsUrl)
467 for (var i = 0; i < subscription.filters.length; i++) 471 $("#acceptableAds").prop("checked", true);
468 onFilterRemoved(subscription.filters[i]); 472 else if (!findSubscriptionElement(subscription))
469 } 473 addSubscriptionEntry(subscription);
470 else if (subscription.url == Prefs.subscriptions_exceptionsurl) 474 });
471 $("#acceptableAds").prop("checked", false); 475 break;
472 else 476 case "removed":
473 { 477 getPref("subscriptions_exceptionsurl", function (acceptableAdsUrl)
474 var element = findSubscriptionElement(subscription); 478 {
475 if (element) 479 if (subscription.url == acceptableAdsUrl)
476 element.parentNode.removeChild(element); 480 {
481 $("#acceptableAds").prop("checked", false);
482 }
483 else
484 {
485 var element = findSubscriptionElement(subscription);
486 if (element)
487 element.parentNode.removeChild(element);
488 }
489 });
490 break;
477 } 491 }
478 } 492 }
479 493
480 function onFilterAdded(filter) 494 function onPrefMessage(key, value)
481 { 495 {
482 if (filter instanceof WhitelistFilter && 496 if (key == "notifications_showui")
Sebastian Noack 2016/04/03 15:05:14 Nit: There will be soon a third case for safari_co
kzar 2016/04/03 15:50:19 Done.
483 /^@@\|\|([^\/:]+)\^\$document$/.test(filter.text)) 497 {
484 appendToListBox("excludedDomainsBox", RegExp.$1); 498 document.getElementById("shouldShowNotificationsContainer").hidden = !value;
485 else 499 return;
486 appendToListBox("userFiltersBox", filter.text); 500 }
501 else if (key == "notifications_ignoredcategories")
502 {
503 key = "shouldShowNotifications";
504 value = value.indexOf("*") == -1;
505 }
506
507 var checkbox = document.getElementById(key);
508 if (checkbox)
509 checkbox.checked = value;
487 } 510 }
488 511
489 function onFilterRemoved(filter) 512 function onFilterMessage(action, filter)
490 { 513 {
491 if (filter instanceof WhitelistFilter && 514 switch (action)
492 /^@@\|\|([^\/:]+)\^\$document$/.test(filter.text)) 515 {
493 removeFromListBox("excludedDomainsBox", RegExp.$1); 516 case "loaded":
494 else 517 reloadFilters();
495 removeFromListBox("userFiltersBox", filter.text); 518 break;
519 case "added":
520 if (whitelistedDomainRegexp.test(filter.text))
521 appendToListBox("excludedDomainsBox", RegExp.$1);
522 else
523 appendToListBox("userFiltersBox", filter.text);
524 break;
525 case "removed":
526 if (whitelistedDomainRegexp.test(filter.text))
527 removeFromListBox("excludedDomainsBox", RegExp.$1);
528 else
529 removeFromListBox("userFiltersBox", filter.text);
530 break;
531 }
496 } 532 }
497 533
498 // Populates a list box with a number of entries 534 function clearListBox(id)
499 function populateList(id, entries)
500 { 535 {
501 var list = document.getElementById(id); 536 var list = document.getElementById(id);
502 while (list.lastChild) 537 while (list.lastChild)
503 list.removeChild(list.lastChild); 538 list.removeChild(list.lastChild);
504
505 entries.sort();
506 for (var i = 0; i < entries.length; i++)
507 {
508 var option = new Option();
509 option.text = entries[i];
510 option.value = entries[i];
511 list.appendChild(option);
512 }
513 } 539 }
514 540
515 // Add a filter string to the list box. 541 // Add a filter string to the list box.
516 function appendToListBox(boxId, text) 542 function appendToListBox(boxId, text)
517 { 543 {
518 var elt = new Option(); /* Note: document.createElement("option") is unreliab le in Opera */ 544 // Note: document.createElement("option") is unreliable in Opera
545 var elt = new Option();
519 elt.text = text; 546 elt.text = text;
520 elt.value = text; 547 elt.value = text;
521 document.getElementById(boxId).appendChild(elt); 548 document.getElementById(boxId).appendChild(elt);
522 } 549 }
523 550
524 // Remove a filter string from a list box. 551 // Remove a filter string from a list box.
525 function removeFromListBox(boxId, text) 552 function removeFromListBox(boxId, text)
526 { 553 {
527 var list = document.getElementById(boxId); 554 var list = document.getElementById(boxId);
528 for (var i = 0; i < list.length; i++) 555 for (var i = 0; i < list.length; i++)
529 if (list.options[i].value == text) 556 if (list.options[i].value == text)
530 list.remove(i--); 557 list.remove(i--);
531 } 558 }
532 559
533 function addWhitelistDomain(event) 560 function addWhitelistDomain(event)
534 { 561 {
535 event.preventDefault(); 562 event.preventDefault();
536 563
537 var domain = document.getElementById("newWhitelistDomain").value.replace(/\s/g , ""); 564 var domain = document.getElementById("newWhitelistDomain").value.replace(/\s/g , "");
538 document.getElementById("newWhitelistDomain").value = ""; 565 document.getElementById("newWhitelistDomain").value = "";
539 if (!domain) 566 if (!domain)
540 return; 567 return;
541 568
542 var filterText = "@@||" + domain + "^$document"; 569 var filterText = "@@||" + domain + "^$document";
543 FilterStorage.addFilter(Filter.fromText(filterText)); 570 addFilter(filterText);
544 } 571 }
545 572
546 // Adds filter text that user typed to the selection box 573 // Adds filter text that user typed to the selection box
547 function addTypedFilter(event) 574 function addTypedFilter(event)
548 { 575 {
549 event.preventDefault(); 576 event.preventDefault();
550 577
551 var element = document.getElementById("newFilter"); 578 var element = document.getElementById("newFilter");
552 var result = parseFilter(element.value); 579 addFilter(element.value, function (errors)
553
554 if (result.error)
555 { 580 {
556 alert(result.error); 581 if (errors.length > 0)
557 return; 582 alert(errors.join("\n"));
558 } 583 else
559 584 element.value = "";
560 if (result.filter) 585 });
561 FilterStorage.addFilter(result.filter);
562
563 element.value = "";
564 } 586 }
565 587
566 // Removes currently selected whitelisted domains 588 // Removes currently selected whitelisted domains
567 function removeSelectedExcludedDomain(event) 589 function removeSelectedExcludedDomain(event)
568 { 590 {
569 event.preventDefault(); 591 event.preventDefault();
570 var excludedDomainsBox = document.getElementById("excludedDomainsBox"); 592 var excludedDomainsBox = document.getElementById("excludedDomainsBox");
571 var remove = []; 593 var remove = [];
572 for (var i = 0; i < excludedDomainsBox.length; i++) 594 for (var i = 0; i < excludedDomainsBox.length; i++)
573 if (excludedDomainsBox.options[i].selected) 595 if (excludedDomainsBox.options[i].selected)
574 remove.push(excludedDomainsBox.options[i].value); 596 remove.push(excludedDomainsBox.options[i].value);
575 if (!remove.length) 597 if (!remove.length)
576 return; 598 return;
577 599
578 for (var i = 0; i < remove.length; i++) 600 for (var i = 0; i < remove.length; i++)
579 FilterStorage.removeFilter(Filter.fromText("@@||" + remove[i] + "^$document" )); 601 removeFilter("@@||" + remove[i] + "^$document");
580 } 602 }
581 603
582 // Removes all currently selected filters 604 // Removes all currently selected filters
583 function removeSelectedFilters(event) 605 function removeSelectedFilters(event)
584 { 606 {
585 event.preventDefault(); 607 event.preventDefault();
586 var userFiltersBox = document.getElementById("userFiltersBox"); 608 var userFiltersBox = document.getElementById("userFiltersBox");
587 var remove = []; 609 var remove = [];
588 for (var i = 0; i < userFiltersBox.length; i++) 610 for (var i = 0; i < userFiltersBox.length; i++)
589 if (userFiltersBox.options[i].selected) 611 if (userFiltersBox.options[i].selected)
590 remove.push(userFiltersBox.options[i].value); 612 remove.push(userFiltersBox.options[i].value);
591 if (!remove.length) 613 if (!remove.length)
592 return; 614 return;
593 615
594 for (var i = 0; i < remove.length; i++) 616 for (var i = 0; i < remove.length; i++)
595 FilterStorage.removeFilter(Filter.fromText(remove[i])); 617 removeFilter(remove[i]);
596 } 618 }
597 619
598 // Shows raw filters box and fills it with the current user filters 620 // Shows raw filters box and fills it with the current user filters
599 function toggleFiltersInRawFormat(event) 621 function toggleFiltersInRawFormat(event)
600 { 622 {
601 event.preventDefault(); 623 event.preventDefault();
602 624
603 $("#rawFilters").toggle(); 625 $("#rawFilters").toggle();
604 if ($("#rawFilters").is(":visible")) 626 if ($("#rawFilters").is(":visible"))
605 { 627 {
606 var userFiltersBox = document.getElementById("userFiltersBox"); 628 var userFiltersBox = document.getElementById("userFiltersBox");
607 var text = ""; 629 var text = "";
608 for (var i = 0; i < userFiltersBox.length; i++) 630 for (var i = 0; i < userFiltersBox.length; i++)
609 text += userFiltersBox.options[i].value + "\n"; 631 text += userFiltersBox.options[i].value + "\n";
610 document.getElementById("rawFiltersText").value = text; 632 document.getElementById("rawFiltersText").value = text;
611 } 633 }
612 } 634 }
613 635
614 // Imports filters in the raw text box 636 // Imports filters in the raw text box
615 function importRawFiltersText() 637 function importRawFiltersText()
616 { 638 {
617 var text = document.getElementById("rawFiltersText").value; 639 var text = document.getElementById("rawFiltersText").value;
618 var result = parseFilters(text);
619 640
620 var errors = result.errors.filter(function(e) 641 importRawFilters(text, function (errors)
621 { 642 {
622 return e.type != "unexpected-filter-list-header"; 643 if (errors.length > 0)
644 alert(errors.join("\n"));
645 else
646 $("#rawFilters").hide();
623 }); 647 });
624
625 if (errors.length > 0)
626 {
627 alert(errors.join("\n"));
628 return;
629 }
630
631 var seenFilter = Object.create(null);
632 for (var i = 0; i < result.filters.length; i++)
633 {
634 var filter = result.filters[i];
635 FilterStorage.addFilter(filter);
636 seenFilter[filter.text] = null;
637 }
638
639 var remove = [];
640 for (var i = 0; i < FilterStorage.subscriptions.length; i++)
641 {
642 var subscription = FilterStorage.subscriptions[i];
643 if (!(subscription instanceof SpecialSubscription))
644 continue;
645
646 for (var j = 0; j < subscription.filters.length; j++)
647 {
648 var filter = subscription.filters[j];
649 if (filter instanceof WhitelistFilter && /^@@\|\|([^\/:]+)\^\$document$/.t est(filter.text))
650 continue;
651
652 if (!(filter.text in seenFilter))
653 remove.push(filter);
654 }
655 }
656
657 for (var i = 0; i < remove.length; i++)
658 FilterStorage.removeFilter(remove[i]);
659
660 $("#rawFilters").hide();
661 } 648 }
662 649
663 // Called when user explicitly requests filter list updates 650 // Called when user explicitly requests filter list updates
664 function updateFilterLists() 651 function updateFilterLists()
665 { 652 {
666 for (var i = 0; i < FilterStorage.subscriptions.length; i++) 653 // Without the URL parameter this will update all subscriptions
654 updateSubscription();
655
656 // Display a message for any subscriptions that are currently being downloaded
657 getSubscriptions(true, false, function(subscriptions)
667 { 658 {
668 var subscription = FilterStorage.subscriptions[i]; 659 var inProgressMessage = i18n.getMessage(
669 if (subscription instanceof DownloadableSubscription) 660 "filters_subscription_lastDownload_inProgress"
670 Synchronizer.execute(subscription, true, true); 661 );
671 } 662
663 for (var i = 0; i < subscriptions.length; i += 1)
664 {
665 var element = findSubscriptionElement(subscriptions[i]);
666 if (element)
667 {
668 var label = element.getElementsByClassName("subscriptionUpdate")[0];
669 isSubscriptionDownloading(subscriptions[i].url, function (isDownloading)
Sebastian Noack 2016/04/03 15:11:41 Nit: Redundant space after function statement.
kzar 2016/04/03 15:50:19 Done.
670 {
671 if (isDownloading)
672 label.textContent = inProgressMessage;
Sebastian Noack 2016/04/03 15:27:40 This will always be the label of the last subscrip
kzar 2016/04/03 16:11:33 Oops, you're right. Done.
673 });
674 }
675 }
676 });
672 } 677 }
673 678
674 // Adds a subscription entry to the UI. 679 // Adds a subscription entry to the UI.
675 function addSubscriptionEntry(subscription) 680 function addSubscriptionEntry(subscription)
676 { 681 {
677 var template = document.getElementById("subscriptionTemplate"); 682 var template = document.getElementById("subscriptionTemplate");
678 var element = template.cloneNode(true); 683 var element = template.cloneNode(true);
679 element.removeAttribute("id"); 684 element.removeAttribute("id");
680 element._subscription = subscription; 685 element._subscription = subscription;
681 686
682 var removeButton = element.getElementsByClassName("subscriptionRemoveButton")[ 0]; 687 var removeButton = element.getElementsByClassName("subscriptionRemoveButton")[ 0];
683 removeButton.setAttribute("title", removeButton.textContent); 688 removeButton.setAttribute("title", removeButton.textContent);
684 removeButton.textContent = "\xD7"; 689 removeButton.textContent = "\xD7";
685 removeButton.addEventListener("click", function() 690 removeButton.addEventListener("click", function()
686 { 691 {
687 if (!confirm(i18n.getMessage("global_remove_subscription_warning"))) 692 if (!confirm(i18n.getMessage("global_remove_subscription_warning")))
688 return; 693 return;
689 694
690 FilterStorage.removeSubscription(subscription); 695 removeSubscription(subscription.url);
691 }, false); 696 }, false);
692 if (Prefs.additional_subscriptions.indexOf(subscription.url) != -1) 697
693 removeButton.style.visibility = "hidden"; 698 getPref("additional_subscriptions", function (additionalSubscriptions)
699 {
700 if (additionalSubscriptions.indexOf(subscription.url) != -1)
701 removeButton.style.visibility = "hidden";
702 });
694 703
695 var enabled = element.getElementsByClassName("subscriptionEnabled")[0]; 704 var enabled = element.getElementsByClassName("subscriptionEnabled")[0];
696 enabled.addEventListener("click", function() 705 enabled.addEventListener("click", function()
697 { 706 {
698 if (subscription.disabled == !enabled.checked) 707 subscription.disabled = !subscription.disabled;
699 return; 708 toggleSubscription(subscription.url, true);
700
701 subscription.disabled = !enabled.checked;
702 }, false); 709 }, false);
703 710
704 updateSubscriptionInfo(element); 711 updateSubscriptionInfo(element);
705 712
706 document.getElementById("filterLists").appendChild(element); 713 document.getElementById("filterLists").appendChild(element);
707 } 714 }
708 715
709 function setLinks(id) 716 function setLinks(id)
710 { 717 {
711 var element = document.getElementById(id); 718 var element = document.getElementById(id);
712 if (!element) 719 if (!element)
713 return; 720 return;
714 721
715 var links = element.getElementsByTagName("a"); 722 var links = element.getElementsByTagName("a");
716 for (var i = 0; i < links.length; i++) 723 for (var i = 0; i < links.length; i++)
717 { 724 {
718 if (typeof arguments[i + 1] == "string") 725 if (typeof arguments[i + 1] == "string")
719 { 726 {
720 links[i].href = arguments[i + 1]; 727 links[i].href = arguments[i + 1];
721 links[i].setAttribute("target", "_blank"); 728 links[i].setAttribute("target", "_blank");
722 } 729 }
723 else if (typeof arguments[i + 1] == "function") 730 else if (typeof arguments[i + 1] == "function")
724 { 731 {
725 links[i].href = "javascript:void(0);"; 732 links[i].href = "javascript:void(0);";
726 links[i].addEventListener("click", arguments[i + 1], false); 733 links[i].addEventListener("click", arguments[i + 1], false);
727 } 734 }
728 } 735 }
729 } 736 }
737
738 ext.onMessage.addListener(function(message)
739 {
740 switch (message.type)
741 {
742 case "app.respond":
743 if (message.action == "addSubscription")
744 {
745 var subscription = message.args[0];
746 startSubscriptionSelection(subscription.title, subscription.url);
747 }
748 break;
749 case "focus-section":
Sebastian Noack 2016/04/03 15:05:14 This should be handled the same way app.respond[ad
kzar 2016/04/03 15:50:19 Done. (Not tested yet as leaving for airport short
750 var tabs = document.getElementsByClassName("ui-tabs-panel");
751 for (var i = 0; i < tabs.length; i++)
752 {
753 var found = tabs[i].querySelector(
754 "[data-section='" + message.section + "']"
755 );
756 if (!found)
757 continue;
758
759 var previous = document.getElementsByClassName("focused");
760 if (previous.length > 0)
761 previous[0].classList.remove("focused");
762
763 var tab = $("[href='#" + tabs[i].id + "']");
764 $("#tabs").tabs("select", tab.parent().index());
765 found.classList.add("focused");
766 }
767 break;
768 case "filters.respond":
769 onFilterMessage(message.action, message.args[0]);
770 break;
771 case "prefs.respond":
772 onPrefMessage(message.action, message.args[0]);
773 break;
774 case "subscriptions.respond":
775 onSubscriptionMessage(message.action, message.args[0]);
776 break;
777 }
778 });
OLDNEW
« no previous file with comments | « dependencies ('k') | safari/ext/background.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld