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

Delta Between Two Patch Sets: new-options.js

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

Powered by Google App Engine
This is Rietveld