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

Side by Side Diff: new-options.js

Issue 29411555: Issue 5169 - Add whitelisted tab to the new options page (Closed)
Patch Set: Move new domains to top, implicit submission and comments Created June 16, 2017, 4:15 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « new-options.html ('k') | skin/new-options.css » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-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
(...skipping 19 matching lines...) Expand all
30 let filterErrors = new Map([ 30 let filterErrors = new Map([
31 ["synchronize_invalid_url", 31 ["synchronize_invalid_url",
32 "options_filterList_lastDownload_invalidURL"], 32 "options_filterList_lastDownload_invalidURL"],
33 ["synchronize_connection_error", 33 ["synchronize_connection_error",
34 "options_filterList_lastDownload_connectionError"], 34 "options_filterList_lastDownload_connectionError"],
35 ["synchronize_invalid_data", 35 ["synchronize_invalid_data",
36 "options_filterList_lastDownload_invalidData"], 36 "options_filterList_lastDownload_invalidData"],
37 ["synchronize_checksum_mismatch", 37 ["synchronize_checksum_mismatch",
38 "options_filterList_lastDownload_checksumMismatch"] 38 "options_filterList_lastDownload_checksumMismatch"]
39 ]); 39 ]);
40 const timestampUI = Symbol();
40 41
41 function Collection(details) 42 function Collection(details)
42 { 43 {
43 this.details = details; 44 this.details = details;
44 this.items = []; 45 this.items = [];
45 } 46 }
46 47
47 Collection.prototype._setEmpty = function(table, text) 48 Collection.prototype._setEmpty = function(table, texts)
48 { 49 {
49 let placeholder = table.querySelector(".empty-placeholder"); 50 let placeholders = table.querySelectorAll(".empty-placeholder");
50 if (text && !placeholder) 51
52 if (texts && placeholders.length == 0)
51 { 53 {
52 placeholder = document.createElement("li"); 54 for (let text of texts)
53 placeholder.className = "empty-placeholder"; 55 {
54 placeholder.textContent = getMessage(text); 56 let placeholder = document.createElement("li");
55 table.appendChild(placeholder); 57 placeholder.className = "empty-placeholder";
58 placeholder.textContent = getMessage(text);
59 table.appendChild(placeholder);
60 }
56 } 61 }
57 else if (placeholder) 62 else if (placeholders.length > 0)
58 table.removeChild(placeholder); 63 {
64 for (let placeholder of placeholders)
65 table.removeChild(placeholder);
66 }
59 }; 67 };
60 68
61 Collection.prototype._createElementQuery = function(item) 69 Collection.prototype._createElementQuery = function(item)
62 { 70 {
63 let access = (item.url || item.text).replace(/'/g, "\\'"); 71 let access = (item.url || item.text).replace(/'/g, "\\'");
64 return function(container) 72 return function(container)
65 { 73 {
66 return container.querySelector("[data-access='" + access + "']"); 74 return container.querySelector("[data-access='" + access + "']");
67 }; 75 };
68 }; 76 };
69 77
70 Collection.prototype._getItemTitle = function(item, i) 78 Collection.prototype._getItemTitle = function(item, i)
71 { 79 {
72 if (item.url == acceptableAdsUrl) 80 if (item.url == acceptableAdsUrl)
73 return getMessage("options_acceptableAds_description"); 81 return getMessage("options_acceptableAds_description");
74 if (this.details[i].useOriginalTitle && item.originalTitle) 82 if (this.details[i].useOriginalTitle && item.originalTitle)
75 return item.originalTitle; 83 return item.originalTitle;
76 return item.title || item.url || item.text; 84 return item.title || item.url || item.text;
77 }; 85 };
78 86
79 Collection.prototype.addItem = function(item) 87 Collection.prototype._sortItems = function()
80 { 88 {
81 if (this.items.indexOf(item) >= 0)
82 return;
83
84 this.items.push(item);
85 this.items.sort((a, b) => 89 this.items.sort((a, b) =>
86 { 90 {
87 // Make sure that Acceptable Ads is always last, since it cannot be 91 // Make sure that Acceptable Ads is always last, since it cannot be
88 // disabled, but only be removed. That way it's grouped together with 92 // disabled, but only be removed. That way it's grouped together with
89 // the "Own filter list" which cannot be disabled either at the bottom 93 // the "Own filter list" which cannot be disabled either at the bottom
90 // of the filter lists in the Advanced tab. 94 // of the filter lists in the Advanced tab.
91 if (a.url == acceptableAdsUrl) 95 if (a.url == acceptableAdsUrl)
92 return 1; 96 return 1;
93 if (b.url == acceptableAdsUrl) 97 if (b.url == acceptableAdsUrl)
94 return -1; 98 return -1;
95 99
100 // Make sure that newly added entries are always appear on top in
Thomas Greiner 2017/06/16 17:28:28 Typo: Remove "are".
101 // descending chronological order
102 let aTimestamp = a[timestampUI] || 0;
103 let bTimestamp = b[timestampUI] || 0;
104 if (aTimestamp || bTimestamp)
105 return bTimestamp - aTimestamp;
106
96 let aTitle = this._getItemTitle(a, 0).toLowerCase(); 107 let aTitle = this._getItemTitle(a, 0).toLowerCase();
97 let bTitle = this._getItemTitle(b, 0).toLowerCase(); 108 let bTitle = this._getItemTitle(b, 0).toLowerCase();
98 return aTitle.localeCompare(bTitle); 109 return aTitle.localeCompare(bTitle);
99 }); 110 });
111 };
100 112
113 Collection.prototype.addItem = function(item)
114 {
115 if (this.items.indexOf(item) >= 0)
116 return;
117
118 this.items.push(item);
119 this._sortItems();
101 for (let j = 0; j < this.details.length; j++) 120 for (let j = 0; j < this.details.length; j++)
102 { 121 {
103 let table = E(this.details[j].id); 122 let table = E(this.details[j].id);
104 let template = table.querySelector("template"); 123 let template = table.querySelector("template");
105 let listItem = document.createElement("li"); 124 let listItem = document.createElement("li");
106 listItem.appendChild(document.importNode(template.content, true)); 125 listItem.appendChild(document.importNode(template.content, true));
107 listItem.setAttribute("aria-label", this._getItemTitle(item, j)); 126 listItem.setAttribute("aria-label", this._getItemTitle(item, j));
108 listItem.setAttribute("data-access", item.url || item.text); 127 listItem.setAttribute("data-access", item.url || item.text);
109 listItem.setAttribute("role", "section"); 128 listItem.setAttribute("role", "section");
110 129
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 } 191 }
173 192
174 element.parentElement.removeChild(element); 193 element.parentElement.removeChild(element);
175 if (this.items.length == 0) 194 if (this.items.length == 0)
176 this._setEmpty(table, detail.emptyText); 195 this._setEmpty(table, detail.emptyText);
177 } 196 }
178 }; 197 };
179 198
180 Collection.prototype.updateItem = function(item) 199 Collection.prototype.updateItem = function(item)
181 { 200 {
201 let oldIndex = this.items.indexOf(item);
202 this._sortItems();
182 let access = (item.url || item.text).replace(/'/g, "\\'"); 203 let access = (item.url || item.text).replace(/'/g, "\\'");
183 for (let i = 0; i < this.details.length; i++) 204 for (let i = 0; i < this.details.length; i++)
184 { 205 {
185 let table = E(this.details[i].id); 206 let table = E(this.details[i].id);
186 let element = table.querySelector("[data-access='" + access + "']"); 207 let element = table.querySelector("[data-access='" + access + "']");
187 if (!element) 208 if (!element)
188 continue; 209 continue;
189 210
190 let title = this._getItemTitle(item, i); 211 let title = this._getItemTitle(item, i);
191 element.querySelector(".display").textContent = title; 212 element.querySelector(".display").textContent = title;
192 element.setAttribute("aria-label", title); 213 element.setAttribute("aria-label", title);
193 if (this.details[i].searchable) 214 if (this.details[i].searchable)
194 element.setAttribute("data-search", title.toLowerCase()); 215 element.setAttribute("data-search", title.toLowerCase());
195 let control = element.querySelector(".control[role='checkbox']"); 216 let control = element.querySelector(".control[role='checkbox']");
196 if (control) 217 if (control)
197 { 218 {
198 control.setAttribute("aria-checked", item.disabled == false); 219 control.setAttribute("aria-checked", item.disabled == false);
199 if (item.url == acceptableAdsUrl && this == collections.filterLists) 220 if (item.url == acceptableAdsUrl && this == collections.filterLists)
200 control.setAttribute("disabled", true); 221 control.disabled = true;
201 } 222 }
202 223
203 let dateElement = element.querySelector(".date"); 224 let dateElement = element.querySelector(".date");
204 let timeElement = element.querySelector(".time"); 225 let timeElement = element.querySelector(".time");
205 if (dateElement && timeElement) 226 if (dateElement && timeElement)
206 { 227 {
207 let message = element.querySelector(".message"); 228 let message = element.querySelector(".message");
208 if (item.isDownloading) 229 if (item.isDownloading)
209 { 230 {
210 let text = getMessage("options_filterList_lastDownload_inProgress"); 231 let text = getMessage("options_filterList_lastDownload_inProgress");
(...skipping 23 matching lines...) Expand all
234 { 255 {
235 if (item.homepage) 256 if (item.homepage)
236 websiteElement.setAttribute("href", item.homepage); 257 websiteElement.setAttribute("href", item.homepage);
237 else 258 else
238 websiteElement.setAttribute("aria-hidden", true); 259 websiteElement.setAttribute("aria-hidden", true);
239 } 260 }
240 261
241 let sourceElement = element.querySelector(".context-menu .source"); 262 let sourceElement = element.querySelector(".context-menu .source");
242 if (sourceElement) 263 if (sourceElement)
243 sourceElement.setAttribute("href", item.url); 264 sourceElement.setAttribute("href", item.url);
265
266 let newIndex = this.items.indexOf(item);
267 if (oldIndex != newIndex)
268 table.insertBefore(element, table.childNodes[newIndex]);
244 } 269 }
245 }; 270 };
246 271
247 Collection.prototype.clearAll = function() 272 Collection.prototype.clearAll = function()
248 { 273 {
249 this.items = []; 274 this.items = [];
250 for (let detail of this.details) 275 for (let detail of this.details)
251 { 276 {
252 let table = E(detail.id); 277 let table = E(detail.id);
253 let element = table.firstChild; 278 let element = table.firstChild;
(...skipping 24 matching lines...) Expand all
278 } 303 }
279 304
280 collections.popular = new Collection([ 305 collections.popular = new Collection([
281 { 306 {
282 id: "recommend-list-table" 307 id: "recommend-list-table"
283 } 308 }
284 ]); 309 ]);
285 collections.langs = new Collection([ 310 collections.langs = new Collection([
286 { 311 {
287 id: "blocking-languages-table", 312 id: "blocking-languages-table",
288 emptyText: "options_dialog_language_added_empty" 313 emptyText: ["options_dialog_language_added_empty"]
289 }, 314 },
290 { 315 {
291 id: "blocking-languages-dialog-table", 316 id: "blocking-languages-dialog-table",
292 emptyText: "options_dialog_language_added_empty" 317 emptyText: ["options_dialog_language_added_empty"]
293 } 318 }
294 ]); 319 ]);
295 collections.allLangs = new Collection([ 320 collections.allLangs = new Collection([
296 { 321 {
297 id: "all-lang-table", 322 id: "all-lang-table",
298 emptyText: "options_dialog_language_other_empty", 323 emptyText: ["options_dialog_language_other_empty"],
299 searchable: true 324 searchable: true
300 } 325 }
301 ]); 326 ]);
302 collections.acceptableAds = new Collection([ 327 collections.acceptableAds = new Collection([
303 { 328 {
304 id: "acceptableads-table" 329 id: "acceptableads-table"
305 } 330 }
306 ]); 331 ]);
307 collections.custom = new Collection([ 332 collections.custom = new Collection([
308 { 333 {
309 id: "custom-list-table" 334 id: "custom-list-table"
310 } 335 }
311 ]); 336 ]);
312 collections.whitelist = new Collection([ 337 collections.whitelist = new Collection([
313 { 338 {
314 id: "whitelisting-table", 339 id: "whitelisting-table",
315 emptyText: "options_whitelisted_empty" 340 emptyText: ["options_whitelist_empty_1", "options_whitelist_empty_2"]
316 } 341 }
317 ]); 342 ]);
318 collections.customFilters = new Collection([ 343 collections.customFilters = new Collection([
319 { 344 {
320 id: "custom-filters-table", 345 id: "custom-filters-table",
321 emptyText: "options_customFilters_empty" 346 emptyText: ["options_customFilters_empty"]
322 } 347 }
323 ]); 348 ]);
324 collections.filterLists = new Collection([ 349 collections.filterLists = new Collection([
325 { 350 {
326 id: "all-filter-lists-table", 351 id: "all-filter-lists-table",
327 useOriginalTitle: true 352 useOriginalTitle: true
328 } 353 }
329 ]); 354 ]);
330 355
331 function toggleShowLanguage(subscription) 356 function toggleShowLanguage(subscription)
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after
488 let dialog = E("dialog-content-predefined"); 513 let dialog = E("dialog-content-predefined");
489 let title = dialog.querySelector("h3").textContent; 514 let title = dialog.querySelector("h3").textContent;
490 let url = dialog.querySelector(".url").textContent; 515 let url = dialog.querySelector(".url").textContent;
491 addEnableSubscription(url, title); 516 addEnableSubscription(url, title);
492 closeDialog(); 517 closeDialog();
493 break; 518 break;
494 } 519 }
495 case "cancel-custom-filters": 520 case "cancel-custom-filters":
496 E("custom-filters").classList.remove("mode-edit"); 521 E("custom-filters").classList.remove("mode-edit");
497 break; 522 break;
498 case "cancel-domain-exception":
499 E("whitelisting-textbox").value = "";
500 document.querySelector("#whitelisting .controls").classList
501 .remove("mode-edit");
502 break;
503 case "close-dialog": 523 case "close-dialog":
504 closeDialog(); 524 closeDialog();
505 break; 525 break;
506 case "edit-custom-filters": 526 case "edit-custom-filters":
507 editCustomFilters(); 527 editCustomFilters();
508 break; 528 break;
509 case "edit-domain-exception":
510 document.querySelector("#whitelisting .controls").classList
511 .add("mode-edit");
512 E("whitelisting-textbox").focus();
513 break;
514 case "import-subscription": { 529 case "import-subscription": {
515 let url = E("blockingList-textbox").value; 530 let url = E("blockingList-textbox").value;
516 addEnableSubscription(url); 531 addEnableSubscription(url);
517 closeDialog(); 532 closeDialog();
518 break; 533 break;
519 } 534 }
520 case "open-context-menu": { 535 case "open-context-menu": {
521 let listItem = findParentData(element, "access", true); 536 let listItem = findParentData(element, "access", true);
522 if (listItem && !listItem.classList.contains("show-context-menu")) 537 if (listItem && !listItem.classList.contains("show-context-menu"))
523 listItem.classList.add("show-context-menu"); 538 listItem.classList.add("show-context-menu");
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
738 }); 753 });
739 getDocLink("releases", (link) => 754 getDocLink("releases", (link) =>
740 { 755 {
741 E("link-version").setAttribute("href", link); 756 E("link-version").setAttribute("href", link);
742 }); 757 });
743 758
744 updateShareLink(); 759 updateShareLink();
745 updateTooltips(); 760 updateTooltips();
746 761
747 // Initialize interactive UI elements 762 // Initialize interactive UI elements
748 document.body.addEventListener("click", onClick, false); 763 document.body.addEventListener("click", onClick, false);
saroyanm 2017/06/16 16:43:08 I think I've learned something today: Apparently h
Thomas Greiner 2017/06/16 17:28:28 I was aware of an implicit submission causing the
749 document.body.addEventListener("keyup", onKeyUp, false); 764 document.body.addEventListener("keyup", onKeyUp, false);
750 let placeholderValue = getMessage("options_dialog_language_find"); 765 let placeholderValue = getMessage("options_dialog_language_find");
751 E("find-language").setAttribute("placeholder", placeholderValue); 766 E("find-language").setAttribute("placeholder", placeholderValue);
752 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); 767 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false);
753 E("whitelisting-textbox").addEventListener("keypress", (e) => 768 let exampleValue = getMessage("options_whitelist_placeholder_example",
769 ["www.example.com"]);
770 E("whitelisting-textbox").setAttribute("placeholder", exampleValue);
771 E("whitelisting-textbox").addEventListener("keyup", (e) =>
754 { 772 {
755 if (getKey(e) == "Enter") 773 E("whitelisting-add-button").disabled = !e.target.value;
saroyanm 2017/06/16 16:43:08 Not sure if this also needs to be moved into `onKe
Thomas Greiner 2017/06/16 17:28:28 I'd say either way is fine so let's keep it as is.
756 addWhitelistedDomain();
757 }, false); 774 }, false);
758 775
759 // Advanced tab 776 // Advanced tab
760 let tweaks = document.querySelectorAll("#tweaks li[data-pref]"); 777 let tweaks = document.querySelectorAll("#tweaks li[data-pref]");
761 tweaks = Array.prototype.map.call(tweaks, (checkbox) => 778 tweaks = Array.prototype.map.call(tweaks, (checkbox) =>
762 { 779 {
763 return checkbox.getAttribute("data-pref"); 780 return checkbox.getAttribute("data-pref");
764 }); 781 });
765 for (let key of tweaks) 782 for (let key of tweaks)
766 { 783 {
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after
955 { 972 {
956 for (let subscription of subscriptions) 973 for (let subscription of subscriptions)
957 onSubscriptionMessage("added", subscription); 974 onSubscriptionMessage("added", subscription);
958 }); 975 });
959 }); 976 });
960 } 977 }
961 978
962 function addWhitelistedDomain() 979 function addWhitelistedDomain()
963 { 980 {
964 let domain = E("whitelisting-textbox"); 981 let domain = E("whitelisting-textbox");
982 for (let whitelistItem of collections.whitelist.items)
983 {
984 if (whitelistItem.title == domain.value)
985 {
986 whitelistItem[timestampUI] = Date.now();
987 collections.whitelist.updateItem(whitelistItem);
988 domain.value = "";
989 break;
990 }
991 }
965 if (domain.value) 992 if (domain.value)
966 { 993 {
967 sendMessageHandleErrors({ 994 sendMessageHandleErrors({
968 type: "filters.add", 995 type: "filters.add",
969 text: "@@||" + domain.value.toLowerCase() + "^$document" 996 text: "@@||" + domain.value.toLowerCase() + "^$document"
970 }); 997 });
971 } 998 }
972 999
973 domain.value = ""; 1000 domain.value = "";
974 document.querySelector("#whitelisting .controls") 1001 E("whitelisting-add-button").disabled = true;
975 .classList.remove("mode-edit");
976 } 1002 }
977 1003
978 function editCustomFilters() 1004 function editCustomFilters()
979 { 1005 {
980 if (!isCustomFiltersLoaded) 1006 if (!isCustomFiltersLoaded)
981 { 1007 {
982 console.error("Custom filters are not loaded"); 1008 console.error("Custom filters are not loaded");
983 return; 1009 return;
984 } 1010 }
985 1011
(...skipping 23 matching lines...) Expand all
1009 message.homepage = homepage; 1035 message.homepage = homepage;
1010 1036
1011 ext.backgroundPage.sendMessage(message); 1037 ext.backgroundPage.sendMessage(message);
1012 } 1038 }
1013 1039
1014 function onFilterMessage(action, filter) 1040 function onFilterMessage(action, filter)
1015 { 1041 {
1016 switch (action) 1042 switch (action)
1017 { 1043 {
1018 case "added": 1044 case "added":
1045 filter[timestampUI] = Date.now();
1019 updateFilter(filter); 1046 updateFilter(filter);
1020 updateShareLink(); 1047 updateShareLink();
1021 break; 1048 break;
1022 case "loaded": 1049 case "loaded":
1023 populateLists(); 1050 populateLists();
1024 break; 1051 break;
1025 case "removed": 1052 case "removed":
1026 let knownFilter = filtersMap[filter.text]; 1053 let knownFilter = filtersMap[filter.text];
1027 collections.whitelist.removeItem(knownFilter); 1054 collections.whitelist.removeItem(knownFilter);
1028 collections.customFilters.removeItem(knownFilter); 1055 collections.customFilters.removeItem(knownFilter);
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after
1289 }); 1316 });
1290 ext.backgroundPage.sendMessage({ 1317 ext.backgroundPage.sendMessage({
1291 type: "subscriptions.listen", 1318 type: "subscriptions.listen",
1292 filter: ["added", "disabled", "homepage", "lastDownload", "removed", 1319 filter: ["added", "disabled", "homepage", "lastDownload", "removed",
1293 "title", "downloadStatus", "downloading"] 1320 "title", "downloadStatus", "downloading"]
1294 }); 1321 });
1295 1322
1296 window.addEventListener("DOMContentLoaded", onDOMLoaded, false); 1323 window.addEventListener("DOMContentLoaded", onDOMLoaded, false);
1297 window.addEventListener("hashchange", onHashChange, false); 1324 window.addEventListener("hashchange", onHashChange, false);
1298 } 1325 }
OLDNEW
« no previous file with comments | « new-options.html ('k') | skin/new-options.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld