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

Side by Side Diff: new-options.js

Issue 29375899: Issue 4871 - Start using ESLint for adblockplusui (Closed)
Patch Set: Rebased. Created March 7, 2017, 12:55 p.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 | « messageResponder.js ('k') | no next file » | 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/>.
16 */ 16 */
17 17
18 /* globals checkShareResource, getDocLink, i18nFormatDateTime, openSharePopup,
Thomas Greiner 2017/03/07 13:33:02 Detail: Isn't this violating the "max-len" rule?
kzar 2017/03/08 10:29:32 No, just under.
19 E */
20
18 "use strict"; 21 "use strict";
19 22
20 (function()
21 { 23 {
22 var subscriptionsMap = Object.create(null); 24 let subscriptionsMap = Object.create(null);
23 var filtersMap = Object.create(null); 25 let filtersMap = Object.create(null);
24 var collections = Object.create(null); 26 let collections = Object.create(null);
25 var acceptableAdsUrl = null; 27 let acceptableAdsUrl = null;
26 var getMessage = ext.i18n.getMessage; 28 let {getMessage} = ext.i18n;
27 var filterErrors = 29 let filterErrors = new Map([
28 { 30 ["synchronize_invalid_url",
29 "synchronize_invalid_url": "options_filterList_lastDownload_invalidURL", 31 "options_filterList_lastDownload_invalidURL"],
30 "synchronize_connection_error": "options_filterList_lastDownload_connectionE rror", 32 ["synchronize_connection_error",
31 "synchronize_invalid_data": "options_filterList_lastDownload_invalidData", 33 "options_filterList_lastDownload_connectionError"],
32 "synchronize_checksum_mismatch": "options_filterList_lastDownload_checksumMi smatch" 34 ["synchronize_invalid_data",
33 }; 35 "options_filterList_lastDownload_invalidData"],
36 ["synchronize_checksum_mismatch",
37 "options_filterList_lastDownload_checksumMismatch"]
38 ]);
34 39
35 function Collection(details) 40 function Collection(details)
36 { 41 {
37 this.details = details; 42 this.details = details;
38 this.items = []; 43 this.items = [];
39 } 44 }
40 45
41 Collection.prototype._setEmpty = function(table, text) 46 Collection.prototype._setEmpty = function(table, text)
42 { 47 {
43 var placeholder = table.querySelector(".empty-placeholder"); 48 let placeholder = table.querySelector(".empty-placeholder");
44 if (text && !placeholder) 49 if (text && !placeholder)
45 { 50 {
46 placeholder = document.createElement("li"); 51 placeholder = document.createElement("li");
47 placeholder.className = "empty-placeholder"; 52 placeholder.className = "empty-placeholder";
48 placeholder.textContent = getMessage(text); 53 placeholder.textContent = getMessage(text);
49 table.appendChild(placeholder); 54 table.appendChild(placeholder);
50 } 55 }
51 else if (placeholder) 56 else if (placeholder)
52 table.removeChild(placeholder); 57 table.removeChild(placeholder);
53 }; 58 };
54 59
55 Collection.prototype._createElementQuery = function(item) 60 Collection.prototype._createElementQuery = function(item)
56 { 61 {
57 var access = (item.url || item.text).replace(/'/g, "\\'"); 62 let access = (item.url || item.text).replace(/'/g, "\\'");
58 return function(container) 63 return function(container)
59 { 64 {
60 return container.querySelector("[data-access='" + access + "']"); 65 return container.querySelector("[data-access='" + access + "']");
61 }; 66 };
62 }; 67 };
63 68
64 Collection.prototype._getItemTitle = function(item, i) 69 Collection.prototype._getItemTitle = function(item, i)
65 { 70 {
66 if (item.url == acceptableAdsUrl) 71 if (item.url == acceptableAdsUrl)
67 return getMessage("options_acceptableAds_description"); 72 return getMessage("options_acceptableAds_description");
68 if (this.details[i].useOriginalTitle && item.originalTitle) 73 if (this.details[i].useOriginalTitle && item.originalTitle)
69 return item.originalTitle; 74 return item.originalTitle;
70 return item.title || item.url || item.text; 75 return item.title || item.url || item.text;
71 }; 76 };
72 77
73 Collection.prototype.addItem = function(item) 78 Collection.prototype.addItem = function(item)
74 { 79 {
75 if (this.items.indexOf(item) >= 0) 80 if (this.items.indexOf(item) >= 0)
76 return; 81 return;
77 82
78 this.items.push(item); 83 this.items.push(item);
79 this.items.sort(function(a, b) 84 this.items.sort((a, b) =>
80 { 85 {
81 // Make sure that Acceptable Ads is always last, since it cannot be 86 // Make sure that Acceptable Ads is always last, since it cannot be
82 // disabled, but only be removed. That way it's grouped together with 87 // disabled, but only be removed. That way it's grouped together with
83 // the "Own filter list" which cannot be disabled either at the bottom 88 // the "Own filter list" which cannot be disabled either at the bottom
84 // of the filter lists in the Advanced tab. 89 // of the filter lists in the Advanced tab.
85 if (a.url == acceptableAdsUrl) 90 if (a.url == acceptableAdsUrl)
86 return 1; 91 return 1;
87 if (b.url == acceptableAdsUrl) 92 if (b.url == acceptableAdsUrl)
88 return -1; 93 return -1;
89 94
90 var aTitle = this._getItemTitle(a, 0).toLowerCase(); 95 let aTitle = this._getItemTitle(a, 0).toLowerCase();
91 var bTitle = this._getItemTitle(b, 0).toLowerCase(); 96 let bTitle = this._getItemTitle(b, 0).toLowerCase();
92 return aTitle.localeCompare(bTitle); 97 return aTitle.localeCompare(bTitle);
93 }.bind(this)); 98 });
94 99
95 for (var j = 0; j < this.details.length; j++) 100 for (let j = 0; j < this.details.length; j++)
96 { 101 {
97 var table = E(this.details[j].id); 102 let table = E(this.details[j].id);
98 var template = table.querySelector("template"); 103 let template = table.querySelector("template");
99 var listItem = document.createElement("li"); 104 let listItem = document.createElement("li");
100 listItem.appendChild(document.importNode(template.content, true)); 105 listItem.appendChild(document.importNode(template.content, true));
101 listItem.setAttribute("aria-label", this._getItemTitle(item, j)); 106 listItem.setAttribute("aria-label", this._getItemTitle(item, j));
102 listItem.setAttribute("data-access", item.url || item.text); 107 listItem.setAttribute("data-access", item.url || item.text);
103 listItem.setAttribute("role", "section"); 108 listItem.setAttribute("role", "section");
104 109
105 var label = listItem.querySelector(".display"); 110 let label = listItem.querySelector(".display");
106 if (item.recommended && label.hasAttribute("data-tooltip")) 111 if (item.recommended && label.hasAttribute("data-tooltip"))
107 { 112 {
108 var tooltipId = label.getAttribute("data-tooltip"); 113 let tooltipId = label.getAttribute("data-tooltip");
109 tooltipId = tooltipId.replace("%value%", item.recommended); 114 tooltipId = tooltipId.replace("%value%", item.recommended);
110 label.setAttribute("data-tooltip", tooltipId); 115 label.setAttribute("data-tooltip", tooltipId);
111 } 116 }
112 117
113 var controls = listItem.querySelectorAll(".control"); 118 for (let control of listItem.querySelectorAll(".control"))
114 for (var k = 0; k < controls.length; k++)
115 { 119 {
116 if (controls[k].hasAttribute("title")) 120 if (control.hasAttribute("title"))
117 { 121 {
118 var titleValue = getMessage(controls[k].getAttribute("title")); 122 let titleValue = getMessage(control.getAttribute("title"));
119 controls[k].setAttribute("title", titleValue) 123 control.setAttribute("title", titleValue);
120 } 124 }
121 } 125 }
122 126
123 this._setEmpty(table, null); 127 this._setEmpty(table, null);
124 if (table.hasChildNodes()) 128 if (table.hasChildNodes())
125 { 129 {
126 table.insertBefore(listItem, 130 table.insertBefore(listItem,
127 table.childNodes[this.items.indexOf(item)]); 131 table.childNodes[this.items.indexOf(item)]);
128 } 132 }
129 else 133 else
130 table.appendChild(listItem); 134 table.appendChild(listItem);
131 this.updateItem(item); 135 this.updateItem(item);
132 } 136 }
133 return length; 137 return length;
134 }; 138 };
135 139
136 Collection.prototype.removeItem = function(item) 140 Collection.prototype.removeItem = function(item)
137 { 141 {
138 var index = this.items.indexOf(item); 142 let index = this.items.indexOf(item);
139 if (index == -1) 143 if (index == -1)
140 return; 144 return;
141 145
142 this.items.splice(index, 1); 146 this.items.splice(index, 1);
143 var getListElement = this._createElementQuery(item); 147 let getListElement = this._createElementQuery(item);
144 for (var i = 0; i < this.details.length; i++) 148 for (let detail of this.details)
145 { 149 {
146 var table = E(this.details[i].id); 150 let table = E(detail.id);
147 var element = getListElement(table); 151 let element = getListElement(table);
148 152
149 // Element gets removed so make sure to handle focus appropriately 153 // Element gets removed so make sure to handle focus appropriately
150 var control = element.querySelector(".control"); 154 let control = element.querySelector(".control");
151 if (control && control == document.activeElement) 155 if (control && control == document.activeElement)
152 { 156 {
153 if (!focusNextElement(element.parentElement, control)) 157 if (!focusNextElement(element.parentElement, control))
154 { 158 {
155 // Fall back to next focusable element within same tab or dialog 159 // Fall back to next focusable element within same tab or dialog
156 var focusableElement = element.parentElement; 160 let focusableElement = element.parentElement;
157 while (focusableElement) 161 while (focusableElement)
158 { 162 {
159 if (focusableElement.classList.contains("tab-content") 163 if (focusableElement.classList.contains("tab-content") ||
160 || focusableElement.classList.contains("dialog-content")) 164 focusableElement.classList.contains("dialog-content"))
161 break; 165 break;
162 166
163 focusableElement = focusableElement.parentElement; 167 focusableElement = focusableElement.parentElement;
164 } 168 }
165 focusNextElement(focusableElement || document, control); 169 focusNextElement(focusableElement || document, control);
166 } 170 }
167 } 171 }
168 172
169 element.parentElement.removeChild(element); 173 element.parentElement.removeChild(element);
170 if (this.items.length == 0) 174 if (this.items.length == 0)
171 this._setEmpty(table, this.details[i].emptyText); 175 this._setEmpty(table, detail.emptyText);
172 } 176 }
173 }; 177 };
174 178
175 Collection.prototype.updateItem = function(item) 179 Collection.prototype.updateItem = function(item)
176 { 180 {
177 var access = (item.url || item.text).replace(/'/g, "\\'"); 181 let access = (item.url || item.text).replace(/'/g, "\\'");
178 for (var i = 0; i < this.details.length; i++) 182 for (let i = 0; i < this.details.length; i++)
179 { 183 {
180 var table = E(this.details[i].id); 184 let table = E(this.details[i].id);
181 var element = table.querySelector("[data-access='" + access + "']"); 185 let element = table.querySelector("[data-access='" + access + "']");
182 if (!element) 186 if (!element)
183 continue; 187 continue;
184 188
185 var title = this._getItemTitle(item, i); 189 let title = this._getItemTitle(item, i);
186 element.querySelector(".display").textContent = title; 190 element.querySelector(".display").textContent = title;
187 element.setAttribute("aria-label", title); 191 element.setAttribute("aria-label", title);
188 if (this.details[i].searchable) 192 if (this.details[i].searchable)
189 element.setAttribute("data-search", title.toLowerCase()); 193 element.setAttribute("data-search", title.toLowerCase());
190 var control = element.querySelector(".control[role='checkbox']"); 194 let control = element.querySelector(".control[role='checkbox']");
191 if (control) 195 if (control)
192 { 196 {
193 control.setAttribute("aria-checked", item.disabled == false); 197 control.setAttribute("aria-checked", item.disabled == false);
194 if (item.url == acceptableAdsUrl && this == collections.filterLists) 198 if (item.url == acceptableAdsUrl && this == collections.filterLists)
195 control.setAttribute("disabled", true); 199 control.setAttribute("disabled", true);
196 } 200 }
197 201
198 var dateElement = element.querySelector(".date"); 202 let dateElement = element.querySelector(".date");
199 var timeElement = element.querySelector(".time"); 203 let timeElement = element.querySelector(".time");
200 if (dateElement && timeElement) 204 if (dateElement && timeElement)
201 { 205 {
202 var message = element.querySelector(".message"); 206 let message = element.querySelector(".message");
203 if (item.isDownloading) 207 if (item.isDownloading)
204 { 208 {
205 var text = getMessage("options_filterList_lastDownload_inProgress"); 209 let text = getMessage("options_filterList_lastDownload_inProgress");
206 message.textContent = text; 210 message.textContent = text;
207 element.classList.add("show-message"); 211 element.classList.add("show-message");
208 } 212 }
209 else if (item.downloadStatus != "synchronize_ok") 213 else if (item.downloadStatus != "synchronize_ok")
210 { 214 {
211 var error = filterErrors[item.downloadStatus]; 215 let error = filterErrors.get(item.downloadStatus);
212 if (error) 216 if (error)
213 message.textContent = getMessage(error); 217 message.textContent = getMessage(error);
214 else 218 else
215 message.textContent = item.downloadStatus; 219 message.textContent = item.downloadStatus;
216 element.classList.add("show-message"); 220 element.classList.add("show-message");
217 } 221 }
218 else if (item.lastDownload > 0) 222 else if (item.lastDownload > 0)
219 { 223 {
220 var dateTime = i18n_formatDateTime(item.lastDownload * 1000); 224 let dateTime = i18nFormatDateTime(item.lastDownload * 1000);
221 dateElement.textContent = dateTime[0]; 225 dateElement.textContent = dateTime[0];
222 timeElement.textContent = dateTime[1]; 226 timeElement.textContent = dateTime[1];
223 element.classList.remove("show-message"); 227 element.classList.remove("show-message");
224 } 228 }
225 } 229 }
226 230
227 var websiteElement = element.querySelector(".context-menu .website"); 231 let websiteElement = element.querySelector(".context-menu .website");
228 if (websiteElement) 232 if (websiteElement)
229 { 233 {
230 if (item.homepage) 234 if (item.homepage)
231 websiteElement.setAttribute("href", item.homepage); 235 websiteElement.setAttribute("href", item.homepage);
232 else 236 else
233 websiteElement.setAttribute("aria-hidden", true); 237 websiteElement.setAttribute("aria-hidden", true);
234 } 238 }
235 239
236 var sourceElement = element.querySelector(".context-menu .source"); 240 let sourceElement = element.querySelector(".context-menu .source");
237 if (sourceElement) 241 if (sourceElement)
238 sourceElement.setAttribute("href", item.url); 242 sourceElement.setAttribute("href", item.url);
239 } 243 }
240 }; 244 };
241 245
242 Collection.prototype.clearAll = function() 246 Collection.prototype.clearAll = function()
243 { 247 {
244 this.items = []; 248 this.items = [];
245 for (var i = 0; i < this.details.length; i++) 249 for (let detail of this.details)
246 { 250 {
247 var table = E(this.details[i].id); 251 let table = E(detail.id);
248 var element = table.firstChild; 252 let element = table.firstChild;
249 while (element) 253 while (element)
250 { 254 {
251 if (element.tagName == "LI" && !element.classList.contains("static")) 255 if (element.tagName == "LI" && !element.classList.contains("static"))
252 table.removeChild(element); 256 table.removeChild(element);
253 element = element.nextElementSibling; 257 element = element.nextElementSibling;
254 } 258 }
255 259
256 this._setEmpty(table, this.details[i].emptyText); 260 this._setEmpty(table, detail.emptyText);
257 } 261 }
258 }; 262 };
259 263
260 function focusNextElement(container, currentElement) 264 function focusNextElement(container, currentElement)
261 { 265 {
262 var focusables = container.querySelectorAll("a, button, input, .control"); 266 let focusables = container.querySelectorAll("a, button, input, .control");
263 focusables = Array.prototype.slice.call(focusables); 267 focusables = Array.prototype.slice.call(focusables);
264 var index = focusables.indexOf(currentElement); 268 let index = focusables.indexOf(currentElement);
265 index += (index == focusables.length - 1) ? -1 : 1; 269 index += (index == focusables.length - 1) ? -1 : 1;
266 270
267 var nextElement = focusables[index]; 271 let nextElement = focusables[index];
268 if (!nextElement) 272 if (!nextElement)
269 return false; 273 return false;
270 274
271 nextElement.focus(); 275 nextElement.focus();
272 return true; 276 return true;
273 } 277 }
274 278
275 collections.popular = new Collection( 279 collections.popular = new Collection([
276 [
277 { 280 {
278 id: "recommend-list-table" 281 id: "recommend-list-table"
279 } 282 }
280 ]); 283 ]);
281 collections.langs = new Collection( 284 collections.langs = new Collection([
282 [
283 { 285 {
284 id: "blocking-languages-table", 286 id: "blocking-languages-table",
285 emptyText: "options_dialog_language_added_empty" 287 emptyText: "options_dialog_language_added_empty"
286 }, 288 },
287 { 289 {
288 id: "blocking-languages-dialog-table", 290 id: "blocking-languages-dialog-table",
289 emptyText: "options_dialog_language_added_empty" 291 emptyText: "options_dialog_language_added_empty"
290 } 292 }
291 ]); 293 ]);
292 collections.allLangs = new Collection( 294 collections.allLangs = new Collection([
293 [
294 { 295 {
295 id: "all-lang-table", 296 id: "all-lang-table",
296 emptyText: "options_dialog_language_other_empty", 297 emptyText: "options_dialog_language_other_empty",
297 searchable: true 298 searchable: true
298 } 299 }
299 ]); 300 ]);
300 collections.acceptableAds = new Collection( 301 collections.acceptableAds = new Collection([
301 [
302 { 302 {
303 id: "acceptableads-table" 303 id: "acceptableads-table"
304 } 304 }
305 ]); 305 ]);
306 collections.custom = new Collection( 306 collections.custom = new Collection([
307 [
308 { 307 {
309 id: "custom-list-table" 308 id: "custom-list-table"
310 } 309 }
311 ]); 310 ]);
312 collections.whitelist = new Collection( 311 collections.whitelist = new Collection([
313 [
314 { 312 {
315 id: "whitelisting-table", 313 id: "whitelisting-table",
316 emptyText: "options_whitelisted_empty" 314 emptyText: "options_whitelisted_empty"
317 } 315 }
318 ]); 316 ]);
319 collections.customFilters = new Collection( 317 collections.customFilters = new Collection([
320 [
321 { 318 {
322 id: "custom-filters-table", 319 id: "custom-filters-table",
323 emptyText: "options_customFilters_empty" 320 emptyText: "options_customFilters_empty"
324 } 321 }
325 ]); 322 ]);
326 collections.filterLists = new Collection( 323 collections.filterLists = new Collection([
327 [
328 { 324 {
329 id: "all-filter-lists-table", 325 id: "all-filter-lists-table",
330 useOriginalTitle: true 326 useOriginalTitle: true
331 } 327 }
332 ]); 328 ]);
333 329
334 function toggleShowLanguage(subscription) 330 function toggleShowLanguage(subscription)
335 { 331 {
336 if (subscription.recommended == "ads") 332 if (subscription.recommended == "ads")
337 { 333 {
338 if (subscription.disabled) 334 if (subscription.disabled)
339 { 335 {
340 collections.allLangs.addItem(subscription); 336 collections.allLangs.addItem(subscription);
341 collections.langs.removeItem(subscription); 337 collections.langs.removeItem(subscription);
342 } 338 }
343 else 339 else
344 { 340 {
345 collections.allLangs.removeItem(subscription); 341 collections.allLangs.removeItem(subscription);
346 collections.langs.addItem(subscription); 342 collections.langs.addItem(subscription);
347 } 343 }
348 } 344 }
349 } 345 }
350 346
351 function addSubscription(subscription) 347 function addSubscription(subscription)
352 { 348 {
353 var collection; 349 let collection;
354 if (subscription.recommended) 350 if (subscription.recommended)
355 { 351 {
356 if (subscription.recommended != "ads") 352 if (subscription.recommended != "ads")
357 collection = collections.popular; 353 collection = collections.popular;
358 else if (subscription.disabled == false) 354 else if (subscription.disabled == false)
359 collection = collections.langs; 355 collection = collections.langs;
360 else 356 else
361 collection = collections.allLangs; 357 collection = collections.allLangs;
362 } 358 }
363 else if (subscription.url == acceptableAdsUrl) 359 else if (subscription.url == acceptableAdsUrl)
364 collection = collections.acceptableAds; 360 collection = collections.acceptableAds;
365 else 361 else
366 collection = collections.custom; 362 collection = collections.custom;
367 363
368 collection.addItem(subscription); 364 collection.addItem(subscription);
369 subscriptionsMap[subscription.url] = subscription; 365 subscriptionsMap[subscription.url] = subscription;
370 toggleShowLanguage(subscription); 366 toggleShowLanguage(subscription);
371 updateTooltips(); 367 updateTooltips();
372 } 368 }
373 369
374 function updateSubscription(subscription) 370 function updateSubscription(subscription)
375 { 371 {
376 for (var name in collections) 372 for (let name in collections)
377 collections[name].updateItem(subscription); 373 collections[name].updateItem(subscription);
378 374
379 toggleShowLanguage(subscription); 375 toggleShowLanguage(subscription);
380 } 376 }
381 377
382 function updateFilter(filter) 378 function updateFilter(filter)
383 { 379 {
384 var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/); 380 let match = filter.text.match(/^@@\|\|([^/:]+)\^\$document$/);
385 if (match && !filtersMap[filter.text]) 381 if (match && !filtersMap[filter.text])
386 { 382 {
387 filter.title = match[1]; 383 filter.title = match[1];
388 collections.whitelist.addItem(filter); 384 collections.whitelist.addItem(filter);
389 } 385 }
390 else 386 else
391 collections.customFilters.addItem(filter); 387 collections.customFilters.addItem(filter);
392 388
393 filtersMap[filter.text] = filter; 389 filtersMap[filter.text] = filter;
394 } 390 }
395 391
396 function loadRecommendations() 392 function loadRecommendations()
397 { 393 {
398 fetch("subscriptions.xml") 394 fetch("subscriptions.xml")
399 .then(function(response) 395 .then((response) =>
400 { 396 {
401 return response.text(); 397 return response.text();
402 }) 398 })
403 .then(function(text) 399 .then((text) =>
404 { 400 {
405 var list = document.getElementById("subscriptionSelector"); 401 let doc = new DOMParser().parseFromString(text, "application/xml");
406 var doc = new DOMParser().parseFromString(text, "application/xml"); 402 let elements = doc.documentElement.getElementsByTagName("subscription");
407 var elements = doc.documentElement.getElementsByTagName("subscription"); 403 for (let element of elements)
408 for (var i = 0; i < elements.length; i++)
409 { 404 {
410 var element = elements[i]; 405 let type = element.getAttribute("type");
411 var type = element.getAttribute("type"); 406 let subscription = {
412 var subscription = {
413 disabled: true, 407 disabled: true,
414 downloadStatus: null, 408 downloadStatus: null,
415 homepage: null, 409 homepage: null,
416 originalTitle: element.getAttribute("title"), 410 originalTitle: element.getAttribute("title"),
417 recommended: type, 411 recommended: type,
418 url: element.getAttribute("url") 412 url: element.getAttribute("url")
419 }; 413 };
420 414
421 var prefix = element.getAttribute("prefixes"); 415 let prefix = element.getAttribute("prefixes");
422 if (prefix) 416 if (prefix)
423 { 417 {
424 prefix = prefix.replace(/\W/g, "_"); 418 prefix = prefix.replace(/\W/g, "_");
425 subscription.title = getMessage("options_language_" + prefix); 419 subscription.title = getMessage("options_language_" + prefix);
426 } 420 }
427 else 421 else
428 { 422 {
429 type = type.replace(/\W/g, "_"); 423 type = type.replace(/\W/g, "_");
430 subscription.title = getMessage("common_feature_" + type + "_title") ; 424 subscription.title = getMessage("common_feature_" +
425 type + "_title");
431 } 426 }
432 427
433 addSubscription(subscription); 428 addSubscription(subscription);
434 } 429 }
435 }); 430 });
436 } 431 }
437 432
438 function findParentData(element, dataName, returnElement) 433 function findParentData(element, dataName, returnElement)
439 { 434 {
440 while (element) 435 while (element)
441 { 436 {
442 if (element.hasAttribute("data-" + dataName)) 437 if (element.hasAttribute("data-" + dataName))
443 return returnElement ? element : element.getAttribute("data-" + dataName ); 438 {
439 if (returnElement)
440 return element;
441 return element.getAttribute("data-" + dataName);
442 }
444 443
445 element = element.parentElement; 444 element = element.parentElement;
446 } 445 }
447 return null; 446 return null;
448 } 447 }
449 448
450 function sendMessageHandleErrors(message, onSuccess) 449 function sendMessageHandleErrors(message, onSuccess)
451 { 450 {
452 ext.backgroundPage.sendMessage(message, function(errors) 451 ext.backgroundPage.sendMessage(message, (errors) =>
453 { 452 {
454 if (errors.length > 0) 453 if (errors.length > 0)
455 alert(errors.join("\n")); 454 alert(errors.join("\n"));
456 else if (onSuccess) 455 else if (onSuccess)
457 onSuccess(); 456 onSuccess();
458 }); 457 });
459 } 458 }
460 459
461 function openDocLink(id) 460 function openDocLink(id)
462 { 461 {
463 getDocLink(id, function(link) 462 getDocLink(id, (link) =>
464 { 463 {
465 if (id == "share-general") 464 if (id == "share-general")
466 openSharePopup(link); 465 openSharePopup(link);
467 else 466 else
468 location.href = link; 467 location.href = link;
469 }); 468 });
470 } 469 }
471 470
472 function switchTab(id) 471 function switchTab(id)
473 { 472 {
474 location.hash = id; 473 location.hash = id;
475 } 474 }
476 475
477 function onClick(e) 476 function onClick(e)
478 { 477 {
479 var context = document.querySelector(".show-context-menu"); 478 let context = document.querySelector(".show-context-menu");
480 if (context) 479 if (context)
481 context.classList.remove("show-context-menu"); 480 context.classList.remove("show-context-menu");
482 481
483 var element = e.target; 482 let element = findParentData(e.target, "action", true);
484 while (true) 483 if (!element)
484 return;
485
486 let actions = element.getAttribute("data-action").split(",");
487 for (let action of actions)
485 { 488 {
486 if (!element) 489 switch (action)
487 return;
488
489 if (element.hasAttribute("data-action"))
490 break;
491
492 element = element.parentElement;
493 }
494
495 var element = findParentData(e.target, "action", true);
496 var actions = element.getAttribute("data-action").split(",");
497 for (var i = 0; i < actions.length; i++)
498 {
499 switch (actions[i])
500 { 490 {
501 case "add-domain-exception": 491 case "add-domain-exception":
502 addWhitelistedDomain(); 492 addWhitelistedDomain();
503 break; 493 break;
504 case "add-predefined-subscription": 494 case "add-predefined-subscription": {
505 var dialog = E("dialog-content-predefined"); 495 let dialog = E("dialog-content-predefined");
506 var title = dialog.querySelector("h3").textContent; 496 let title = dialog.querySelector("h3").textContent;
507 var url = dialog.querySelector(".url").textContent; 497 let url = dialog.querySelector(".url").textContent;
508 addEnableSubscription(url, title); 498 addEnableSubscription(url, title);
509 closeDialog(); 499 closeDialog();
510 break; 500 break;
501 }
511 case "cancel-custom-filters": 502 case "cancel-custom-filters":
512 E("custom-filters").classList.remove("mode-edit"); 503 E("custom-filters").classList.remove("mode-edit");
513 break; 504 break;
514 case "cancel-domain-exception": 505 case "cancel-domain-exception":
515 E("whitelisting-textbox").value = ""; 506 E("whitelisting-textbox").value = "";
516 document.querySelector("#whitelisting .controls").classList.remove("mo de-edit"); 507 document.querySelector("#whitelisting .controls").classList.
508 remove("mode-edit");
517 break; 509 break;
518 case "close-dialog": 510 case "close-dialog":
519 closeDialog(); 511 closeDialog();
520 break; 512 break;
521 case "edit-custom-filters": 513 case "edit-custom-filters":
522 E("custom-filters").classList.add("mode-edit"); 514 E("custom-filters").classList.add("mode-edit");
523 editCustomFilters(); 515 editCustomFilters();
524 break; 516 break;
525 case "edit-domain-exception": 517 case "edit-domain-exception":
526 document.querySelector("#whitelisting .controls").classList.add("mode- edit"); 518 document.querySelector("#whitelisting .controls").classList.
519 add("mode-edit");
527 E("whitelisting-textbox").focus(); 520 E("whitelisting-textbox").focus();
528 break; 521 break;
529 case "import-subscription": 522 case "import-subscription": {
530 var url = E("blockingList-textbox").value; 523 let url = E("blockingList-textbox").value;
531 addEnableSubscription(url); 524 addEnableSubscription(url);
532 closeDialog(); 525 closeDialog();
533 break; 526 break;
534 case "open-dialog": 527 }
535 var dialog = findParentData(element, "dialog", false); 528 case "open-dialog": {
529 let dialog = findParentData(element, "dialog", false);
536 openDialog(dialog); 530 openDialog(dialog);
537 break; 531 break;
538 case "open-doclink": 532 }
539 var doclink = findParentData(element, "doclink", false); 533 case "open-doclink": {
534 let doclink = findParentData(element, "doclink", false);
540 openDocLink(doclink); 535 openDocLink(doclink);
541 break; 536 break;
537 }
542 case "save-custom-filters": 538 case "save-custom-filters":
543 sendMessageHandleErrors( 539 sendMessageHandleErrors({
544 {
545 type: "filters.importRaw", 540 type: "filters.importRaw",
546 text: E("custom-filters-raw").value, 541 text: E("custom-filters-raw").value,
547 removeExisting: true 542 removeExisting: true
548 }, 543 },
549 function() 544 () =>
550 { 545 {
551 E("custom-filters").classList.remove("mode-edit"); 546 E("custom-filters").classList.remove("mode-edit");
552 }); 547 });
553 break; 548 break;
554 case "switch-tab": 549 case "switch-tab": {
555 var tabId = findParentData(e.target, "tab", false); 550 let tabId = findParentData(e.target, "tab", false);
556 switchTab(tabId); 551 switchTab(tabId);
557 break; 552 break;
553 }
558 case "toggle-pref": 554 case "toggle-pref":
559 ext.backgroundPage.sendMessage( 555 ext.backgroundPage.sendMessage({
560 {
561 type: "prefs.toggle", 556 type: "prefs.toggle",
562 key: findParentData(element, "pref", false) 557 key: findParentData(element, "pref", false)
563 }); 558 });
564 break; 559 break;
565 case "update-all-subscriptions": 560 case "update-all-subscriptions":
566 ext.backgroundPage.sendMessage( 561 ext.backgroundPage.sendMessage({
567 {
568 type: "subscriptions.update" 562 type: "subscriptions.update"
569 }); 563 });
570 break; 564 break;
571 case "open-context-menu": 565 case "open-context-menu": {
572 var listItem = findParentData(element, "access", true); 566 let listItem = findParentData(element, "access", true);
573 if (listItem != context) 567 if (listItem != context)
574 listItem.classList.add("show-context-menu"); 568 listItem.classList.add("show-context-menu");
575 break; 569 break;
570 }
576 case "update-subscription": 571 case "update-subscription":
577 ext.backgroundPage.sendMessage( 572 ext.backgroundPage.sendMessage({
578 {
579 type: "subscriptions.update", 573 type: "subscriptions.update",
580 url: findParentData(element, "access", false) 574 url: findParentData(element, "access", false)
581 }); 575 });
582 break; 576 break;
583 case "remove-subscription": 577 case "remove-subscription":
584 ext.backgroundPage.sendMessage( 578 ext.backgroundPage.sendMessage({
585 {
586 type: "subscriptions.remove", 579 type: "subscriptions.remove",
587 url: findParentData(element, "access", false) 580 url: findParentData(element, "access", false)
588 }); 581 });
589 break; 582 break;
590 case "toggle-remove-subscription": 583 case "toggle-remove-subscription": {
591 var subscriptionUrl = findParentData(element, "access", false); 584 let subscriptionUrl = findParentData(element, "access", false);
592 if (element.getAttribute("aria-checked") == "true") 585 if (element.getAttribute("aria-checked") == "true")
593 { 586 {
594 ext.backgroundPage.sendMessage({ 587 ext.backgroundPage.sendMessage({
595 type: "subscriptions.remove", 588 type: "subscriptions.remove",
596 url: subscriptionUrl 589 url: subscriptionUrl
597 }); 590 });
598 } 591 }
599 else 592 else
600 addEnableSubscription(subscriptionUrl); 593 addEnableSubscription(subscriptionUrl);
601 break; 594 break;
595 }
602 case "toggle-disable-subscription": 596 case "toggle-disable-subscription":
603 ext.backgroundPage.sendMessage( 597 ext.backgroundPage.sendMessage({
604 {
605 type: "subscriptions.toggle", 598 type: "subscriptions.toggle",
606 keepInstalled: true, 599 keepInstalled: true,
607 url: findParentData(element, "access", false) 600 url: findParentData(element, "access", false)
608 }); 601 });
609 break; 602 break;
610 case "add-language-subscription": 603 case "add-language-subscription":
611 addEnableSubscription(findParentData(element, "access", false)); 604 addEnableSubscription(findParentData(element, "access", false));
612 break; 605 break;
613 case "remove-filter": 606 case "remove-filter":
614 ext.backgroundPage.sendMessage( 607 ext.backgroundPage.sendMessage({
615 {
616 type: "filters.remove", 608 type: "filters.remove",
617 text: findParentData(element, "access", false) 609 text: findParentData(element, "access", false)
618 }); 610 });
619 break; 611 break;
620 } 612 }
621 } 613 }
622 } 614 }
623 615
624 function getKey(e) 616 function getKey(e)
625 { 617 {
626 // e.keyCode has been deprecated so we attempt to use e.key 618 // e.keyCode has been deprecated so we attempt to use e.key
627 if ("key" in e) 619 if ("key" in e)
628 return e.key; 620 return e.key;
629 return getKey.keys[e.keyCode]; 621 return getKey.keys[e.keyCode];
630 } 622 }
631 getKey.keys = { 623 getKey.keys = {
632 9: "Tab", 624 9: "Tab",
633 13: "Enter", 625 13: "Enter",
634 27: "Escape", 626 27: "Escape",
635 37: "ArrowLeft", 627 37: "ArrowLeft",
636 38: "ArrowUp", 628 38: "ArrowUp",
637 39: "ArrowRight", 629 39: "ArrowRight",
638 40: "ArrowDown" 630 40: "ArrowDown"
639 }; 631 };
640 632
641 function onKeyUp(e) 633 function onKeyUp(e)
642 { 634 {
643 var key = getKey(e); 635 let key = getKey(e);
644 var element = document.activeElement; 636 let element = document.activeElement;
645 if (!key || !element) 637 if (!key || !element)
646 return; 638 return;
647 639
648 var container = findParentData(element, "action", true); 640 let container = findParentData(element, "action", true);
649 if (!container || !container.hasAttribute("data-keys")) 641 if (!container || !container.hasAttribute("data-keys"))
650 return; 642 return;
651 643
652 var keys = container.getAttribute("data-keys").split(" "); 644 let keys = container.getAttribute("data-keys").split(" ");
653 if (keys.indexOf(key) < 0) 645 if (keys.indexOf(key) < 0)
654 return; 646 return;
655 647
656 switch (container.getAttribute("data-action")) 648 switch (container.getAttribute("data-action"))
657 { 649 {
658 case "add-domain-exception": 650 case "add-domain-exception":
659 addWhitelistedDomain(); 651 addWhitelistedDomain();
660 break; 652 break;
661 case "open-doclink": 653 case "open-doclink":
662 var doclink = findParentData(element, "doclink", false); 654 let doclink = findParentData(element, "doclink", false);
663 openDocLink(doclink); 655 openDocLink(doclink);
664 break; 656 break;
665 case "switch-tab": 657 case "switch-tab":
666 if (key == "Enter") 658 if (key == "Enter")
667 { 659 {
668 var tabId = findParentData(element, "tab", false); 660 let tabId = findParentData(element, "tab", false);
669 switchTab(tabId); 661 switchTab(tabId);
670 } 662 }
671 else if (element.hasAttribute("aria-selected")) 663 else if (element.hasAttribute("aria-selected"))
672 { 664 {
673 if (key == "ArrowLeft" || key == "ArrowUp") 665 if (key == "ArrowLeft" || key == "ArrowUp")
674 { 666 {
675 element = element.previousElementSibling 667 element = element.previousElementSibling ||
676 || container.lastElementChild; 668 container.lastElementChild;
677 } 669 }
678 else if (key == "ArrowRight" || key == "ArrowDown") 670 else if (key == "ArrowRight" || key == "ArrowDown")
679 { 671 {
680 element = element.nextElementSibling 672 element = element.nextElementSibling ||
681 || container.firstElementChild; 673 container.firstElementChild;
682 } 674 }
683 675
684 var tabId = findParentData(element, "tab", false); 676 let tabId = findParentData(element, "tab", false);
685 switchTab(tabId); 677 switchTab(tabId);
686 } 678 }
687 break; 679 break;
688 } 680 }
689 } 681 }
690 682
691 function selectTabItem(tabId, container, focus) 683 function selectTabItem(tabId, container, focus)
692 { 684 {
693 // Show tab content 685 // Show tab content
694 document.body.setAttribute("data-tab", tabId); 686 document.body.setAttribute("data-tab", tabId);
695 687
696 // Select tab 688 // Select tab
697 var tabList = container.querySelector("[role='tablist']"); 689 let tabList = container.querySelector("[role='tablist']");
698 if (!tabList) 690 if (!tabList)
699 return null; 691 return null;
700 692
701 var previousTab = tabList.querySelector("[aria-selected]"); 693 let previousTab = tabList.querySelector("[aria-selected]");
702 previousTab.removeAttribute("aria-selected"); 694 previousTab.removeAttribute("aria-selected");
703 previousTab.setAttribute("tabindex", -1); 695 previousTab.setAttribute("tabindex", -1);
704 696
705 var tab = tabList.querySelector("li[data-tab='" + tabId + "']"); 697 let tab = tabList.querySelector("li[data-tab='" + tabId + "']");
706 tab.setAttribute("aria-selected", true); 698 tab.setAttribute("aria-selected", true);
707 tab.setAttribute("tabindex", 0); 699 tab.setAttribute("tabindex", 0);
708 700
709 var tabContentId = tab.getAttribute("aria-controls"); 701 let tabContentId = tab.getAttribute("aria-controls");
710 var tabContent = document.getElementById(tabContentId); 702 let tabContent = document.getElementById(tabContentId);
711 703
712 // Select sub tabs 704 // Select sub tabs
713 if (tab.hasAttribute("data-subtab")) 705 if (tab.hasAttribute("data-subtab"))
714 selectTabItem(tab.getAttribute("data-subtab"), tabContent, false); 706 selectTabItem(tab.getAttribute("data-subtab"), tabContent, false);
715 707
716 if (tab && focus) 708 if (tab && focus)
717 tab.focus(); 709 tab.focus();
718 710
719 return tabContent; 711 return tabContent;
720 } 712 }
721 713
722 function onHashChange() 714 function onHashChange()
723 { 715 {
724 var hash = location.hash.substr(1); 716 let hash = location.hash.substr(1);
725 if (!hash) 717 if (!hash)
726 return; 718 return;
727 719
728 // Select tab and parent tabs 720 // Select tab and parent tabs
729 var tabIds = hash.split("-"); 721 let tabIds = hash.split("-");
730 var tabContent = document.body; 722 let tabContent = document.body;
731 for (var i = 0; i < tabIds.length; i++) 723 for (let i = 0; i < tabIds.length; i++)
732 { 724 {
733 var tabId = tabIds.slice(0, i + 1).join("-"); 725 let tabId = tabIds.slice(0, i + 1).join("-");
734 tabContent = selectTabItem(tabId, tabContent, true); 726 tabContent = selectTabItem(tabId, tabContent, true);
735 if (!tabContent) 727 if (!tabContent)
736 break; 728 break;
737 } 729 }
738 } 730 }
739 731
740 function onDOMLoaded() 732 function onDOMLoaded()
741 { 733 {
742 populateLists(); 734 populateLists();
743 function onFindLanguageKeyUp() 735 function onFindLanguageKeyUp()
744 { 736 {
745 var searchStyle = E("search-style"); 737 let searchStyle = E("search-style");
746 if (!this.value) 738 if (!this.value)
747 searchStyle.innerHTML = ""; 739 searchStyle.innerHTML = "";
748 else 740 else
749 searchStyle.innerHTML = "#all-lang-table li:not([data-search*=\"" + this .value.toLowerCase() + "\"]) { display: none; }"; 741 {
742 searchStyle.innerHTML = "#all-lang-table li:not([data-search*=\"" +
743 this.value.toLowerCase() + "\"]) { display: none; }";
744 }
750 } 745 }
751 746
752 // Initialize navigation sidebar 747 // Initialize navigation sidebar
753 ext.backgroundPage.sendMessage( 748 ext.backgroundPage.sendMessage({
754 {
755 type: "app.get", 749 type: "app.get",
756 what: "addonVersion" 750 what: "addonVersion"
757 }, 751 },
758 function(addonVersion) 752 (addonVersion) =>
759 { 753 {
760 E("abp-version").textContent = addonVersion; 754 E("abp-version").textContent = addonVersion;
761 }); 755 });
762 getDocLink("releases", function(link) 756 getDocLink("releases", (link) =>
763 { 757 {
764 E("link-version").setAttribute("href", link); 758 E("link-version").setAttribute("href", link);
765 }); 759 });
766 760
767 updateShareLink(); 761 updateShareLink();
768 updateTooltips(); 762 updateTooltips();
769 763
770 // Initialize interactive UI elements 764 // Initialize interactive UI elements
771 document.body.addEventListener("click", onClick, false); 765 document.body.addEventListener("click", onClick, false);
772 document.body.addEventListener("keyup", onKeyUp, false); 766 document.body.addEventListener("keyup", onKeyUp, false);
773 var placeholderValue = getMessage("options_dialog_language_find"); 767 let placeholderValue = getMessage("options_dialog_language_find");
774 E("find-language").setAttribute("placeholder", placeholderValue); 768 E("find-language").setAttribute("placeholder", placeholderValue);
775 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); 769 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false);
776 E("whitelisting-textbox").addEventListener("keypress", function(e) 770 E("whitelisting-textbox").addEventListener("keypress", (e) =>
777 { 771 {
778 if (getKey(e) == "Enter") 772 if (getKey(e) == "Enter")
779 addWhitelistedDomain(); 773 addWhitelistedDomain();
780 }, false); 774 }, false);
781 775
782 // Advanced tab 776 // Advanced tab
783 var tweaks = document.querySelectorAll("#tweaks li[data-pref]"); 777 let tweaks = document.querySelectorAll("#tweaks li[data-pref]");
784 tweaks = Array.prototype.map.call(tweaks, function(checkbox) 778 tweaks = Array.prototype.map.call(tweaks, (checkbox) =>
785 { 779 {
786 return checkbox.getAttribute("data-pref"); 780 return checkbox.getAttribute("data-pref");
787 }); 781 });
788 tweaks.forEach(function(key) 782 for (let key of tweaks)
789 { 783 {
790 getPref(key, function(value) 784 getPref(key, (value) =>
791 { 785 {
792 onPrefMessage(key, value, true); 786 onPrefMessage(key, value, true);
793 }); 787 });
794 }); 788 }
795 ext.backgroundPage.sendMessage( 789 ext.backgroundPage.sendMessage({
796 {
797 type: "app.get", 790 type: "app.get",
798 what: "features" 791 what: "features"
799 }, 792 },
800 function(features) 793 (features) =>
801 { 794 {
802 hidePref("show_devtools_panel", !features.devToolsPanel); 795 hidePref("show_devtools_panel", !features.devToolsPanel);
803 }); 796 });
804 797
805 var filterTextbox = document.querySelector("#custom-filters-add input"); 798 let filterTextbox = document.querySelector("#custom-filters-add input");
806 placeholderValue = getMessage("options_customFilters_textbox_placeholder"); 799 placeholderValue = getMessage("options_customFilters_textbox_placeholder");
807 filterTextbox.setAttribute("placeholder", placeholderValue); 800 filterTextbox.setAttribute("placeholder", placeholderValue);
808 function addCustomFilters() 801 function addCustomFilters()
809 { 802 {
810 var filterText = filterTextbox.value; 803 let filterText = filterTextbox.value;
811 sendMessageHandleErrors( 804 sendMessageHandleErrors({
812 {
813 type: "filters.add", 805 type: "filters.add",
814 text: filterText 806 text: filterText
815 }, 807 },
816 function() 808 () =>
817 { 809 {
818 filterTextbox.value = ""; 810 filterTextbox.value = "";
819 }); 811 });
820 } 812 }
821 E("custom-filters-add").addEventListener("submit", function(e) 813 E("custom-filters-add").addEventListener("submit", (e) =>
822 { 814 {
823 e.preventDefault(); 815 e.preventDefault();
824 addCustomFilters(); 816 addCustomFilters();
825 }, false); 817 }, false);
826 var customFilterEditButtons = document.querySelectorAll("#custom-filters-edi t-wrapper button");
827 818
828 // Help tab 819 // Help tab
829 getDocLink("faq", function(link) 820 getDocLink("faq", (link) =>
830 { 821 {
831 E("link-faq").setAttribute("href", link); 822 E("link-faq").setAttribute("href", link);
832 }); 823 });
833 getDocLink("social_twitter", function(link) 824 getDocLink("social_twitter", (link) =>
834 { 825 {
835 E("link-twitter").setAttribute("href", link); 826 E("link-twitter").setAttribute("href", link);
836 }); 827 });
837 getDocLink("social_facebook", function(link) 828 getDocLink("social_facebook", (link) =>
838 { 829 {
839 E("link-facebook").setAttribute("href", link); 830 E("link-facebook").setAttribute("href", link);
840 }); 831 });
841 getDocLink("social_gplus", function(link) 832 getDocLink("social_gplus", (link) =>
842 { 833 {
843 E("link-gplus").setAttribute("href", link); 834 E("link-gplus").setAttribute("href", link);
844 }); 835 });
845 getDocLink("social_renren", function(link) 836 getDocLink("social_renren", (link) =>
846 { 837 {
847 E("link-renren").setAttribute("href", link); 838 E("link-renren").setAttribute("href", link);
848 }); 839 });
849 getDocLink("social_weibo", function(link) 840 getDocLink("social_weibo", (link) =>
850 { 841 {
851 E("link-weibo").setAttribute("href", link); 842 E("link-weibo").setAttribute("href", link);
852 }); 843 });
853 844
854 // Set forum link 845 // Set forum link
855 ext.backgroundPage.sendMessage( 846 ext.backgroundPage.sendMessage({
856 {
857 type: "app.get", 847 type: "app.get",
858 what: "platform" 848 what: "platform"
859 }, 849 },
860 function(platform) 850 (platform) =>
861 { 851 {
862 ext.backgroundPage.sendMessage( 852 ext.backgroundPage.sendMessage({
863 {
864 type: "app.get", 853 type: "app.get",
865 what: "application" 854 what: "application"
866 }, 855 },
867 function(application) 856 (application) =>
868 { 857 {
869 if (platform == "chromium" && application != "opera") 858 if (platform == "chromium" && application != "opera")
870 application = "chrome"; 859 application = "chrome";
871 860
872 getDocLink(application + "_support", function(link) 861 getDocLink(application + "_support", (link) =>
873 { 862 {
874 E("link-forum").setAttribute("href", link); 863 E("link-forum").setAttribute("href", link);
875 }); 864 });
876 }); 865 });
877 }); 866 });
878 867
879 E("dialog").addEventListener("keydown", function(e) 868 E("dialog").addEventListener("keydown", function(e)
880 { 869 {
881 switch (getKey(e)) 870 switch (getKey(e))
882 { 871 {
(...skipping 14 matching lines...) Expand all
897 e.preventDefault(); 886 e.preventDefault();
898 this.querySelector(".focus-first").focus(); 887 this.querySelector(".focus-first").focus();
899 } 888 }
900 break; 889 break;
901 } 890 }
902 }, false); 891 }, false);
903 892
904 onHashChange(); 893 onHashChange();
905 } 894 }
906 895
907 var focusedBeforeDialog = null; 896 let focusedBeforeDialog = null;
908 function openDialog(name) 897 function openDialog(name)
909 { 898 {
910 var dialog = E("dialog"); 899 let dialog = E("dialog");
911 dialog.setAttribute("aria-hidden", false); 900 dialog.setAttribute("aria-hidden", false);
912 dialog.setAttribute("aria-labelledby", "dialog-title-" + name); 901 dialog.setAttribute("aria-labelledby", "dialog-title-" + name);
913 document.body.setAttribute("data-dialog", name); 902 document.body.setAttribute("data-dialog", name);
914 903
915 var defaultFocus = document.querySelector("#dialog-content-" + name 904 let defaultFocus = document.querySelector(
916 + " .default-focus"); 905 "#dialog-content-" + name + " .default-focus"
906 );
917 if (!defaultFocus) 907 if (!defaultFocus)
918 defaultFocus = dialog.querySelector(".focus-first"); 908 defaultFocus = dialog.querySelector(".focus-first");
919 focusedBeforeDialog = document.activeElement; 909 focusedBeforeDialog = document.activeElement;
920 defaultFocus.focus(); 910 defaultFocus.focus();
921 } 911 }
922 912
923 function closeDialog() 913 function closeDialog()
924 { 914 {
925 var dialog = E("dialog"); 915 let dialog = E("dialog");
926 dialog.setAttribute("aria-hidden", true); 916 dialog.setAttribute("aria-hidden", true);
927 dialog.removeAttribute("aria-labelledby"); 917 dialog.removeAttribute("aria-labelledby");
928 document.body.removeAttribute("data-dialog"); 918 document.body.removeAttribute("data-dialog");
929 focusedBeforeDialog.focus(); 919 focusedBeforeDialog.focus();
930 } 920 }
931 921
932 function populateLists() 922 function populateLists()
933 { 923 {
934 subscriptionsMap = Object.create(null); 924 subscriptionsMap = Object.create(null);
935 filtersMap = Object.create(null); 925 filtersMap = Object.create(null);
936 926
937 // Empty collections and lists 927 // Empty collections and lists
938 for (var property in collections) 928 for (let property in collections)
939 collections[property].clearAll(); 929 collections[property].clearAll();
940 930
941 ext.backgroundPage.sendMessage( 931 ext.backgroundPage.sendMessage({
942 {
943 type: "subscriptions.get", 932 type: "subscriptions.get",
944 special: true 933 special: true
945 }, 934 },
946 function(subscriptions) 935 (subscriptions) =>
947 { 936 {
948 // Load filters 937 // Load filters
949 for (var i = 0; i < subscriptions.length; i++) 938 for (let subscription of subscriptions)
950 { 939 {
951 ext.backgroundPage.sendMessage( 940 ext.backgroundPage.sendMessage({
941 type: "filters.get",
942 subscriptionUrl: subscription.url
943 },
944 (filters) =>
952 { 945 {
953 type: "filters.get", 946 for (let filter of filters)
954 subscriptionUrl: subscriptions[i].url 947 updateFilter(filter);
955 },
956 function(filters)
957 {
958 for (var i = 0; i < filters.length; i++)
959 updateFilter(filters[i]);
960 }); 948 });
961 } 949 }
962 }); 950 });
963 loadRecommendations(); 951 loadRecommendations();
964 ext.backgroundPage.sendMessage( 952 ext.backgroundPage.sendMessage({
965 {
966 type: "prefs.get", 953 type: "prefs.get",
967 key: "subscriptions_exceptionsurl" 954 key: "subscriptions_exceptionsurl"
968 }, 955 },
969 function(url) 956 (url) =>
970 { 957 {
971 acceptableAdsUrl = url; 958 acceptableAdsUrl = url;
972 addSubscription({ 959 addSubscription({
973 url: acceptableAdsUrl, 960 url: acceptableAdsUrl,
974 disabled: true 961 disabled: true
975 }); 962 });
976 963
977 // Load user subscriptions 964 // Load user subscriptions
978 ext.backgroundPage.sendMessage( 965 ext.backgroundPage.sendMessage({
979 {
980 type: "subscriptions.get", 966 type: "subscriptions.get",
981 downloadable: true 967 downloadable: true
982 }, 968 },
983 function(subscriptions) 969 (subscriptions) =>
984 { 970 {
985 for (var i = 0; i < subscriptions.length; i++) 971 for (let subscription of subscriptions)
986 onSubscriptionMessage("added", subscriptions[i]); 972 onSubscriptionMessage("added", subscription);
987 }); 973 });
988 }); 974 });
989 } 975 }
990 976
991 function addWhitelistedDomain() 977 function addWhitelistedDomain()
992 { 978 {
993 var domain = E("whitelisting-textbox"); 979 let domain = E("whitelisting-textbox");
994 if (domain.value) 980 if (domain.value)
995 { 981 {
996 sendMessageHandleErrors( 982 sendMessageHandleErrors({
997 {
998 type: "filters.add", 983 type: "filters.add",
999 text: "@@||" + domain.value.toLowerCase() + "^$document" 984 text: "@@||" + domain.value.toLowerCase() + "^$document"
1000 }); 985 });
1001 } 986 }
1002 987
1003 domain.value = ""; 988 domain.value = "";
1004 document.querySelector("#whitelisting .controls").classList.remove("mode-edi t"); 989 document.querySelector("#whitelisting .controls").
990 classList.remove("mode-edit");
1005 } 991 }
1006 992
1007 function editCustomFilters() 993 function editCustomFilters()
1008 { 994 {
1009 var customFilterItems = collections.customFilters.items; 995 let filterTexts = [];
1010 var filterTexts = []; 996 for (let customFilterItem of collections.customFilters.items)
1011 for (var i = 0; i < customFilterItems.length; i++) 997 filterTexts.push(customFilterItem.text);
1012 filterTexts.push(customFilterItems[i].text);
1013 E("custom-filters-raw").value = filterTexts.join("\n"); 998 E("custom-filters-raw").value = filterTexts.join("\n");
1014 } 999 }
1015 1000
1016 function addEnableSubscription(url, title, homepage) 1001 function addEnableSubscription(url, title, homepage)
1017 { 1002 {
1018 var messageType = null; 1003 let messageType = null;
1019 var knownSubscription = subscriptionsMap[url]; 1004 let knownSubscription = subscriptionsMap[url];
1020 if (knownSubscription && knownSubscription.disabled == true) 1005 if (knownSubscription && knownSubscription.disabled == true)
1021 messageType = "subscriptions.toggle"; 1006 messageType = "subscriptions.toggle";
1022 else 1007 else
1023 messageType = "subscriptions.add"; 1008 messageType = "subscriptions.add";
1024 1009
1025 var message = { 1010 let message = {
1026 type: messageType, 1011 type: messageType,
1027 url: url 1012 url
1028 }; 1013 };
1029 if (title) 1014 if (title)
1030 message.title = title; 1015 message.title = title;
1031 if (homepage) 1016 if (homepage)
1032 message.homepage = homepage; 1017 message.homepage = homepage;
1033 1018
1034 ext.backgroundPage.sendMessage(message); 1019 ext.backgroundPage.sendMessage(message);
1035 } 1020 }
1036 1021
1037 function onFilterMessage(action, filter) 1022 function onFilterMessage(action, filter)
1038 { 1023 {
1039 switch (action) 1024 switch (action)
1040 { 1025 {
1041 case "added": 1026 case "added":
1042 updateFilter(filter); 1027 updateFilter(filter);
1043 updateShareLink(); 1028 updateShareLink();
1044 break; 1029 break;
1045 case "loaded": 1030 case "loaded":
1046 populateLists(); 1031 populateLists();
1047 break; 1032 break;
1048 case "removed": 1033 case "removed":
1049 var knownFilter = filtersMap[filter.text]; 1034 let knownFilter = filtersMap[filter.text];
1050 collections.whitelist.removeItem(knownFilter); 1035 collections.whitelist.removeItem(knownFilter);
1051 collections.customFilters.removeItem(knownFilter); 1036 collections.customFilters.removeItem(knownFilter);
1052 delete filtersMap[filter.text]; 1037 delete filtersMap[filter.text];
1053 updateShareLink(); 1038 updateShareLink();
1054 break; 1039 break;
1055 } 1040 }
1056 } 1041 }
1057 1042
1058 function onSubscriptionMessage(action, subscription) 1043 function onSubscriptionMessage(action, subscription)
1059 { 1044 {
1060 if (subscription.url in subscriptionsMap) 1045 if (subscription.url in subscriptionsMap)
1061 { 1046 {
1062 var knownSubscription = subscriptionsMap[subscription.url]; 1047 let knownSubscription = subscriptionsMap[subscription.url];
1063 for (var property in subscription) 1048 for (let property in subscription)
1064 { 1049 {
1065 if (property == "title" && knownSubscription.recommended) 1050 if (property == "title" && knownSubscription.recommended)
1066 knownSubscription.originalTitle = subscription.title; 1051 knownSubscription.originalTitle = subscription.title;
1067 else 1052 else
1068 knownSubscription[property] = subscription[property]; 1053 knownSubscription[property] = subscription[property];
1069 } 1054 }
1070 subscription = knownSubscription; 1055 subscription = knownSubscription;
1071 } 1056 }
1072 switch (action) 1057 switch (action)
1073 { 1058 {
(...skipping 28 matching lines...) Expand all
1102 } 1087 }
1103 collections.filterLists.removeItem(subscription); 1088 collections.filterLists.removeItem(subscription);
1104 break; 1089 break;
1105 } 1090 }
1106 1091
1107 updateShareLink(); 1092 updateShareLink();
1108 } 1093 }
1109 1094
1110 function hidePref(key, value) 1095 function hidePref(key, value)
1111 { 1096 {
1112 var element = document.querySelector("[data-pref='" + key + "']"); 1097 let element = document.querySelector("[data-pref='" + key + "']");
1113 if (element) 1098 if (element)
1114 element.setAttribute("aria-hidden", value); 1099 element.setAttribute("aria-hidden", value);
1115 } 1100 }
1116 1101
1117 function getPref(key, callback) 1102 function getPref(key, callback)
1118 { 1103 {
1119 var checkPref = getPref.checks[key] || getPref.checkNone; 1104 let checkPref = getPref.checks[key] || getPref.checkNone;
1120 checkPref(function(isActive) 1105 checkPref((isActive) =>
1121 { 1106 {
1122 if (!isActive) 1107 if (!isActive)
1123 { 1108 {
1124 hidePref(key, !isActive); 1109 hidePref(key, !isActive);
1125 return; 1110 return;
1126 } 1111 }
1127 1112
1128 ext.backgroundPage.sendMessage( 1113 ext.backgroundPage.sendMessage({
1129 {
1130 type: "prefs.get", 1114 type: "prefs.get",
1131 key: key 1115 key
1132 }, callback); 1116 }, callback);
1133 }); 1117 });
1134 } 1118 }
1135 1119
1136 getPref.checkNone = function(callback) 1120 getPref.checkNone = function(callback)
1137 { 1121 {
1138 callback(true); 1122 callback(true);
1139 }; 1123 };
1140 1124
1141 getPref.checks = 1125 getPref.checks =
1142 { 1126 {
1143 notifications_ignoredcategories: function(callback) 1127 notifications_ignoredcategories(callback)
1144 { 1128 {
1145 getPref("notifications_showui", callback); 1129 getPref("notifications_showui", callback);
1146 } 1130 }
1147 }; 1131 };
1148 1132
1149 function onPrefMessage(key, value, initial) 1133 function onPrefMessage(key, value, initial)
1150 { 1134 {
1151 switch (key) 1135 switch (key)
1152 { 1136 {
1153 case "notifications_ignoredcategories": 1137 case "notifications_ignoredcategories":
1154 value = value.indexOf("*") == -1; 1138 value = value.indexOf("*") == -1;
1155 break; 1139 break;
1156 1140
1157 case "notifications_showui": 1141 case "notifications_showui":
1158 hidePref("notifications_ignoredcategories", !value); 1142 hidePref("notifications_ignoredcategories", !value);
1159 break; 1143 break;
1160 } 1144 }
1161 1145
1162 var checkbox = document.querySelector("[data-pref='" + key + "'] button[role ='checkbox']"); 1146 let checkbox = document.querySelector(
1147 "[data-pref='" + key + "'] button[role='checkbox']"
1148 );
1163 if (checkbox) 1149 if (checkbox)
1164 checkbox.setAttribute("aria-checked", value); 1150 checkbox.setAttribute("aria-checked", value);
1165 } 1151 }
1166 1152
1167 function updateShareLink() 1153 function updateShareLink()
1168 { 1154 {
1169 var shareResources = [ 1155 let shareResources = [
1170 "https://facebook.com/plugins/like.php?", 1156 "https://facebook.com/plugins/like.php?",
1171 "https://platform.twitter.com/widgets/", 1157 "https://platform.twitter.com/widgets/",
1172 "https://apis.google.com/se/0/_/+1/fastbutton?" 1158 "https://apis.google.com/se/0/_/+1/fastbutton?"
1173 ]; 1159 ];
1174 var isAnyBlocked = false; 1160 let isAnyBlocked = false;
1175 var checksRemaining = shareResources.length; 1161 let checksRemaining = shareResources.length;
1176 1162
1177 function onResult(isBlocked) 1163 function onResult(isBlocked)
1178 { 1164 {
1179 isAnyBlocked |= isBlocked; 1165 isAnyBlocked |= isBlocked;
1180 if (!--checksRemaining) 1166 if (!--checksRemaining)
1181 { 1167 {
1182 // Hide the share tab if a script on the share page would be blocked 1168 // Hide the share tab if a script on the share page would be blocked
1183 E("tab-share").hidden = isAnyBlocked; 1169 E("tab-share").hidden = isAnyBlocked;
1184 } 1170 }
1185 } 1171 }
1186 1172
1187 for (var i = 0; i < shareResources.length; i++) 1173 for (let sharedResource of shareResources)
1188 checkShareResource(shareResources[i], onResult); 1174 checkShareResource(sharedResource, onResult);
1189 } 1175 }
1190 1176
1191 function getMessages(id) 1177 function getMessages(id)
1192 { 1178 {
1193 var messages = []; 1179 let messages = [];
1194 for (var i = 1; true; i++) 1180 for (let i = 1; true; i++)
1195 { 1181 {
1196 var message = ext.i18n.getMessage(id + "_" + i); 1182 let message = ext.i18n.getMessage(id + "_" + i);
1197 if (!message) 1183 if (!message)
1198 break; 1184 break;
1199 1185
1200 messages.push(message); 1186 messages.push(message);
1201 } 1187 }
1202 return messages; 1188 return messages;
1203 } 1189 }
1204 1190
1205 function updateTooltips() 1191 function updateTooltips()
1206 { 1192 {
1207 var anchors = document.querySelectorAll(":not(.tooltip) > [data-tooltip]"); 1193 let anchors = document.querySelectorAll(":not(.tooltip) > [data-tooltip]");
1208 for (var i = 0; i < anchors.length; i++) 1194 for (let anchor of anchors)
1209 { 1195 {
1210 var anchor = anchors[i]; 1196 let id = anchor.getAttribute("data-tooltip");
1211 var id = anchor.getAttribute("data-tooltip");
1212 1197
1213 var wrapper = document.createElement("div"); 1198 let wrapper = document.createElement("div");
1214 wrapper.className = "tooltip"; 1199 wrapper.className = "tooltip";
1215 anchor.parentNode.replaceChild(wrapper, anchor); 1200 anchor.parentNode.replaceChild(wrapper, anchor);
1216 wrapper.appendChild(anchor); 1201 wrapper.appendChild(anchor);
1217 1202
1218 var topTexts = getMessages(id); 1203 let topTexts = getMessages(id);
1219 var bottomTexts = getMessages(id + "_notes"); 1204 let bottomTexts = getMessages(id + "_notes");
1220 1205
1221 // We have to use native tooltips to avoid issues when attaching a tooltip 1206 // We have to use native tooltips to avoid issues when attaching a tooltip
1222 // to an element in a scrollable list or otherwise it might get cut off 1207 // to an element in a scrollable list or otherwise it might get cut off
1223 if (anchor.hasAttribute("data-tooltip-native")) 1208 if (anchor.hasAttribute("data-tooltip-native"))
1224 { 1209 {
1225 var title = topTexts.concat(bottomTexts).join("\n\n"); 1210 let title = topTexts.concat(bottomTexts).join("\n\n");
1226 anchor.setAttribute("title", title); 1211 anchor.setAttribute("title", title);
1227 continue; 1212 continue;
1228 } 1213 }
1229 1214
1230 var tooltip = document.createElement("div"); 1215 let tooltip = document.createElement("div");
1231 tooltip.setAttribute("role", "tooltip"); 1216 tooltip.setAttribute("role", "tooltip");
1232 1217
1233 var flip = anchor.getAttribute("data-tooltip-flip"); 1218 let flip = anchor.getAttribute("data-tooltip-flip");
1234 if (flip) 1219 if (flip)
1235 tooltip.className = "flip-" + flip; 1220 tooltip.className = "flip-" + flip;
1236 1221
1237 var imageSource = anchor.getAttribute("data-tooltip-image"); 1222 let imageSource = anchor.getAttribute("data-tooltip-image");
1238 if (imageSource) 1223 if (imageSource)
1239 { 1224 {
1240 var image = document.createElement("img"); 1225 let image = document.createElement("img");
1241 image.src = imageSource; 1226 image.src = imageSource;
1242 image.alt = ""; 1227 image.alt = "";
1243 tooltip.appendChild(image); 1228 tooltip.appendChild(image);
1244 } 1229 }
1245 1230
1246 for (var j = 0; j < topTexts.length; j++) 1231 for (let topText of topTexts)
1247 { 1232 {
1248 var paragraph = document.createElement("p"); 1233 let paragraph = document.createElement("p");
1249 paragraph.innerHTML = topTexts[j]; 1234 paragraph.innerHTML = topText;
1250 tooltip.appendChild(paragraph); 1235 tooltip.appendChild(paragraph);
1251 } 1236 }
1252 if (bottomTexts.length > 0) 1237 if (bottomTexts.length > 0)
1253 { 1238 {
1254 var notes = document.createElement("div"); 1239 let notes = document.createElement("div");
1255 notes.className = "notes"; 1240 notes.className = "notes";
1256 for (var j = 0; j < bottomTexts.length; j++) 1241 for (let bottomText of bottomTexts)
1257 { 1242 {
1258 var paragraph = document.createElement("p"); 1243 let paragraph = document.createElement("p");
1259 paragraph.innerHTML = bottomTexts[j]; 1244 paragraph.innerHTML = bottomText;
1260 notes.appendChild(paragraph); 1245 notes.appendChild(paragraph);
1261 } 1246 }
1262 tooltip.appendChild(notes); 1247 tooltip.appendChild(notes);
1263 } 1248 }
1264 1249
1265 wrapper.appendChild(tooltip); 1250 wrapper.appendChild(tooltip);
1266 } 1251 }
1267 } 1252 }
1268 1253
1269 ext.onMessage.addListener(function(message) 1254 ext.onMessage.addListener((message) =>
1270 { 1255 {
1271 switch (message.type) 1256 switch (message.type)
1272 { 1257 {
1273 case "app.respond": 1258 case "app.respond":
1274 switch (message.action) 1259 switch (message.action)
1275 { 1260 {
1276 case "addSubscription": 1261 case "addSubscription":
1277 var subscription = message.args[0]; 1262 let subscription = message.args[0];
1278 var dialog = E("dialog-content-predefined"); 1263 let dialog = E("dialog-content-predefined");
1279 dialog.querySelector("h3").textContent = subscription.title || ""; 1264 dialog.querySelector("h3").textContent = subscription.title || "";
1280 dialog.querySelector(".url").textContent = subscription.url; 1265 dialog.querySelector(".url").textContent = subscription.url;
1281 openDialog("predefined"); 1266 openDialog("predefined");
1282 break; 1267 break;
1283 case "focusSection": 1268 case "focusSection":
1284 document.body.setAttribute("data-tab", message.args[0]); 1269 document.body.setAttribute("data-tab", message.args[0]);
1285 break; 1270 break;
1286 } 1271 }
1287 break; 1272 break;
1288 case "filters.respond": 1273 case "filters.respond":
1289 onFilterMessage(message.action, message.args[0]); 1274 onFilterMessage(message.action, message.args[0]);
1290 break; 1275 break;
1291 case "prefs.respond": 1276 case "prefs.respond":
1292 onPrefMessage(message.action, message.args[0], false); 1277 onPrefMessage(message.action, message.args[0], false);
1293 break; 1278 break;
1294 case "subscriptions.respond": 1279 case "subscriptions.respond":
1295 onSubscriptionMessage(message.action, message.args[0]); 1280 onSubscriptionMessage(message.action, message.args[0]);
1296 break; 1281 break;
1297 } 1282 }
1298 }); 1283 });
1299 1284
1300 ext.backgroundPage.sendMessage( 1285 ext.backgroundPage.sendMessage({
1301 {
1302 type: "app.listen", 1286 type: "app.listen",
1303 filter: ["addSubscription", "focusSection"] 1287 filter: ["addSubscription", "focusSection"]
1304 }); 1288 });
1305 ext.backgroundPage.sendMessage( 1289 ext.backgroundPage.sendMessage({
1306 {
1307 type: "filters.listen", 1290 type: "filters.listen",
1308 filter: ["added", "loaded", "removed"] 1291 filter: ["added", "loaded", "removed"]
1309 }); 1292 });
1310 ext.backgroundPage.sendMessage( 1293 ext.backgroundPage.sendMessage({
1311 {
1312 type: "prefs.listen", 1294 type: "prefs.listen",
1313 filter: ["notifications_ignoredcategories", "notifications_showui", 1295 filter: ["notifications_ignoredcategories", "notifications_showui",
1314 "show_devtools_panel", "shouldShowBlockElementMenu"] 1296 "show_devtools_panel", "shouldShowBlockElementMenu"]
1315 }); 1297 });
1316 ext.backgroundPage.sendMessage( 1298 ext.backgroundPage.sendMessage({
1317 {
1318 type: "subscriptions.listen", 1299 type: "subscriptions.listen",
1319 filter: ["added", "disabled", "homepage", "lastDownload", "removed", 1300 filter: ["added", "disabled", "homepage", "lastDownload", "removed",
1320 "title", "downloadStatus", "downloading"] 1301 "title", "downloadStatus", "downloading"]
1321 }); 1302 });
1322 1303
1323 window.addEventListener("DOMContentLoaded", onDOMLoaded, false); 1304 window.addEventListener("DOMContentLoaded", onDOMLoaded, false);
1324 window.addEventListener("hashchange", onHashChange, false); 1305 window.addEventListener("hashchange", onHashChange, false);
1325 })(); 1306 }
OLDNEW
« no previous file with comments | « messageResponder.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld