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

Side by Side Diff: new-options.js

Issue 29445590: Issue 5255 - Advanced tab (HTML, strings and functionality) (Closed)
Patch Set: Addressed notes from weekly(ish) meeting and small collection fix Created June 14, 2017, 11:58 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
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 20 matching lines...) Expand all
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 40
41 const whitelistedDomainRegexp = /^@@\|\|([^/:]+)\^\$document$/;
42
41 function Collection(details) 43 function Collection(details)
42 { 44 {
43 this.details = details; 45 this.details = details;
44 this.items = []; 46 this.items = [];
45 } 47 }
46 48
47 Collection.prototype._setEmpty = function(table, text) 49 Collection.prototype._setEmpty = function(table, text)
48 { 50 {
49 let placeholder = table.querySelector(".empty-placeholder"); 51 let placeholder = table.querySelector(".empty-placeholder");
50 if (text && !placeholder) 52 if (text && !placeholder)
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
93 if (b.url == acceptableAdsUrl) 95 if (b.url == acceptableAdsUrl)
94 return -1; 96 return -1;
95 97
96 let aTitle = this._getItemTitle(a, 0).toLowerCase(); 98 let aTitle = this._getItemTitle(a, 0).toLowerCase();
97 let bTitle = this._getItemTitle(b, 0).toLowerCase(); 99 let bTitle = this._getItemTitle(b, 0).toLowerCase();
98 return aTitle.localeCompare(bTitle); 100 return aTitle.localeCompare(bTitle);
99 }); 101 });
100 102
101 for (let j = 0; j < this.details.length; j++) 103 for (let j = 0; j < this.details.length; j++)
102 { 104 {
103 let table = E(this.details[j].id); 105 let detail = this.details[j];
106 let table = E(detail.id);
104 let template = table.querySelector("template"); 107 let template = table.querySelector("template");
105 let listItem = document.createElement("li"); 108 let listItem = document.createElement("li");
106 listItem.appendChild(document.importNode(template.content, true)); 109 listItem.appendChild(document.importNode(template.content, true));
107 listItem.setAttribute("aria-label", this._getItemTitle(item, j)); 110 listItem.setAttribute("aria-label", this._getItemTitle(item, j));
108 listItem.setAttribute("data-access", item.url || item.text); 111 listItem.setAttribute("data-access", item.url || item.text);
109 listItem.setAttribute("role", "section"); 112 listItem.setAttribute("role", "section");
110 113
111 let label = listItem.querySelector(".display"); 114 let label = listItem.querySelector(".display");
112 if (item.recommended && label.hasAttribute("data-tooltip")) 115 if (item.recommended && label.hasAttribute("data-tooltip"))
113 { 116 {
114 let tooltipId = label.getAttribute("data-tooltip"); 117 let tooltipId = label.getAttribute("data-tooltip");
115 tooltipId = tooltipId.replace("%value%", item.recommended); 118 tooltipId = tooltipId.replace("%value%", item.recommended);
116 label.setAttribute("data-tooltip", tooltipId); 119 label.setAttribute("data-tooltip", tooltipId);
117 } 120 }
118 121
119 for (let control of listItem.querySelectorAll(".control")) 122 for (let control of listItem.querySelectorAll(".control"))
120 { 123 {
121 if (control.hasAttribute("title")) 124 if (control.hasAttribute("title"))
122 { 125 {
123 let titleValue = getMessage(control.getAttribute("title")); 126 let titleValue = getMessage(control.getAttribute("title"));
124 control.setAttribute("title", titleValue); 127 control.setAttribute("title", titleValue);
125 } 128 }
126 } 129 }
127 130
128 this._setEmpty(table, null); 131 this._setEmpty(table, null);
129 if (table.hasChildNodes()) 132 if (table.children.length > 0)
130 { 133 {
131 table.insertBefore(listItem, 134 let beforeIndex = this.items.indexOf(item) + (detail.useHeader == true);
132 table.childNodes[this.items.indexOf(item)]); 135 table.insertBefore(listItem, table.children[beforeIndex]);
133 } 136 }
Thomas Greiner 2017/07/07 13:01:09 What's the reason for those changes? I didn't noti
saroyanm 2017/07/10 11:38:00 Header is column name. I used naming header from "
saroyanm 2017/07/10 13:27:43 I'll move the column names row out of the list for
saroyanm 2017/07/12 15:29:02 Done.
134 else 137 else
138 {
135 table.appendChild(listItem); 139 table.appendChild(listItem);
140 }
136 this.updateItem(item); 141 this.updateItem(item);
137 } 142 }
138 return length; 143 return length;
139 }; 144 };
140 145
141 Collection.prototype.removeItem = function(item) 146 Collection.prototype.removeItem = function(item)
142 { 147 {
143 let index = this.items.indexOf(item); 148 let index = this.items.indexOf(item);
144 if (index == -1) 149 if (index == -1)
145 return; 150 return;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 if (this.details[i].searchable) 198 if (this.details[i].searchable)
194 element.setAttribute("data-search", title.toLowerCase()); 199 element.setAttribute("data-search", title.toLowerCase());
195 let control = element.querySelector(".control[role='checkbox']"); 200 let control = element.querySelector(".control[role='checkbox']");
196 if (control) 201 if (control)
197 { 202 {
198 control.setAttribute("aria-checked", item.disabled == false); 203 control.setAttribute("aria-checked", item.disabled == false);
199 if (item.url == acceptableAdsUrl && this == collections.filterLists) 204 if (item.url == acceptableAdsUrl && this == collections.filterLists)
200 control.setAttribute("disabled", true); 205 control.setAttribute("disabled", true);
201 } 206 }
202 207
203 let dateElement = element.querySelector(".date"); 208 let lastUpdateElem = element.querySelector(".last-update");
204 let timeElement = element.querySelector(".time"); 209 if (lastUpdateElem)
205 if (dateElement && timeElement)
206 { 210 {
207 let message = element.querySelector(".message"); 211 let message = element.querySelector(".message");
208 if (item.isDownloading) 212 if (item.isDownloading)
209 { 213 {
210 let text = getMessage("options_filterList_lastDownload_inProgress"); 214 let text = getMessage("options_filterList_lastDownload_inProgress");
211 message.textContent = text; 215 message.textContent = text;
212 element.classList.add("show-message"); 216 element.classList.add("show-message");
213 } 217 }
214 else if (item.downloadStatus != "synchronize_ok") 218 else if (item.downloadStatus != "synchronize_ok")
215 { 219 {
216 let error = filterErrors.get(item.downloadStatus); 220 let error = filterErrors.get(item.downloadStatus);
217 if (error) 221 if (error)
218 message.textContent = getMessage(error); 222 message.textContent = getMessage(error);
219 else 223 else
220 message.textContent = item.downloadStatus; 224 message.textContent = item.downloadStatus;
221 element.classList.add("show-message"); 225 element.classList.add("show-message");
222 } 226 }
223 else if (item.lastDownload > 0) 227 else if (item.lastDownload > 0)
224 { 228 {
225 let dateTime = i18nFormatDateTime(item.lastDownload * 1000); 229 let timer = setInterval(lastUpdateLive, 60000);
Thomas Greiner 2017/07/07 13:01:09 As far as I see, the spec doesn't say that this ti
saroyanm 2017/07/10 11:38:02 I do remember that this was discussed during our m
saroyanm 2017/07/12 15:29:01 Removed live update
226 dateElement.textContent = dateTime[0]; 230 let lastUpdate = item.lastDownload * 1000;
227 timeElement.textContent = dateTime[1]; 231 function lastUpdateLive()
232 {
233 let sinceUpdate = Date.now() - lastUpdate;
234 if(sinceUpdate > 86400000)
Thomas Greiner 2017/07/07 13:01:10 Detail: Usually, we create constants to make it mo
saroyanm 2017/07/10 11:38:01 Agree
saroyanm 2017/07/12 15:29:02 Done.
235 {
236 let lastUpdateDate = new Date(item.lastDownload * 1000);
237 let monthName = lastUpdateDate.toLocaleString(
238 document.documentElement.lang, { month: "short" });
Thomas Greiner 2017/07/07 13:01:10 Actually, while reviewing #5278 I noticed that we
Thomas Greiner 2017/07/07 13:01:10 Coding style: This violates the rule "object-curly
saroyanm 2017/07/10 11:38:00 Good point, I'll run eslint before uploading next
saroyanm 2017/07/10 11:38:01 Acknowledged, checked specifications as well I thi
saroyanm 2017/07/12 15:29:01 Done.
saroyanm 2017/07/12 15:29:01 Done.
239 let day = lastUpdateDate.getDate();
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 }
262 lastUpdateLive();
228 element.classList.remove("show-message"); 263 element.classList.remove("show-message");
229 } 264 }
230 } 265 }
231 266
232 let websiteElement = element.querySelector(".context-menu .website"); 267 let websiteElement = element.querySelector(".context-menu .website");
233 if (websiteElement) 268 if (websiteElement)
234 { 269 {
235 if (item.homepage) 270 if (item.homepage)
236 websiteElement.setAttribute("href", item.homepage); 271 websiteElement.setAttribute("href", item.homepage);
237 else 272 else
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
308 { 343 {
309 id: "custom-list-table" 344 id: "custom-list-table"
310 } 345 }
311 ]); 346 ]);
312 collections.whitelist = new Collection([ 347 collections.whitelist = new Collection([
313 { 348 {
314 id: "whitelisting-table", 349 id: "whitelisting-table",
315 emptyText: "options_whitelisted_empty" 350 emptyText: "options_whitelisted_empty"
316 } 351 }
317 ]); 352 ]);
318 collections.customFilters = new Collection([
319 {
320 id: "custom-filters-table",
321 emptyText: "options_customFilters_empty"
322 }
323 ]);
324 collections.filterLists = new Collection([ 353 collections.filterLists = new Collection([
325 { 354 {
326 id: "all-filter-lists-table", 355 id: "all-filter-lists-table",
327 useOriginalTitle: true 356 useOriginalTitle: true,
357 useHeader: true
328 } 358 }
329 ]); 359 ]);
330 360
331 function toggleShowLanguage(subscription) 361 function toggleShowLanguage(subscription)
332 { 362 {
333 if (subscription.recommended == "ads") 363 if (subscription.recommended == "ads")
334 { 364 {
335 if (subscription.disabled) 365 if (subscription.disabled)
336 { 366 {
337 collections.allLangs.addItem(subscription); 367 collections.allLangs.addItem(subscription);
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 function updateSubscription(subscription) 401 function updateSubscription(subscription)
372 { 402 {
373 for (let name in collections) 403 for (let name in collections)
374 collections[name].updateItem(subscription); 404 collections[name].updateItem(subscription);
375 405
376 toggleShowLanguage(subscription); 406 toggleShowLanguage(subscription);
377 } 407 }
378 408
379 function updateFilter(filter) 409 function updateFilter(filter)
380 { 410 {
381 let match = filter.text.match(/^@@\|\|([^/:]+)\^\$document$/); 411 let match = filter.text.match(whitelistedDomainRegexp);
382 if (match && !filtersMap[filter.text]) 412 if (match && !filtersMap[filter.text])
383 { 413 {
384 filter.title = match[1]; 414 filter.title = match[1];
385 collections.whitelist.addItem(filter); 415 collections.whitelist.addItem(filter);
386 } 416 }
387 else 417 else
388 collections.customFilters.addItem(filter); 418 addCustomFilter(filter);
389 419
390 filtersMap[filter.text] = filter; 420 filtersMap[filter.text] = filter;
391 } 421 }
392 422
423 function addCustomFilter(filter)
424 {
425 customFiltersView("read");
Thomas Greiner 2017/07/07 13:01:10 Detail: The name is ambiguous because it doesn't s
saroyanm 2017/07/10 11:38:01 Agree.
saroyanm 2017/07/12 15:29:01 Done.
426
427 E("custom-filters-box").appendChild(new Option(filter.text, filter.text));
428 }
429
430 function removeCustomFilter(text)
431 {
432 let list = E("custom-filters-box");
433 let selector = "option[value=" + CSS.escape(text) + "]";
434 for (let option of list.querySelectorAll(selector))
435 list.removeChild(option);
436
437 if (E("custom-filters-box").options.length == 0)
438 customFiltersView("empty")
439 }
440
441 function convertToRawFormat(event)
442 {
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 }
450
393 function loadRecommendations() 451 function loadRecommendations()
394 { 452 {
395 fetch("subscriptions.xml") 453 fetch("subscriptions.xml")
396 .then((response) => 454 .then((response) =>
397 { 455 {
398 return response.text(); 456 return response.text();
399 }) 457 })
400 .then((text) => 458 .then((text) =>
401 { 459 {
402 let doc = new DOMParser().parseFromString(text, "application/xml"); 460 let doc = new DOMParser().parseFromString(text, "application/xml");
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
467 else 525 else
468 location.href = link; 526 location.href = link;
469 }); 527 });
470 } 528 }
471 529
472 function switchTab(id) 530 function switchTab(id)
473 { 531 {
474 location.hash = id; 532 location.hash = id;
475 } 533 }
476 534
477 function execAction(action, element) 535 function execAction(action, element, event)
478 { 536 {
537 if (element.getAttribute("href") == "#")
Thomas Greiner 2017/07/07 13:01:10 Detail: Why do you even set the "href" attribute i
saroyanm 2017/07/10 11:38:00 Good question, I think we don't need href attribut
saroyanm 2017/07/12 15:29:02 Done.
538 event.preventDefault();
539
479 switch (action) 540 switch (action)
480 { 541 {
481 case "add-domain-exception": 542 case "add-domain-exception":
482 addWhitelistedDomain(); 543 addWhitelistedDomain();
483 break; 544 break;
484 case "add-language-subscription": 545 case "add-language-subscription":
485 addEnableSubscription(findParentData(element, "access", false)); 546 addEnableSubscription(findParentData(element, "access", false));
486 break; 547 break;
487 case "add-predefined-subscription": { 548 case "add-predefined-subscription": {
488 let dialog = E("dialog-content-predefined"); 549 let dialog = E("dialog-content-predefined");
489 let title = dialog.querySelector("h3").textContent; 550 let title = dialog.querySelector("h3").textContent;
490 let url = dialog.querySelector(".url").textContent; 551 let url = dialog.querySelector(".url").textContent;
491 addEnableSubscription(url, title); 552 addEnableSubscription(url, title);
492 closeDialog(); 553 closeDialog();
493 break; 554 break;
494 } 555 }
495 case "cancel-custom-filters": 556 case "cancel-custom-filters":
496 E("custom-filters").classList.remove("mode-edit"); 557 customFiltersView("read");
497 break; 558 break;
498 case "cancel-domain-exception": 559 case "cancel-domain-exception":
499 E("whitelisting-textbox").value = ""; 560 E("whitelisting-textbox").value = "";
500 document.querySelector("#whitelisting .controls").classList 561 document.querySelector("#whitelisting .controls").classList
501 .remove("mode-edit"); 562 .remove("mode-edit");
502 break; 563 break;
503 case "close-dialog": 564 case "close-dialog":
504 closeDialog(); 565 closeDialog();
505 break; 566 break;
506 case "edit-custom-filters": 567 case "edit-custom-filters":
507 editCustomFilters(); 568 customFiltersView("write");
508 break; 569 break;
509 case "edit-domain-exception": 570 case "edit-domain-exception":
510 document.querySelector("#whitelisting .controls").classList 571 document.querySelector("#whitelisting .controls").classList
511 .add("mode-edit"); 572 .add("mode-edit");
512 E("whitelisting-textbox").focus(); 573 E("whitelisting-textbox").focus();
513 break; 574 break;
514 case "import-subscription": { 575 case "import-subscription": {
515 let url = E("blockingList-textbox").value; 576 let url = E("blockingList-textbox").value;
516 addEnableSubscription(url); 577 addEnableSubscription(url);
517 closeDialog(); 578 closeDialog();
(...skipping 28 matching lines...) Expand all
546 }); 607 });
547 break; 608 break;
548 case "save-custom-filters": 609 case "save-custom-filters":
549 sendMessageHandleErrors({ 610 sendMessageHandleErrors({
550 type: "filters.importRaw", 611 type: "filters.importRaw",
551 text: E("custom-filters-raw").value, 612 text: E("custom-filters-raw").value,
552 removeExisting: true 613 removeExisting: true
553 }, 614 },
554 () => 615 () =>
555 { 616 {
556 E("custom-filters").classList.remove("mode-edit"); 617 customFiltersView("read");
557 }); 618 });
558 break; 619 break;
559 case "switch-tab": 620 case "switch-tab":
560 let tabId = findParentData(element, "tab", false); 621 let tabId = findParentData(element, "tab", false);
561 switchTab(tabId); 622 switchTab(tabId);
562 break; 623 break;
563 case "toggle-disable-subscription": 624 case "toggle-disable-subscription":
564 ext.backgroundPage.sendMessage({ 625 ext.backgroundPage.sendMessage({
565 type: "subscriptions.toggle", 626 type: "subscriptions.toggle",
566 keepInstalled: true, 627 keepInstalled: true,
(...skipping 25 matching lines...) Expand all
592 break; 653 break;
593 case "update-subscription": 654 case "update-subscription":
594 ext.backgroundPage.sendMessage({ 655 ext.backgroundPage.sendMessage({
595 type: "subscriptions.update", 656 type: "subscriptions.update",
596 url: findParentData(element, "access", false) 657 url: findParentData(element, "access", false)
597 }); 658 });
598 break; 659 break;
599 } 660 }
600 } 661 }
601 662
663 function customFiltersView(mode)
Thomas Greiner 2017/07/07 13:01:09 Detail: I noticed that you've call `E("custom-filt
Thomas Greiner 2017/07/07 13:01:09 You're trying to switch between three modes but ea
saroyanm 2017/07/10 11:38:00 ".mode-edit" Will be consistent with other impleme
saroyanm 2017/07/10 11:38:00 Agree.
saroyanm 2017/07/12 15:29:02 Done.
saroyanm 2017/07/12 15:29:02 Done.
664 {
665 switch (mode)
666 {
667 case "read":
668 if (E("custom-filters-box").options.length == 0)
669 {
670 customFiltersView("empty");
671 }
672 else
673 {
674 E("custom-filters").classList.add("mode-view");
Thomas Greiner 2017/07/07 13:01:10 Detail: Why the different naming? I do like that y
saroyanm 2017/07/10 11:38:00 Acknowledged, will make them consistent
saroyanm 2017/07/12 15:29:02 Done.
675 E("custom-filters").classList.remove("mode-edit");
676 E("custom-filters").classList.remove("mode-empty");
Thomas Greiner 2017/07/07 13:01:09 Detail: Note that you can pass multiple arguments
saroyanm 2017/07/10 11:38:01 Acknowledged.
677 }
678 break;
679 case "write":
680 convertToRawFormat();
681 E("custom-filters").classList.remove("mode-view");
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 }
692
602 function onClick(e) 693 function onClick(e)
603 { 694 {
604 let context = document.querySelector(".show-context-menu"); 695 let context = document.querySelector(".show-context-menu");
605 if (context) 696 if (context)
606 context.classList.remove("show-context-menu"); 697 context.classList.remove("show-context-menu");
607 698
608 let actions = findParentData(e.target, "action", false); 699 let actions = findParentData(e.target, "action", false);
609 if (!actions) 700 if (!actions)
610 return; 701 return;
611 702
612 actions = actions.split(","); 703 actions = actions.split(",");
613 for (let action of actions) 704 for (let action of actions)
614 { 705 {
615 execAction(action, e.target); 706 execAction(action, e.target, e);
616 } 707 }
617 } 708 }
618 709
619 function getKey(e) 710 function getKey(e)
620 { 711 {
621 // e.keyCode has been deprecated so we attempt to use e.key 712 // e.keyCode has been deprecated so we attempt to use e.key
622 if ("key" in e) 713 if ("key" in e)
623 return e.key; 714 return e.key;
624 return getKey.keys[e.keyCode]; 715 return getKey.keys[e.keyCode];
625 } 716 }
(...skipping 26 matching lines...) Expand all
652 { 743 {
653 if (key == "ArrowLeft" || key == "ArrowUp") 744 if (key == "ArrowLeft" || key == "ArrowUp")
654 element = element.previousElementSibling || container.lastElementChild; 745 element = element.previousElementSibling || container.lastElementChild;
655 else if (key == "ArrowRight" || key == "ArrowDown") 746 else if (key == "ArrowRight" || key == "ArrowDown")
656 element = element.nextElementSibling || container.firstElementChild; 747 element = element.nextElementSibling || container.firstElementChild;
657 } 748 }
658 749
659 let actions = container.getAttribute("data-action").split(","); 750 let actions = container.getAttribute("data-action").split(",");
660 for (let action of actions) 751 for (let action of actions)
661 { 752 {
662 execAction(action, element); 753 execAction(action, element, e);
663 } 754 }
664 } 755 }
665 756
666 function selectTabItem(tabId, container, focus) 757 function selectTabItem(tabId, container, focus)
667 { 758 {
668 // Show tab content 759 // Show tab content
669 document.body.setAttribute("data-tab", tabId); 760 document.body.setAttribute("data-tab", tabId);
670 761
671 // Select tab 762 // Select tab
672 let tabList = container.querySelector("[role='tablist']"); 763 let tabList = container.querySelector("[role='tablist']");
673 if (!tabList) 764 if (!tabList)
674 return null; 765 return null;
675 766
676 let previousTab = tabList.querySelector("[aria-selected]"); 767 let previousTab = tabList.querySelector("[aria-selected]");
677 previousTab.removeAttribute("aria-selected"); 768 previousTab.removeAttribute("aria-selected");
678 previousTab.setAttribute("tabindex", -1); 769 previousTab.setAttribute("tabindex", -1);
679 770
680 let tab = tabList.querySelector("li[data-tab='" + tabId + "']"); 771 let tab = tabList.querySelector("li[data-tab='" + tabId + "']");
681 tab.setAttribute("aria-selected", true); 772 tab.setAttribute("aria-selected", true);
682 tab.setAttribute("tabindex", 0); 773 tab.setAttribute("tabindex", 0);
683 774
684 let tabContentId = tab.getAttribute("aria-controls"); 775 let tabContentId = tab.getAttribute("aria-controls");
685 let tabContent = document.getElementById(tabContentId); 776 let tabContent = document.getElementById(tabContentId);
686 777
687 // Select sub tabs
688 if (tab.hasAttribute("data-subtab"))
689 selectTabItem(tab.getAttribute("data-subtab"), tabContent, false);
690
691 if (tab && focus) 778 if (tab && focus)
692 tab.focus(); 779 tab.focus();
693 780
694 return tabContent; 781 return tabContent;
695 } 782 }
696 783
697 function onHashChange() 784 function onHashChange()
698 { 785 {
699 let hash = location.hash.substr(1); 786 let hash = location.hash.substr(1);
700 if (!hash) 787 if (!hash)
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
750 let placeholderValue = getMessage("options_dialog_language_find"); 837 let placeholderValue = getMessage("options_dialog_language_find");
751 E("find-language").setAttribute("placeholder", placeholderValue); 838 E("find-language").setAttribute("placeholder", placeholderValue);
752 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); 839 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false);
753 E("whitelisting-textbox").addEventListener("keypress", (e) => 840 E("whitelisting-textbox").addEventListener("keypress", (e) =>
754 { 841 {
755 if (getKey(e) == "Enter") 842 if (getKey(e) == "Enter")
756 addWhitelistedDomain(); 843 addWhitelistedDomain();
757 }, false); 844 }, false);
758 845
759 // Advanced tab 846 // Advanced tab
760 let tweaks = document.querySelectorAll("#tweaks li[data-pref]"); 847 let customize = document.querySelectorAll("#customize li[data-pref]");
761 tweaks = Array.prototype.map.call(tweaks, (checkbox) => 848 customize = Array.prototype.map.call(customize, (checkbox) =>
762 { 849 {
763 return checkbox.getAttribute("data-pref"); 850 return checkbox.getAttribute("data-pref");
764 }); 851 });
765 for (let key of tweaks) 852 for (let key of customize)
766 { 853 {
767 getPref(key, (value) => 854 getPref(key, (value) =>
768 { 855 {
769 onPrefMessage(key, value, true); 856 onPrefMessage(key, value, true);
770 }); 857 });
771 } 858 }
772 ext.backgroundPage.sendMessage({ 859 ext.backgroundPage.sendMessage({
773 type: "app.get", 860 type: "app.get",
774 what: "features" 861 what: "features"
775 }, 862 },
776 (features) => 863 (features) =>
777 { 864 {
778 hidePref("show_devtools_panel", !features.devToolsPanel); 865 hidePref("show_devtools_panel", !features.devToolsPanel);
779 }); 866 });
780 867
781 let filterTextbox = document.querySelector("#custom-filters-add input"); 868 getDocLink("filterdoc", (link) =>
782 placeholderValue = getMessage("options_customFilters_textbox_placeholder");
783 filterTextbox.setAttribute("placeholder", placeholderValue);
784 function addCustomFilters()
785 { 869 {
786 let filterText = filterTextbox.value; 870 E("link-filters").setAttribute("href", link);
787 sendMessageHandleErrors({ 871 });
788 type: "filters.add", 872
789 text: filterText 873 getDocLink("subscriptions", (link) =>
790 },
791 () =>
792 {
793 filterTextbox.value = "";
794 });
795 }
796 E("custom-filters-add").addEventListener("submit", (e) =>
797 { 874 {
798 e.preventDefault(); 875 setLinks("filter-lists-description", link);
799 addCustomFilters(); 876 });
800 }, false); 877
878 E("custom-filters-raw").setAttribute("placeholder",
879 getMessage("options_customFilters_edit_placeholder", ["/ads/track/*"]));
801 880
802 // Help tab 881 // Help tab
803 getDocLink("faq", (link) => 882 getDocLink("faq", (link) =>
804 { 883 {
805 E("link-faq").setAttribute("href", link); 884 E("link-faq").setAttribute("href", link);
806 }); 885 });
807 getDocLink("social_twitter", (link) => 886 getDocLink("social_twitter", (link) =>
808 { 887 {
809 E("link-twitter").setAttribute("href", link); 888 E("link-twitter").setAttribute("href", link);
810 }); 889 });
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after
1017 { 1096 {
1018 case "added": 1097 case "added":
1019 updateFilter(filter); 1098 updateFilter(filter);
1020 updateShareLink(); 1099 updateShareLink();
1021 break; 1100 break;
1022 case "loaded": 1101 case "loaded":
1023 populateLists(); 1102 populateLists();
1024 break; 1103 break;
1025 case "removed": 1104 case "removed":
1026 let knownFilter = filtersMap[filter.text]; 1105 let knownFilter = filtersMap[filter.text];
1027 collections.whitelist.removeItem(knownFilter); 1106 if (whitelistedDomainRegexp.test(knownFilter.text))
1028 collections.customFilters.removeItem(knownFilter); 1107 collections.whitelist.removeItem(knownFilter);
1108 else
1109 removeCustomFilter(filter.text);
1110
1029 delete filtersMap[filter.text]; 1111 delete filtersMap[filter.text];
1030 updateShareLink(); 1112 updateShareLink();
1031 break; 1113 break;
1032 } 1114 }
1033 } 1115 }
1034 1116
1035 function onSubscriptionMessage(action, subscription) 1117 function onSubscriptionMessage(action, subscription)
1036 { 1118 {
1037 if (subscription.url in subscriptionsMap) 1119 if (subscription.url in subscriptionsMap)
1038 { 1120 {
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after
1289 }); 1371 });
1290 ext.backgroundPage.sendMessage({ 1372 ext.backgroundPage.sendMessage({
1291 type: "subscriptions.listen", 1373 type: "subscriptions.listen",
1292 filter: ["added", "disabled", "homepage", "lastDownload", "removed", 1374 filter: ["added", "disabled", "homepage", "lastDownload", "removed",
1293 "title", "downloadStatus", "downloading"] 1375 "title", "downloadStatus", "downloading"]
1294 }); 1376 });
1295 1377
1296 window.addEventListener("DOMContentLoaded", onDOMLoaded, false); 1378 window.addEventListener("DOMContentLoaded", onDOMLoaded, false);
1297 window.addEventListener("hashchange", onHashChange, false); 1379 window.addEventListener("hashchange", onHashChange, false);
1298 } 1380 }
OLDNEW

Powered by Google App Engine
This is Rietveld