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

Delta Between Two Patch Sets: new-options.js

Issue 29445590: Issue 5255 - Advanced tab (HTML, strings and functionality) (Closed)
Left Patch Set: Addressed comments from the review meeting Created June 14, 2017, 10:58 a.m.
Right Patch Set: Fixed nits Created July 14, 2017, 5:43 p.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') | skin/new-options.css » ('j') | 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-2017 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, 18 /* globals checkShareResource, getDocLink, i18nFormatDateTime, openSharePopup,
19 E */ 19 E */
20 20
21 "use strict"; 21 "use strict";
22 22
23 { 23 {
24 let subscriptionsMap = Object.create(null); 24 let subscriptionsMap = Object.create(null);
25 let filtersMap = Object.create(null); 25 let filtersMap = Object.create(null);
26 let collections = Object.create(null); 26 let collections = Object.create(null);
27 let acceptableAdsUrl = null; 27 let acceptableAdsUrl = null;
28 let isCustomFiltersLoaded = false; 28 let isCustomFiltersLoaded = false;
29 let {getMessage} = ext.i18n; 29 let {getMessage} = ext.i18n;
30 let customFilters = [];
30 let filterErrors = new Map([ 31 let filterErrors = new Map([
31 ["synchronize_invalid_url", 32 ["synchronize_invalid_url",
32 "options_filterList_lastDownload_invalidURL"], 33 "options_filterList_lastDownload_invalidURL"],
33 ["synchronize_connection_error", 34 ["synchronize_connection_error",
34 "options_filterList_lastDownload_connectionError"], 35 "options_filterList_lastDownload_connectionError"],
35 ["synchronize_invalid_data", 36 ["synchronize_invalid_data",
36 "options_filterList_lastDownload_invalidData"], 37 "options_filterList_lastDownload_invalidData"],
37 ["synchronize_checksum_mismatch", 38 ["synchronize_checksum_mismatch",
38 "options_filterList_lastDownload_checksumMismatch"] 39 "options_filterList_lastDownload_checksumMismatch"]
39 ]); 40 ]);
40 41 const timestampUI = Symbol();
41 const whitelistedDomainRegexp = /^@@\|\|([^/:]+)\^\$document$/; 42 const whitelistedDomainRegexp = /^@@\|\|([^/:]+)\^\$document$/;
43 // Period of time in milliseconds
44 const minuteInMs = 60000;
45 const hourInMs = 3600000;
46 const fullDayInMs = 86400000;
42 47
43 function Collection(details) 48 function Collection(details)
44 { 49 {
45 this.details = details; 50 this.details = details;
46 this.items = []; 51 this.items = [];
47 } 52 }
48 53
49 Collection.prototype._setEmpty = function(table, text) 54 Collection.prototype._setEmpty = function(table, texts)
50 { 55 {
51 let placeholder = table.querySelector(".empty-placeholder"); 56 let placeholders = table.querySelectorAll(".empty-placeholder");
52 if (text && !placeholder) 57
53 { 58 if (texts && placeholders.length == 0)
54 placeholder = document.createElement("li"); 59 {
55 placeholder.className = "empty-placeholder"; 60 for (let text of texts)
56 placeholder.textContent = getMessage(text); 61 {
57 table.appendChild(placeholder); 62 let placeholder = document.createElement("li");
58 } 63 placeholder.className = "empty-placeholder";
59 else if (placeholder) 64 placeholder.textContent = getMessage(text);
60 table.removeChild(placeholder); 65 table.appendChild(placeholder);
66 }
67 }
68 else if (placeholders.length > 0)
69 {
70 for (let placeholder of placeholders)
71 table.removeChild(placeholder);
72 }
61 }; 73 };
62 74
63 Collection.prototype._createElementQuery = function(item) 75 Collection.prototype._createElementQuery = function(item)
64 { 76 {
65 let access = (item.url || item.text).replace(/'/g, "\\'"); 77 let access = (item.url || item.text).replace(/'/g, "\\'");
66 return function(container) 78 return function(container)
67 { 79 {
68 return container.querySelector("[data-access='" + access + "']"); 80 return container.querySelector("[data-access='" + access + "']");
69 }; 81 };
70 }; 82 };
71 83
72 Collection.prototype._getItemTitle = function(item, i) 84 Collection.prototype._getItemTitle = function(item, i)
73 { 85 {
74 if (item.url == acceptableAdsUrl) 86 if (item.url == acceptableAdsUrl)
75 return getMessage("options_acceptableAds_description"); 87 return getMessage("options_acceptableAds_description");
76 if (this.details[i].useOriginalTitle && item.originalTitle) 88 if (this.details[i].useOriginalTitle && item.originalTitle)
77 return item.originalTitle; 89 return item.originalTitle;
78 return item.title || item.url || item.text; 90 return item.title || item.url || item.text;
79 }; 91 };
80 92
81 Collection.prototype.addItem = function(item) 93 Collection.prototype._sortItems = function()
82 { 94 {
83 if (this.items.indexOf(item) >= 0)
84 return;
85
86 this.items.push(item);
87 this.items.sort((a, b) => 95 this.items.sort((a, b) =>
88 { 96 {
89 // Make sure that Acceptable Ads is always last, since it cannot be 97 // Make sure that Acceptable Ads is always last, since it cannot be
90 // disabled, but only be removed. That way it's grouped together with 98 // disabled, but only be removed. That way it's grouped together with
91 // the "Own filter list" which cannot be disabled either at the bottom 99 // the "Own filter list" which cannot be disabled either at the bottom
92 // of the filter lists in the Advanced tab. 100 // of the filter lists in the Advanced tab.
93 if (a.url == acceptableAdsUrl) 101 if (a.url == acceptableAdsUrl)
94 return 1; 102 return 1;
95 if (b.url == acceptableAdsUrl) 103 if (b.url == acceptableAdsUrl)
96 return -1; 104 return -1;
97 105
106 // Make sure that newly added entries always appear on top in descending
107 // chronological order
108 let aTimestamp = a[timestampUI] || 0;
109 let bTimestamp = b[timestampUI] || 0;
110 if (aTimestamp || bTimestamp)
111 return bTimestamp - aTimestamp;
112
98 let aTitle = this._getItemTitle(a, 0).toLowerCase(); 113 let aTitle = this._getItemTitle(a, 0).toLowerCase();
99 let bTitle = this._getItemTitle(b, 0).toLowerCase(); 114 let bTitle = this._getItemTitle(b, 0).toLowerCase();
100 return aTitle.localeCompare(bTitle); 115 return aTitle.localeCompare(bTitle);
101 }); 116 });
102 117 };
118
119 Collection.prototype.addItem = function(item)
120 {
121 if (this.items.indexOf(item) >= 0)
122 return;
123
124 this.items.push(item);
125 this._sortItems();
103 for (let j = 0; j < this.details.length; j++) 126 for (let j = 0; j < this.details.length; j++)
104 { 127 {
105 let detail = this.details[j]; 128 let detail = this.details[j];
106 let table = E(detail.id); 129 let table = E(detail.id);
107 let template = table.querySelector("template"); 130 let template = table.querySelector("template");
108 let listItem = document.createElement("li"); 131 let listItem = document.createElement("li");
109 listItem.appendChild(document.importNode(template.content, true)); 132 listItem.appendChild(document.importNode(template.content, true));
110 listItem.setAttribute("aria-label", this._getItemTitle(item, j)); 133 listItem.setAttribute("aria-label", this._getItemTitle(item, j));
111 listItem.setAttribute("data-access", item.url || item.text); 134 listItem.setAttribute("data-access", item.url || item.text);
112 listItem.setAttribute("role", "section"); 135 listItem.setAttribute("role", "section");
113 136
114 let label = listItem.querySelector(".display"); 137 let label = listItem.querySelector(".display");
115 if (item.recommended && label.hasAttribute("data-tooltip")) 138 if (item.recommended && label.hasAttribute("data-tooltip"))
116 { 139 {
117 let tooltipId = label.getAttribute("data-tooltip"); 140 let tooltipId = label.getAttribute("data-tooltip");
118 tooltipId = tooltipId.replace("%value%", item.recommended); 141 tooltipId = tooltipId.replace("%value%", item.recommended);
119 label.setAttribute("data-tooltip", tooltipId); 142 label.setAttribute("data-tooltip", tooltipId);
120 } 143 }
121 144
122 for (let control of listItem.querySelectorAll(".control")) 145 for (let control of listItem.querySelectorAll(".control"))
123 { 146 {
124 if (control.hasAttribute("title")) 147 if (control.hasAttribute("title"))
125 { 148 {
126 let titleValue = getMessage(control.getAttribute("title")); 149 let titleValue = getMessage(control.getAttribute("title"));
127 control.setAttribute("title", titleValue); 150 control.setAttribute("title", titleValue);
128 } 151 }
129 } 152 }
130 153
131 this._setEmpty(table, null); 154 this._setEmpty(table, null);
132 if (table.hasChildNodes()) 155 if (table.children.length > 0)
saroyanm 2017/06/14 11:07:06 This should be "table.children" so we can ignore t
saroyanm 2017/06/14 12:02:02 Done.
133 { 156 table.insertBefore(listItem, table.children[this.items.indexOf(item)]);
134 let beforeIndex = this.items.indexOf(item) + (detail.useHeader == true);
135 table.insertBefore(listItem, table.children[beforeIndex]);
136 }
137 else 157 else
138 {
139 table.appendChild(listItem); 158 table.appendChild(listItem);
140 } 159
141 this.updateItem(item); 160 this.updateItem(item);
142 } 161 }
143 return length; 162 return length;
144 }; 163 };
145 164
146 Collection.prototype.removeItem = function(item) 165 Collection.prototype.removeItem = function(item)
147 { 166 {
148 let index = this.items.indexOf(item); 167 let index = this.items.indexOf(item);
149 if (index == -1) 168 if (index == -1)
150 return; 169 return;
(...skipping 26 matching lines...) Expand all
177 } 196 }
178 197
179 element.parentElement.removeChild(element); 198 element.parentElement.removeChild(element);
180 if (this.items.length == 0) 199 if (this.items.length == 0)
181 this._setEmpty(table, detail.emptyText); 200 this._setEmpty(table, detail.emptyText);
182 } 201 }
183 }; 202 };
184 203
185 Collection.prototype.updateItem = function(item) 204 Collection.prototype.updateItem = function(item)
186 { 205 {
206 let oldIndex = this.items.indexOf(item);
207 this._sortItems();
187 let access = (item.url || item.text).replace(/'/g, "\\'"); 208 let access = (item.url || item.text).replace(/'/g, "\\'");
188 for (let i = 0; i < this.details.length; i++) 209 for (let i = 0; i < this.details.length; i++)
189 { 210 {
190 let table = E(this.details[i].id); 211 let table = E(this.details[i].id);
191 let element = table.querySelector("[data-access='" + access + "']"); 212 let element = table.querySelector("[data-access='" + access + "']");
192 if (!element) 213 if (!element)
193 continue; 214 continue;
194 215
195 let title = this._getItemTitle(item, i); 216 let title = this._getItemTitle(item, i);
196 element.querySelector(".display").textContent = title; 217 element.querySelector(".display").textContent = title;
197 element.setAttribute("aria-label", title); 218 element.setAttribute("aria-label", title);
198 if (this.details[i].searchable) 219 if (this.details[i].searchable)
199 element.setAttribute("data-search", title.toLowerCase()); 220 element.setAttribute("data-search", title.toLowerCase());
200 let control = element.querySelector(".control[role='checkbox']"); 221 let control = element.querySelector(".control[role='checkbox']");
201 if (control) 222 if (control)
202 { 223 {
203 control.setAttribute("aria-checked", item.disabled == false); 224 control.setAttribute("aria-checked", item.disabled == false);
204 if (item.url == acceptableAdsUrl && this == collections.filterLists) 225 if (item.url == acceptableAdsUrl && this == collections.filterLists)
205 control.setAttribute("disabled", true); 226 control.disabled = true;
206 } 227 }
207 228
208 let lastUpdateElem = element.querySelector(".last-update"); 229 let lastUpdateElement = element.querySelector(".last-update");
209 if (lastUpdateElem) 230 if (lastUpdateElement)
210 { 231 {
211 let message = element.querySelector(".message"); 232 let message = element.querySelector(".message");
212 if (item.isDownloading) 233 if (item.isDownloading)
213 { 234 {
214 let text = getMessage("options_filterList_lastDownload_inProgress"); 235 let text = getMessage("options_filterList_lastDownload_inProgress");
215 message.textContent = text; 236 message.textContent = text;
216 element.classList.add("show-message"); 237 element.classList.add("show-message");
217 } 238 }
218 else if (item.downloadStatus != "synchronize_ok") 239 else if (item.downloadStatus != "synchronize_ok")
219 { 240 {
220 let error = filterErrors.get(item.downloadStatus); 241 let error = filterErrors.get(item.downloadStatus);
221 if (error) 242 if (error)
222 message.textContent = getMessage(error); 243 message.textContent = getMessage(error);
223 else 244 else
224 message.textContent = item.downloadStatus; 245 message.textContent = item.downloadStatus;
225 element.classList.add("show-message"); 246 element.classList.add("show-message");
226 } 247 }
227 else if (item.lastDownload > 0) 248 else if (item.lastDownload > 0)
228 { 249 {
229 let timer = setInterval(lastUpdateLive, 60000);
230 let lastUpdate = item.lastDownload * 1000; 250 let lastUpdate = item.lastDownload * 1000;
231 function lastUpdateLive() 251 let sinceUpdate = Date.now() - lastUpdate;
252 if (sinceUpdate > fullDayInMs)
232 { 253 {
233 let sinceUpdate = Date.now() - lastUpdate; 254 let lastUpdateDate = new Date(item.lastDownload * 1000);
234 if(sinceUpdate > 86400000) 255 let monthName = lastUpdateDate.toLocaleString(undefined,
235 { 256 {month: "short"});
236 let lastUpdateDate = new Date(item.lastDownload * 1000); 257 let day = lastUpdateDate.getDate();
237 let monthName = lastUpdateDate.toLocaleString( 258 day = day < 10 ? "0" + day : day;
238 document.documentElement.lang, { month: "short" }); 259 lastUpdateElement.textContent = day + " " + monthName + " " +
239 let day = lastUpdateDate.getDate(); 260 lastUpdateDate.getFullYear();
240 day = day < 10 ? "0" + day : day;
241 lastUpdateElem.textContent = day + " " + monthName + " " +
242 lastUpdateDate.getFullYear();
243 clearInterval(timer);
244 return;
245 }
246 else if (sinceUpdate > 3600000)
247 {
248 lastUpdateElem.textContent = Math.round(sinceUpdate / 3600000) +
249 " " + getMessage("options_filterList_hours");
250 }
251 else if (sinceUpdate > 60000)
252 {
253 lastUpdateElem.textContent = Math.round(sinceUpdate / 60000) +
254 " " + getMessage("options_filterList_minutes");
255 }
256 else
257 {
258 lastUpdateElem.textContent =
259 getMessage("options_filterList_just");
260 }
261 } 261 }
262 lastUpdateLive(); 262 else if (sinceUpdate > hourInMs)
263 {
264 lastUpdateElement.textContent =
265 getMessage("options_filterList_hours");
266 }
267 else if (sinceUpdate > minuteInMs)
268 {
269 lastUpdateElement.textContent =
270 getMessage("options_filterList_minutes");
271 }
272 else
273 {
274 lastUpdateElement.textContent =
275 getMessage("options_filterList_now");
276 }
263 element.classList.remove("show-message"); 277 element.classList.remove("show-message");
264 } 278 }
265 } 279 }
266 280
267 let websiteElement = element.querySelector(".context-menu .website"); 281 let websiteElement = element.querySelector(".context-menu .website");
268 if (websiteElement) 282 if (websiteElement)
269 { 283 {
270 if (item.homepage) 284 if (item.homepage)
271 websiteElement.setAttribute("href", item.homepage); 285 websiteElement.setAttribute("href", item.homepage);
272 else 286 else
273 websiteElement.setAttribute("aria-hidden", true); 287 websiteElement.setAttribute("aria-hidden", true);
274 } 288 }
275 289
276 let sourceElement = element.querySelector(".context-menu .source"); 290 let sourceElement = element.querySelector(".context-menu .source");
277 if (sourceElement) 291 if (sourceElement)
278 sourceElement.setAttribute("href", item.url); 292 sourceElement.setAttribute("href", item.url);
293
294 let newIndex = this.items.indexOf(item);
295 if (oldIndex != newIndex)
296 table.insertBefore(element, table.childNodes[newIndex]);
279 } 297 }
280 }; 298 };
281 299
282 Collection.prototype.clearAll = function() 300 Collection.prototype.clearAll = function()
283 { 301 {
284 this.items = []; 302 this.items = [];
285 for (let detail of this.details) 303 for (let detail of this.details)
286 { 304 {
287 let table = E(detail.id); 305 let table = E(detail.id);
288 let element = table.firstChild; 306 let element = table.firstChild;
(...skipping 24 matching lines...) Expand all
313 } 331 }
314 332
315 collections.popular = new Collection([ 333 collections.popular = new Collection([
316 { 334 {
317 id: "recommend-list-table" 335 id: "recommend-list-table"
318 } 336 }
319 ]); 337 ]);
320 collections.langs = new Collection([ 338 collections.langs = new Collection([
321 { 339 {
322 id: "blocking-languages-table", 340 id: "blocking-languages-table",
323 emptyText: "options_dialog_language_added_empty" 341 emptyText: ["options_dialog_language_added_empty"]
324 }, 342 },
325 { 343 {
326 id: "blocking-languages-dialog-table", 344 id: "blocking-languages-dialog-table",
327 emptyText: "options_dialog_language_added_empty" 345 emptyText: ["options_dialog_language_added_empty"]
328 } 346 }
329 ]); 347 ]);
330 collections.allLangs = new Collection([ 348 collections.allLangs = new Collection([
331 { 349 {
332 id: "all-lang-table", 350 id: "all-lang-table",
333 emptyText: "options_dialog_language_other_empty", 351 emptyText: ["options_dialog_language_other_empty"],
334 searchable: true 352 searchable: true
335 } 353 }
336 ]); 354 ]);
337 collections.acceptableAds = new Collection([ 355 collections.acceptableAds = new Collection([
338 { 356 {
339 id: "acceptableads-table" 357 id: "acceptableads-table"
340 } 358 }
341 ]); 359 ]);
342 collections.custom = new Collection([ 360 collections.custom = new Collection([
343 { 361 {
344 id: "custom-list-table" 362 id: "custom-list-table"
345 } 363 }
346 ]); 364 ]);
347 collections.whitelist = new Collection([ 365 collections.whitelist = new Collection([
348 { 366 {
349 id: "whitelisting-table", 367 id: "whitelisting-table",
350 emptyText: "options_whitelisted_empty" 368 emptyText: ["options_whitelist_empty_1", "options_whitelist_empty_2"]
351 } 369 }
352 ]); 370 ]);
353 collections.filterLists = new Collection([ 371 collections.filterLists = new Collection([
354 { 372 {
355 id: "all-filter-lists-table", 373 id: "all-filter-lists-table",
356 useOriginalTitle: true, 374 useOriginalTitle: true
357 useHeader: true
358 } 375 }
359 ]); 376 ]);
360 377
361 function toggleShowLanguage(subscription) 378 function toggleShowLanguage(subscription)
362 { 379 {
363 if (subscription.recommended == "ads") 380 if (subscription.recommended == "ads")
364 { 381 {
365 if (subscription.disabled) 382 if (subscription.disabled)
366 { 383 {
367 collections.allLangs.addItem(subscription); 384 collections.allLangs.addItem(subscription);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 425
409 function updateFilter(filter) 426 function updateFilter(filter)
410 { 427 {
411 let match = filter.text.match(whitelistedDomainRegexp); 428 let match = filter.text.match(whitelistedDomainRegexp);
412 if (match && !filtersMap[filter.text]) 429 if (match && !filtersMap[filter.text])
413 { 430 {
414 filter.title = match[1]; 431 filter.title = match[1];
415 collections.whitelist.addItem(filter); 432 collections.whitelist.addItem(filter);
416 } 433 }
417 else 434 else
418 addCustomFilter(filter); 435 {
436 customFilters.push(filter.text);
437 if (isCustomFiltersLoaded)
438 updateCustomFiltersUi();
439 }
419 440
420 filtersMap[filter.text] = filter; 441 filtersMap[filter.text] = filter;
421 } 442 }
422 443
423 function addCustomFilter(filter)
424 {
425 customFiltersView("read");
426
427 E("custom-filters-box").appendChild(new Option(filter.text, filter.text));
428 }
429
430 function removeCustomFilter(text) 444 function removeCustomFilter(text)
431 { 445 {
432 let list = E("custom-filters-box"); 446 let index = customFilters.indexOf(text);
433 let selector = "option[value=" + CSS.escape(text) + "]"; 447 if (index >= 0)
434 for (let option of list.querySelectorAll(selector)) 448 customFilters.splice(index, 1);
435 list.removeChild(option); 449
436 450 updateCustomFiltersUi();
437 if (E("custom-filters-box").options.length == 0) 451 }
438 customFiltersView("empty") 452
439 } 453 function updateCustomFiltersUi()
440 454 {
441 function convertToRawFormat(event) 455 let customFiltersListElement = E("custom-filters-raw");
442 { 456 customFiltersListElement.value = customFilters.join("\n");
443 let filters = [];
444
445 for (let option of E("custom-filters-box").options)
446 filters.push(option.value);
447
448 document.getElementById("custom-filters-raw").value = filters.join("\n");
449 } 457 }
450 458
451 function loadRecommendations() 459 function loadRecommendations()
452 { 460 {
453 fetch("subscriptions.xml") 461 fetch("subscriptions.xml")
454 .then((response) => 462 .then((response) =>
455 { 463 {
456 return response.text(); 464 return response.text();
457 }) 465 })
458 .then((text) => 466 .then((text) =>
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
525 else 533 else
526 location.href = link; 534 location.href = link;
527 }); 535 });
528 } 536 }
529 537
530 function switchTab(id) 538 function switchTab(id)
531 { 539 {
532 location.hash = id; 540 location.hash = id;
533 } 541 }
534 542
535 function execAction(action, element, event) 543 function execAction(action, element)
536 { 544 {
537 if (element.getAttribute("href") == "#")
538 event.preventDefault();
539
540 switch (action) 545 switch (action)
541 { 546 {
542 case "add-domain-exception": 547 case "add-domain-exception":
543 addWhitelistedDomain(); 548 addWhitelistedDomain();
544 break; 549 break;
545 case "add-language-subscription": 550 case "add-language-subscription":
546 addEnableSubscription(findParentData(element, "access", false)); 551 addEnableSubscription(findParentData(element, "access", false));
547 break; 552 break;
548 case "add-predefined-subscription": { 553 case "add-predefined-subscription": {
549 let dialog = E("dialog-content-predefined"); 554 let dialog = E("dialog-content-predefined");
550 let title = dialog.querySelector("h3").textContent; 555 let title = dialog.querySelector("h3").textContent;
551 let url = dialog.querySelector(".url").textContent; 556 let url = dialog.querySelector(".url").textContent;
552 addEnableSubscription(url, title); 557 addEnableSubscription(url, title);
553 closeDialog(); 558 closeDialog();
554 break; 559 break;
555 } 560 }
556 case "cancel-custom-filters": 561 case "cancel-custom-filters":
557 customFiltersView("read"); 562 setCustomFiltersView("read");
558 break;
559 case "cancel-domain-exception":
560 E("whitelisting-textbox").value = "";
561 document.querySelector("#whitelisting .controls").classList
562 .remove("mode-edit");
563 break; 563 break;
564 case "close-dialog": 564 case "close-dialog":
565 closeDialog(); 565 closeDialog();
566 break; 566 break;
567 case "edit-custom-filters": 567 case "edit-custom-filters":
568 customFiltersView("write"); 568 setCustomFiltersView("write");
569 break;
570 case "edit-domain-exception":
571 document.querySelector("#whitelisting .controls").classList
572 .add("mode-edit");
573 E("whitelisting-textbox").focus();
574 break; 569 break;
575 case "import-subscription": { 570 case "import-subscription": {
576 let url = E("blockingList-textbox").value; 571 let url = E("blockingList-textbox").value;
577 addEnableSubscription(url); 572 addEnableSubscription(url);
578 closeDialog(); 573 closeDialog();
579 break; 574 break;
580 } 575 }
581 case "open-context-menu": { 576 case "open-context-menu": {
582 let listItem = findParentData(element, "access", true); 577 let listItem = findParentData(element, "access", true);
583 if (listItem && !listItem.classList.contains("show-context-menu")) 578 if (listItem && !listItem.classList.contains("show-context-menu"))
(...skipping 23 matching lines...) Expand all
607 }); 602 });
608 break; 603 break;
609 case "save-custom-filters": 604 case "save-custom-filters":
610 sendMessageHandleErrors({ 605 sendMessageHandleErrors({
611 type: "filters.importRaw", 606 type: "filters.importRaw",
612 text: E("custom-filters-raw").value, 607 text: E("custom-filters-raw").value,
613 removeExisting: true 608 removeExisting: true
614 }, 609 },
615 () => 610 () =>
616 { 611 {
617 customFiltersView("read"); 612 setCustomFiltersView("read");
618 }); 613 });
619 break; 614 break;
620 case "switch-tab": 615 case "switch-tab":
621 let tabId = findParentData(element, "tab", false); 616 let tabId = findParentData(element, "tab", false);
622 switchTab(tabId); 617 switchTab(tabId);
623 break; 618 break;
624 case "toggle-disable-subscription": 619 case "toggle-disable-subscription":
625 ext.backgroundPage.sendMessage({ 620 ext.backgroundPage.sendMessage({
626 type: "subscriptions.toggle", 621 type: "subscriptions.toggle",
627 keepInstalled: true, 622 keepInstalled: true,
(...skipping 25 matching lines...) Expand all
653 break; 648 break;
654 case "update-subscription": 649 case "update-subscription":
655 ext.backgroundPage.sendMessage({ 650 ext.backgroundPage.sendMessage({
656 type: "subscriptions.update", 651 type: "subscriptions.update",
657 url: findParentData(element, "access", false) 652 url: findParentData(element, "access", false)
658 }); 653 });
659 break; 654 break;
660 } 655 }
661 } 656 }
662 657
663 function customFiltersView(mode) 658 function setCustomFiltersView(mode)
664 { 659 {
665 switch (mode) 660 let customFiltersElement = E("custom-filters-raw");
666 { 661 updateCustomFiltersUi();
667 case "read": 662 if (mode == "read")
668 if (E("custom-filters-box").options.length == 0) 663 {
669 { 664 customFiltersElement.disabled = true;
670 customFiltersView("empty"); 665 if (!customFiltersElement.value)
671 } 666 {
672 else 667 setCustomFiltersView("empty");
673 { 668 return;
674 E("custom-filters").classList.add("mode-view"); 669 }
675 E("custom-filters").classList.remove("mode-edit"); 670 }
676 E("custom-filters").classList.remove("mode-empty"); 671 else if (mode == "write")
677 } 672 {
678 break; 673 customFiltersElement.disabled = false;
679 case "write": 674 }
680 convertToRawFormat(); 675
681 E("custom-filters").classList.remove("mode-view"); 676 E("custom-filters").dataset.mode = mode;
682 E("custom-filters").classList.add("mode-edit");
683 E("custom-filters").classList.remove("mode-empty");
684 break;
685 case "empty":
686 E("custom-filters").classList.remove("mode-view");
687 E("custom-filters").classList.remove("mode-edit");
688 E("custom-filters").classList.add("mode-empty");
689 break;
690 }
691 } 677 }
692 678
693 function onClick(e) 679 function onClick(e)
694 { 680 {
695 let context = document.querySelector(".show-context-menu"); 681 let context = document.querySelector(".show-context-menu");
696 if (context) 682 if (context)
697 context.classList.remove("show-context-menu"); 683 context.classList.remove("show-context-menu");
698 684
699 let actions = findParentData(e.target, "action", false); 685 let actions = findParentData(e.target, "action", false);
700 if (!actions) 686 if (!actions)
701 return; 687 return;
702 688
703 actions = actions.split(","); 689 actions = actions.split(",");
704 for (let action of actions) 690 for (let action of actions)
705 { 691 {
706 execAction(action, e.target, e); 692 execAction(action, e.target);
707 } 693 }
708 } 694 }
709 695
710 function getKey(e) 696 function getKey(e)
711 { 697 {
712 // e.keyCode has been deprecated so we attempt to use e.key 698 // e.keyCode has been deprecated so we attempt to use e.key
713 if ("key" in e) 699 if ("key" in e)
714 return e.key; 700 return e.key;
715 return getKey.keys[e.keyCode]; 701 return getKey.keys[e.keyCode];
716 } 702 }
(...skipping 26 matching lines...) Expand all
743 { 729 {
744 if (key == "ArrowLeft" || key == "ArrowUp") 730 if (key == "ArrowLeft" || key == "ArrowUp")
745 element = element.previousElementSibling || container.lastElementChild; 731 element = element.previousElementSibling || container.lastElementChild;
746 else if (key == "ArrowRight" || key == "ArrowDown") 732 else if (key == "ArrowRight" || key == "ArrowDown")
747 element = element.nextElementSibling || container.firstElementChild; 733 element = element.nextElementSibling || container.firstElementChild;
748 } 734 }
749 735
750 let actions = container.getAttribute("data-action").split(","); 736 let actions = container.getAttribute("data-action").split(",");
751 for (let action of actions) 737 for (let action of actions)
752 { 738 {
753 execAction(action, element, e); 739 execAction(action, element);
754 } 740 }
755 } 741 }
756 742
757 function selectTabItem(tabId, container, focus) 743 function selectTabItem(tabId, container, focus)
758 { 744 {
759 // Show tab content 745 // Show tab content
760 document.body.setAttribute("data-tab", tabId); 746 document.body.setAttribute("data-tab", tabId);
761 747
762 // Select tab 748 // Select tab
763 let tabList = container.querySelector("[role='tablist']"); 749 let tabList = container.querySelector("[role='tablist']");
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
830 816
831 updateShareLink(); 817 updateShareLink();
832 updateTooltips(); 818 updateTooltips();
833 819
834 // Initialize interactive UI elements 820 // Initialize interactive UI elements
835 document.body.addEventListener("click", onClick, false); 821 document.body.addEventListener("click", onClick, false);
836 document.body.addEventListener("keyup", onKeyUp, false); 822 document.body.addEventListener("keyup", onKeyUp, false);
837 let placeholderValue = getMessage("options_dialog_language_find"); 823 let placeholderValue = getMessage("options_dialog_language_find");
838 E("find-language").setAttribute("placeholder", placeholderValue); 824 E("find-language").setAttribute("placeholder", placeholderValue);
839 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); 825 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false);
840 E("whitelisting-textbox").addEventListener("keypress", (e) => 826 let exampleValue = getMessage("options_whitelist_placeholder_example",
841 { 827 ["www.example.com"]);
842 if (getKey(e) == "Enter") 828 E("whitelisting-textbox").setAttribute("placeholder", exampleValue);
843 addWhitelistedDomain(); 829 E("whitelisting-textbox").addEventListener("keyup", (e) =>
830 {
831 E("whitelisting-add-button").disabled = !e.target.value;
844 }, false); 832 }, false);
845 833
846 // Advanced tab 834 // Advanced tab
847 let customize = document.querySelectorAll("#customize li[data-pref]"); 835 let customize = document.querySelectorAll("#customize li[data-pref]");
848 customize = Array.prototype.map.call(customize, (checkbox) => 836 customize = Array.prototype.map.call(customize, (checkbox) =>
849 { 837 {
850 return checkbox.getAttribute("data-pref"); 838 return checkbox.getAttribute("data-pref");
851 }); 839 });
852 for (let key of customize) 840 for (let key of customize)
853 { 841 {
(...skipping 13 matching lines...) Expand all
867 855
868 getDocLink("filterdoc", (link) => 856 getDocLink("filterdoc", (link) =>
869 { 857 {
870 E("link-filters").setAttribute("href", link); 858 E("link-filters").setAttribute("href", link);
871 }); 859 });
872 860
873 getDocLink("subscriptions", (link) => 861 getDocLink("subscriptions", (link) =>
874 { 862 {
875 setLinks("filter-lists-description", link); 863 setLinks("filter-lists-description", link);
876 }); 864 });
865
866 E("custom-filters-raw").setAttribute("placeholder",
867 getMessage("options_customFilters_edit_placeholder", ["/ads/track/*"]));
877 868
878 // Help tab 869 // Help tab
879 getDocLink("faq", (link) => 870 getDocLink("faq", (link) =>
880 { 871 {
881 E("link-faq").setAttribute("href", link); 872 E("link-faq").setAttribute("href", link);
882 }); 873 });
883 getDocLink("social_twitter", (link) => 874 getDocLink("social_twitter", (link) =>
884 { 875 {
885 E("link-twitter").setAttribute("href", link); 876 E("link-twitter").setAttribute("href", link);
886 }); 877 });
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
999 ext.backgroundPage.sendMessage({ 990 ext.backgroundPage.sendMessage({
1000 type: "filters.get", 991 type: "filters.get",
1001 subscriptionUrl: subscription.url 992 subscriptionUrl: subscription.url
1002 }, 993 },
1003 (filters) => 994 (filters) =>
1004 { 995 {
1005 for (let filter of filters) 996 for (let filter of filters)
1006 updateFilter(filter); 997 updateFilter(filter);
1007 998
1008 isCustomFiltersLoaded = true; 999 isCustomFiltersLoaded = true;
1000 setCustomFiltersView("read");
1009 }); 1001 });
1010 } 1002 }
1011 }); 1003 });
1012 loadRecommendations(); 1004 loadRecommendations();
1013 ext.backgroundPage.sendMessage({ 1005 ext.backgroundPage.sendMessage({
1014 type: "prefs.get", 1006 type: "prefs.get",
1015 key: "subscriptions_exceptionsurl" 1007 key: "subscriptions_exceptionsurl"
1016 }, 1008 },
1017 (url) => 1009 (url) =>
1018 { 1010 {
(...skipping 12 matching lines...) Expand all
1031 { 1023 {
1032 for (let subscription of subscriptions) 1024 for (let subscription of subscriptions)
1033 onSubscriptionMessage("added", subscription); 1025 onSubscriptionMessage("added", subscription);
1034 }); 1026 });
1035 }); 1027 });
1036 } 1028 }
1037 1029
1038 function addWhitelistedDomain() 1030 function addWhitelistedDomain()
1039 { 1031 {
1040 let domain = E("whitelisting-textbox"); 1032 let domain = E("whitelisting-textbox");
1033 for (let whitelistItem of collections.whitelist.items)
1034 {
1035 if (whitelistItem.title == domain.value)
1036 {
1037 whitelistItem[timestampUI] = Date.now();
1038 collections.whitelist.updateItem(whitelistItem);
1039 domain.value = "";
1040 break;
1041 }
1042 }
1041 if (domain.value) 1043 if (domain.value)
1042 { 1044 {
1043 sendMessageHandleErrors({ 1045 sendMessageHandleErrors({
1044 type: "filters.add", 1046 type: "filters.add",
1045 text: "@@||" + domain.value.toLowerCase() + "^$document" 1047 text: "@@||" + domain.value.toLowerCase() + "^$document"
1046 }); 1048 });
1047 } 1049 }
1048 1050
1049 domain.value = ""; 1051 domain.value = "";
1050 document.querySelector("#whitelisting .controls") 1052 E("whitelisting-add-button").disabled = true;
1051 .classList.remove("mode-edit");
1052 }
1053
1054 function editCustomFilters()
1055 {
1056 if (!isCustomFiltersLoaded)
1057 {
1058 console.error("Custom filters are not loaded");
1059 return;
1060 }
1061
1062 E("custom-filters").classList.add("mode-edit");
1063 let filterTexts = [];
1064 for (let customFilterItem of collections.customFilters.items)
1065 filterTexts.push(customFilterItem.text);
1066 E("custom-filters-raw").value = filterTexts.join("\n");
1067 } 1053 }
1068 1054
1069 function addEnableSubscription(url, title, homepage) 1055 function addEnableSubscription(url, title, homepage)
1070 { 1056 {
1071 let messageType = null; 1057 let messageType = null;
1072 let knownSubscription = subscriptionsMap[url]; 1058 let knownSubscription = subscriptionsMap[url];
1073 if (knownSubscription && knownSubscription.disabled == true) 1059 if (knownSubscription && knownSubscription.disabled == true)
1074 messageType = "subscriptions.toggle"; 1060 messageType = "subscriptions.toggle";
1075 else 1061 else
1076 messageType = "subscriptions.add"; 1062 messageType = "subscriptions.add";
1077 1063
1078 let message = { 1064 let message = {
1079 type: messageType, 1065 type: messageType,
1080 url 1066 url
1081 }; 1067 };
1082 if (title) 1068 if (title)
1083 message.title = title; 1069 message.title = title;
1084 if (homepage) 1070 if (homepage)
1085 message.homepage = homepage; 1071 message.homepage = homepage;
1086 1072
1087 ext.backgroundPage.sendMessage(message); 1073 ext.backgroundPage.sendMessage(message);
1088 } 1074 }
1089 1075
1090 function onFilterMessage(action, filter) 1076 function onFilterMessage(action, filter)
1091 { 1077 {
1092 switch (action) 1078 switch (action)
1093 { 1079 {
1094 case "added": 1080 case "added":
1081 filter[timestampUI] = Date.now();
1095 updateFilter(filter); 1082 updateFilter(filter);
1096 updateShareLink(); 1083 updateShareLink();
1097 break; 1084 break;
1098 case "loaded": 1085 case "loaded":
1099 populateLists(); 1086 populateLists();
1100 break; 1087 break;
1101 case "removed": 1088 case "removed":
1102 let knownFilter = filtersMap[filter.text]; 1089 let knownFilter = filtersMap[filter.text];
1103 if (whitelistedDomainRegexp.test(knownFilter.text)) 1090 if (whitelistedDomainRegexp.test(knownFilter.text))
1104 collections.whitelist.removeItem(knownFilter); 1091 collections.whitelist.removeItem(knownFilter);
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
1368 }); 1355 });
1369 ext.backgroundPage.sendMessage({ 1356 ext.backgroundPage.sendMessage({
1370 type: "subscriptions.listen", 1357 type: "subscriptions.listen",
1371 filter: ["added", "disabled", "homepage", "lastDownload", "removed", 1358 filter: ["added", "disabled", "homepage", "lastDownload", "removed",
1372 "title", "downloadStatus", "downloading"] 1359 "title", "downloadStatus", "downloading"]
1373 }); 1360 });
1374 1361
1375 window.addEventListener("DOMContentLoaded", onDOMLoaded, false); 1362 window.addEventListener("DOMContentLoaded", onDOMLoaded, false);
1376 window.addEventListener("hashchange", onHashChange, false); 1363 window.addEventListener("hashchange", onHashChange, false);
1377 } 1364 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld