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: HTML, Strings and functionality Created June 11, 2017, 3:22 p.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)
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("en-US", 258 day = day < 10 ? "0" + day : day;
saroyanm 2017/06/11 15:40:12 For reference: Make sense to find a way to pass th
saroyanm 2017/06/12 16:44:42 Create a function in i18n, that returns month: "sh
saroyanm 2017/06/14 10:59:00 I used different approach while we do have the val
Thomas Greiner 2017/07/07 13:01:07 This looks like a race condition because the "lang
238 { 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 + " " +
saroyanm 2017/06/12 16:44:42 Note: Make sense to discuss if we need to localize
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 // Note: document.createElement("option") is unreliable in Opera
saroyanm 2017/06/11 15:40:12 I'm not sure if this still true, I took this from
saroyanm 2017/06/12 16:44:42 Check if it's still an issue in the supported Oper
saroyanm 2017/06/14 10:59:00 Done. This was added back in 2012 -> https://hg.a
Thomas Greiner 2017/07/07 13:01:08 Acknowledged.
428 let elt = new Option();
429 elt.text = filter.text;
430 elt.value = filter.text;
431 E("custom-filters-box").appendChild(elt);
432 }
433
434 function removeCustomFilter(text) 444 function removeCustomFilter(text)
435 { 445 {
436 let list = E("custom-filters-box"); 446 let index = customFilters.indexOf(text);
437 let selector = "option[value=" + CSS.escape(text) + "]"; 447 if (index >= 0)
438 for (let option of list.querySelectorAll(selector)) 448 customFilters.splice(index, 1);
439 list.removeChild(option); 449
440 450 updateCustomFiltersUi();
441 if (E("custom-filters-box").options.length == 0) 451 }
442 customFiltersView("empty") 452
443 } 453 function updateCustomFiltersUi()
444 454 {
445 function convertToRawFormat(event) 455 let customFiltersListElement = E("custom-filters-raw");
446 { 456 customFiltersListElement.value = customFilters.join("\n");
447 let filters = [];
448
449 for (let option of E("custom-filters-box").options)
450 filters.push(option.value);
451
452 document.getElementById("custom-filters-raw").value = filters.join("\n");
453 } 457 }
454 458
455 function loadRecommendations() 459 function loadRecommendations()
456 { 460 {
457 fetch("subscriptions.xml") 461 fetch("subscriptions.xml")
458 .then((response) => 462 .then((response) =>
459 { 463 {
460 return response.text(); 464 return response.text();
461 }) 465 })
462 .then((text) => 466 .then((text) =>
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
548 break; 552 break;
549 case "add-predefined-subscription": { 553 case "add-predefined-subscription": {
550 let dialog = E("dialog-content-predefined"); 554 let dialog = E("dialog-content-predefined");
551 let title = dialog.querySelector("h3").textContent; 555 let title = dialog.querySelector("h3").textContent;
552 let url = dialog.querySelector(".url").textContent; 556 let url = dialog.querySelector(".url").textContent;
553 addEnableSubscription(url, title); 557 addEnableSubscription(url, title);
554 closeDialog(); 558 closeDialog();
555 break; 559 break;
556 } 560 }
557 case "cancel-custom-filters": 561 case "cancel-custom-filters":
558 customFiltersView("read"); 562 setCustomFiltersView("read");
559 break;
560 case "cancel-domain-exception":
561 E("whitelisting-textbox").value = "";
562 document.querySelector("#whitelisting .controls").classList
563 .remove("mode-edit");
564 break; 563 break;
565 case "close-dialog": 564 case "close-dialog":
566 closeDialog(); 565 closeDialog();
567 break; 566 break;
568 case "edit-custom-filters": 567 case "edit-custom-filters":
569 customFiltersView("write"); 568 setCustomFiltersView("write");
570 break;
571 case "edit-domain-exception":
572 document.querySelector("#whitelisting .controls").classList
573 .add("mode-edit");
574 E("whitelisting-textbox").focus();
575 break; 569 break;
576 case "import-subscription": { 570 case "import-subscription": {
577 let url = E("blockingList-textbox").value; 571 let url = E("blockingList-textbox").value;
578 addEnableSubscription(url); 572 addEnableSubscription(url);
579 closeDialog(); 573 closeDialog();
580 break; 574 break;
581 } 575 }
582 case "open-context-menu": { 576 case "open-context-menu": {
583 let listItem = findParentData(element, "access", true); 577 let listItem = findParentData(element, "access", true);
584 if (listItem && !listItem.classList.contains("show-context-menu")) 578 if (listItem && !listItem.classList.contains("show-context-menu"))
(...skipping 16 matching lines...) Expand all
601 text: findParentData(element, "access", false) 595 text: findParentData(element, "access", false)
602 }); 596 });
603 break; 597 break;
604 case "remove-subscription": 598 case "remove-subscription":
605 ext.backgroundPage.sendMessage({ 599 ext.backgroundPage.sendMessage({
606 type: "subscriptions.remove", 600 type: "subscriptions.remove",
607 url: findParentData(element, "access", false) 601 url: findParentData(element, "access", false)
608 }); 602 });
609 break; 603 break;
610 case "save-custom-filters": 604 case "save-custom-filters":
611 sendMessageHandleErrors({ 605 sendMessageHandleErrors({
saroyanm 2017/06/11 15:40:12 I still need to figure out how to get exact filter
saroyanm 2017/06/12 16:44:42 Separate this into another review.
Thomas Greiner 2017/07/07 13:01:08 Acknowledged.
612 type: "filters.importRaw", 606 type: "filters.importRaw",
613 text: E("custom-filters-raw").value, 607 text: E("custom-filters-raw").value,
614 removeExisting: true 608 removeExisting: true
615 }, 609 },
616 () => 610 () =>
617 { 611 {
618 customFiltersView("read"); 612 setCustomFiltersView("read");
619 }); 613 });
620 break; 614 break;
621 case "switch-tab": 615 case "switch-tab":
622 let tabId = findParentData(element, "tab", false); 616 let tabId = findParentData(element, "tab", false);
623 switchTab(tabId); 617 switchTab(tabId);
624 break; 618 break;
625 case "toggle-disable-subscription": 619 case "toggle-disable-subscription":
626 ext.backgroundPage.sendMessage({ 620 ext.backgroundPage.sendMessage({
627 type: "subscriptions.toggle", 621 type: "subscriptions.toggle",
628 keepInstalled: true, 622 keepInstalled: true,
(...skipping 25 matching lines...) Expand all
654 break; 648 break;
655 case "update-subscription": 649 case "update-subscription":
656 ext.backgroundPage.sendMessage({ 650 ext.backgroundPage.sendMessage({
657 type: "subscriptions.update", 651 type: "subscriptions.update",
658 url: findParentData(element, "access", false) 652 url: findParentData(element, "access", false)
659 }); 653 });
660 break; 654 break;
661 } 655 }
662 } 656 }
663 657
664 function customFiltersView(mode) 658 function setCustomFiltersView(mode)
665 { 659 {
666 switch (mode) 660 let customFiltersElement = E("custom-filters-raw");
667 { 661 updateCustomFiltersUi();
668 case "read": 662 if (mode == "read")
669 if (E("custom-filters-box").options.length == 0) 663 {
670 { 664 customFiltersElement.disabled = true;
671 customFiltersView("empty"); 665 if (!customFiltersElement.value)
672 } 666 {
673 else 667 setCustomFiltersView("empty");
674 { 668 return;
675 E("custom-filters").classList.add("mode-view"); 669 }
676 E("custom-filters").classList.remove("mode-edit"); 670 }
677 E("custom-filters").classList.remove("mode-empty"); 671 else if (mode == "write")
678 } 672 {
679 break; 673 customFiltersElement.disabled = false;
680 case "write": 674 }
681 convertToRawFormat(); 675
682 E("custom-filters").classList.remove("mode-view"); 676 E("custom-filters").dataset.mode = mode;
683 E("custom-filters").classList.add("mode-edit");
684 E("custom-filters").classList.remove("mode-empty");
685 break;
686 case "empty":
687 E("custom-filters").classList.remove("mode-view");
688 E("custom-filters").classList.remove("mode-edit");
689 E("custom-filters").classList.add("mode-empty");
690 break;
691 }
692 } 677 }
693 678
694 function onClick(e) 679 function onClick(e)
695 { 680 {
696 let context = document.querySelector(".show-context-menu"); 681 let context = document.querySelector(".show-context-menu");
697 if (context) 682 if (context)
698 context.classList.remove("show-context-menu"); 683 context.classList.remove("show-context-menu");
699 684
700 let actions = findParentData(e.target, "action", false); 685 let actions = findParentData(e.target, "action", false);
701 if (!actions) 686 if (!actions)
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
831 816
832 updateShareLink(); 817 updateShareLink();
833 updateTooltips(); 818 updateTooltips();
834 819
835 // Initialize interactive UI elements 820 // Initialize interactive UI elements
836 document.body.addEventListener("click", onClick, false); 821 document.body.addEventListener("click", onClick, false);
837 document.body.addEventListener("keyup", onKeyUp, false); 822 document.body.addEventListener("keyup", onKeyUp, false);
838 let placeholderValue = getMessage("options_dialog_language_find"); 823 let placeholderValue = getMessage("options_dialog_language_find");
839 E("find-language").setAttribute("placeholder", placeholderValue); 824 E("find-language").setAttribute("placeholder", placeholderValue);
840 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); 825 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false);
841 E("whitelisting-textbox").addEventListener("keypress", (e) => 826 let exampleValue = getMessage("options_whitelist_placeholder_example",
842 { 827 ["www.example.com"]);
843 if (getKey(e) == "Enter") 828 E("whitelisting-textbox").setAttribute("placeholder", exampleValue);
844 addWhitelistedDomain(); 829 E("whitelisting-textbox").addEventListener("keyup", (e) =>
830 {
831 E("whitelisting-add-button").disabled = !e.target.value;
845 }, false); 832 }, false);
846 833
847 // Advanced tab 834 // Advanced tab
848 let customize = document.querySelectorAll("#customize li[data-pref]"); 835 let customize = document.querySelectorAll("#customize li[data-pref]");
849 customize = Array.prototype.map.call(customize, (checkbox) => 836 customize = Array.prototype.map.call(customize, (checkbox) =>
850 { 837 {
851 return checkbox.getAttribute("data-pref"); 838 return checkbox.getAttribute("data-pref");
852 }); 839 });
853 for (let key of customize) 840 for (let key of customize)
854 { 841 {
855 getPref(key, (value) => 842 getPref(key, (value) =>
856 { 843 {
857 onPrefMessage(key, value, true); 844 onPrefMessage(key, value, true);
858 }); 845 });
859 } 846 }
860 ext.backgroundPage.sendMessage({ 847 ext.backgroundPage.sendMessage({
861 type: "app.get", 848 type: "app.get",
862 what: "features" 849 what: "features"
863 }, 850 },
864 (features) => 851 (features) =>
865 { 852 {
866 hidePref("show_devtools_panel", !features.devToolsPanel); 853 hidePref("show_devtools_panel", !features.devToolsPanel);
867 }); 854 });
868 855
869 getDocLink("filterdoc", (link) => 856 getDocLink("filterdoc", (link) =>
870 { 857 {
871 E("link-filters").setAttribute("href", link); 858 E("link-filters").setAttribute("href", link);
872 }); 859 });
860
861 getDocLink("subscriptions", (link) =>
862 {
863 setLinks("filter-lists-description", link);
864 });
865
866 E("custom-filters-raw").setAttribute("placeholder",
867 getMessage("options_customFilters_edit_placeholder", ["/ads/track/*"]));
873 868
874 // Help tab 869 // Help tab
875 getDocLink("faq", (link) => 870 getDocLink("faq", (link) =>
876 { 871 {
877 E("link-faq").setAttribute("href", link); 872 E("link-faq").setAttribute("href", link);
878 }); 873 });
879 getDocLink("social_twitter", (link) => 874 getDocLink("social_twitter", (link) =>
880 { 875 {
881 E("link-twitter").setAttribute("href", link); 876 E("link-twitter").setAttribute("href", link);
882 }); 877 });
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
995 ext.backgroundPage.sendMessage({ 990 ext.backgroundPage.sendMessage({
996 type: "filters.get", 991 type: "filters.get",
997 subscriptionUrl: subscription.url 992 subscriptionUrl: subscription.url
998 }, 993 },
999 (filters) => 994 (filters) =>
1000 { 995 {
1001 for (let filter of filters) 996 for (let filter of filters)
1002 updateFilter(filter); 997 updateFilter(filter);
1003 998
1004 isCustomFiltersLoaded = true; 999 isCustomFiltersLoaded = true;
1000 setCustomFiltersView("read");
1005 }); 1001 });
1006 } 1002 }
1007 }); 1003 });
1008 loadRecommendations(); 1004 loadRecommendations();
1009 ext.backgroundPage.sendMessage({ 1005 ext.backgroundPage.sendMessage({
1010 type: "prefs.get", 1006 type: "prefs.get",
1011 key: "subscriptions_exceptionsurl" 1007 key: "subscriptions_exceptionsurl"
1012 }, 1008 },
1013 (url) => 1009 (url) =>
1014 { 1010 {
(...skipping 12 matching lines...) Expand all
1027 { 1023 {
1028 for (let subscription of subscriptions) 1024 for (let subscription of subscriptions)
1029 onSubscriptionMessage("added", subscription); 1025 onSubscriptionMessage("added", subscription);
1030 }); 1026 });
1031 }); 1027 });
1032 } 1028 }
1033 1029
1034 function addWhitelistedDomain() 1030 function addWhitelistedDomain()
1035 { 1031 {
1036 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 }
1037 if (domain.value) 1043 if (domain.value)
1038 { 1044 {
1039 sendMessageHandleErrors({ 1045 sendMessageHandleErrors({
1040 type: "filters.add", 1046 type: "filters.add",
1041 text: "@@||" + domain.value.toLowerCase() + "^$document" 1047 text: "@@||" + domain.value.toLowerCase() + "^$document"
1042 }); 1048 });
1043 } 1049 }
1044 1050
1045 domain.value = ""; 1051 domain.value = "";
1046 document.querySelector("#whitelisting .controls") 1052 E("whitelisting-add-button").disabled = true;
1047 .classList.remove("mode-edit");
1048 }
1049
1050 function editCustomFilters()
1051 {
1052 if (!isCustomFiltersLoaded)
1053 {
1054 console.error("Custom filters are not loaded");
1055 return;
1056 }
1057
1058 E("custom-filters").classList.add("mode-edit");
1059 let filterTexts = [];
1060 for (let customFilterItem of collections.customFilters.items)
1061 filterTexts.push(customFilterItem.text);
1062 E("custom-filters-raw").value = filterTexts.join("\n");
1063 } 1053 }
1064 1054
1065 function addEnableSubscription(url, title, homepage) 1055 function addEnableSubscription(url, title, homepage)
1066 { 1056 {
1067 let messageType = null; 1057 let messageType = null;
1068 let knownSubscription = subscriptionsMap[url]; 1058 let knownSubscription = subscriptionsMap[url];
1069 if (knownSubscription && knownSubscription.disabled == true) 1059 if (knownSubscription && knownSubscription.disabled == true)
1070 messageType = "subscriptions.toggle"; 1060 messageType = "subscriptions.toggle";
1071 else 1061 else
1072 messageType = "subscriptions.add"; 1062 messageType = "subscriptions.add";
1073 1063
1074 let message = { 1064 let message = {
1075 type: messageType, 1065 type: messageType,
1076 url 1066 url
1077 }; 1067 };
1078 if (title) 1068 if (title)
1079 message.title = title; 1069 message.title = title;
1080 if (homepage) 1070 if (homepage)
1081 message.homepage = homepage; 1071 message.homepage = homepage;
1082 1072
1083 ext.backgroundPage.sendMessage(message); 1073 ext.backgroundPage.sendMessage(message);
1084 } 1074 }
1085 1075
1086 function onFilterMessage(action, filter) 1076 function onFilterMessage(action, filter)
1087 { 1077 {
1088 switch (action) 1078 switch (action)
1089 { 1079 {
1090 case "added": 1080 case "added":
1081 filter[timestampUI] = Date.now();
1091 updateFilter(filter); 1082 updateFilter(filter);
1092 updateShareLink(); 1083 updateShareLink();
1093 break; 1084 break;
1094 case "loaded": 1085 case "loaded":
1095 populateLists(); 1086 populateLists();
1096 break; 1087 break;
1097 case "removed": 1088 case "removed":
1098 let knownFilter = filtersMap[filter.text]; 1089 let knownFilter = filtersMap[filter.text];
1099 if (whitelistedDomainRegexp.test(knownFilter.text)) 1090 if (whitelistedDomainRegexp.test(knownFilter.text))
1100 collections.whitelist.removeItem(knownFilter); 1091 collections.whitelist.removeItem(knownFilter);
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
1364 }); 1355 });
1365 ext.backgroundPage.sendMessage({ 1356 ext.backgroundPage.sendMessage({
1366 type: "subscriptions.listen", 1357 type: "subscriptions.listen",
1367 filter: ["added", "disabled", "homepage", "lastDownload", "removed", 1358 filter: ["added", "disabled", "homepage", "lastDownload", "removed",
1368 "title", "downloadStatus", "downloading"] 1359 "title", "downloadStatus", "downloading"]
1369 }); 1360 });
1370 1361
1371 window.addEventListener("DOMContentLoaded", onDOMLoaded, false); 1362 window.addEventListener("DOMContentLoaded", onDOMLoaded, false);
1372 window.addEventListener("hashchange", onHashChange, false); 1363 window.addEventListener("hashchange", onHashChange, false);
1373 } 1364 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld