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

Side by Side Diff: new-options.js

Issue 29348642: Issue 2409 - Improved accessibility of tabs in options page (Closed)
Patch Set: Created July 25, 2016, 3:01 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
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-2016 Eyeo GmbH 3 * Copyright (C) 2006-2016 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 452 matching lines...) Expand 10 before | Expand all | Expand 10 after
463 { 463 {
464 ext.backgroundPage.sendMessage(message, function(errors) 464 ext.backgroundPage.sendMessage(message, function(errors)
465 { 465 {
466 if (errors.length > 0) 466 if (errors.length > 0)
467 alert(errors.join("\n")); 467 alert(errors.join("\n"));
468 else if (onSuccess) 468 else if (onSuccess)
469 onSuccess(); 469 onSuccess();
470 }); 470 });
471 } 471 }
472 472
473 function openDocLink(id)
474 {
475 getDocLink(id, function(link)
476 {
477 if (id == "share-general")
478 openSharePopup(link);
479 else
480 location.href = link;
481 });
482 }
483
484 function switchTab(id)
485 {
486 location.hash = id;
487 }
488
473 function onClick(e) 489 function onClick(e)
474 { 490 {
475 var context = document.querySelector(".show-context-menu"); 491 var context = document.querySelector(".show-context-menu");
476 if (context) 492 if (context)
477 context.classList.remove("show-context-menu"); 493 context.classList.remove("show-context-menu");
478 494
479 var element = e.target; 495 var element = e.target;
480 while (true) 496 while (true)
481 { 497 {
482 if (!element) 498 if (!element)
483 return; 499 return;
484 500
485 if (element.hasAttribute("data-action")) 501 if (element.hasAttribute("data-action"))
486 break; 502 break;
487 503
488 element = element.parentElement; 504 element = element.parentElement;
489 } 505 }
490 506
507 var element = findParentData(e.target, "action", true);
491 var actions = element.getAttribute("data-action").split(","); 508 var actions = element.getAttribute("data-action").split(",");
492 for (var i = 0; i < actions.length; i++) 509 for (var i = 0; i < actions.length; i++)
493 { 510 {
494 switch (actions[i]) 511 switch (actions[i])
495 { 512 {
496 case "add-domain-exception": 513 case "add-domain-exception":
497 addWhitelistedDomain(); 514 addWhitelistedDomain();
498 break; 515 break;
499 case "add-predefined-subscription": 516 case "add-predefined-subscription":
500 var dialog = E("dialog-content-predefined"); 517 var dialog = E("dialog-content-predefined");
(...skipping 19 matching lines...) Expand all
520 case "edit-domain-exception": 537 case "edit-domain-exception":
521 document.querySelector("#whitelisting .controls").classList.add("mode- edit"); 538 document.querySelector("#whitelisting .controls").classList.add("mode- edit");
522 E("whitelisting-textbox").focus(); 539 E("whitelisting-textbox").focus();
523 break; 540 break;
524 case "import-subscription": 541 case "import-subscription":
525 var url = E("blockingList-textbox").value; 542 var url = E("blockingList-textbox").value;
526 addEnableSubscription(url); 543 addEnableSubscription(url);
527 closeDialog(); 544 closeDialog();
528 break; 545 break;
529 case "open-dialog": 546 case "open-dialog":
530 openDialog(element.getAttribute("data-dialog")); 547 var dialog = findParentData(element, "dialog", false);
saroyanm 2016/07/25 17:13:34 Are you assigning element to the variable because
Thomas Greiner 2016/07/26 12:50:49 Yes, that makes it easier to read than for instanc
548 openDialog(dialog);
549 break;
550 case "open-doclink":
551 var doclink = findParentData(element, "doclink", false);
552 openDocLink(doclink);
531 break; 553 break;
532 case "save-custom-filters": 554 case "save-custom-filters":
533 sendMessageHandleErrors( 555 sendMessageHandleErrors(
534 { 556 {
535 type: "filters.importRaw", 557 type: "filters.importRaw",
536 text: E("custom-filters-raw").value, 558 text: E("custom-filters-raw").value,
537 removeExisting: true 559 removeExisting: true
538 }, 560 },
539 function() 561 function()
540 { 562 {
541 E("custom-filters").classList.remove("mode-edit"); 563 E("custom-filters").classList.remove("mode-edit");
542 }); 564 });
543 break; 565 break;
544 case "switch-tab": 566 case "switch-tab":
545 document.body.setAttribute("data-tab", 567 var tabId = findParentData(e.target, "tab", false);
546 element.getAttribute("data-tab")); 568 switchTab(tabId);
547 break; 569 break;
548 case "toggle-pref": 570 case "toggle-pref":
549 ext.backgroundPage.sendMessage( 571 ext.backgroundPage.sendMessage(
550 { 572 {
551 type: "prefs.toggle", 573 type: "prefs.toggle",
552 key: findParentData(element, "pref", false) 574 key: findParentData(element, "pref", false)
553 }); 575 });
554 break; 576 break;
555 case "update-all-subscriptions": 577 case "update-all-subscriptions":
556 ext.backgroundPage.sendMessage( 578 ext.backgroundPage.sendMessage(
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
604 ext.backgroundPage.sendMessage( 626 ext.backgroundPage.sendMessage(
605 { 627 {
606 type: "filters.remove", 628 type: "filters.remove",
607 text: findParentData(element, "access", false) 629 text: findParentData(element, "access", false)
608 }); 630 });
609 break; 631 break;
610 } 632 }
611 } 633 }
612 } 634 }
613 635
636 function getKey(e)
637 {
638 // e.keyCode has been deprecated so we attempt to use e.key
639 if ("key" in e)
640 return e.key;
641 return getKey.keys[e.keyCode];
642 }
643 getKey.keys = {
644 9: "Tab",
645 13: "Enter",
646 27: "Escape",
647 37: "ArrowLeft",
648 38: "ArrowUp",
649 39: "ArrowRight",
650 40: "ArrowDown"
651 };
652
653 function onKeyUp(e)
654 {
655 var key = getKey(e);
656 var element = document.querySelector(":focus");
saroyanm 2016/07/25 17:13:34 We probably should be able to use document.activeE
Thomas Greiner 2016/07/26 12:50:49 You're absolutely right! Done.
657 if (!key || !element)
658 return;
659
660 var container = findParentData(element, "action", true);
661 if (!container || !container.hasAttribute("data-keys"))
662 return;
663
664 var keys = container.getAttribute("data-keys").split(" ");
665 if (keys.indexOf(key) < 0)
666 return;
667
668 switch (container.getAttribute("data-action"))
669 {
670 case "add-domain-exception":
671 addWhitelistedDomain();
672 break;
673 case "open-doclink":
674 var doclink = findParentData(element, "doclink", false);
675 openDocLink(doclink);
676 break;
677 case "switch-tab":
678 if (key == "Enter")
679 {
680 var tabId = findParentData(element, "tab", false);
saroyanm 2016/07/25 17:13:34 If I don't miss something this can go outside of i
Thomas Greiner 2016/07/26 12:50:49 `switchTab()` is only called if either Enter is pr
681 switchTab(tabId);
682 }
683 else if (element.hasAttribute("aria-selected"))
684 {
685 if (key == "ArrowLeft" || key == "ArrowUp")
686 {
687 element = element.previousElementSibling
688 || container.lastElementChild;
689 }
690 else if (key == "ArrowRight" || key == "ArrowDown")
691 {
692 element = element.nextElementSibling
693 || container.firstElementChild;
694 }
695
696 var tabId = findParentData(element, "tab", false);
697 switchTab(tabId);
698 }
699 break;
700 }
701 }
702
703 function onHashChange()
704 {
705 var hash = location.hash.substr(1);
706 if (!hash)
707 return;
708
709 function selectTab(tabId, container, focus)
saroyanm 2016/07/25 17:13:34 This nested function will always recreated on each
Thomas Greiner 2016/07/26 12:50:50 Done. I wanted to avoid that this function is use
710 {
711 // Show tab content
712 document.body.setAttribute("data-tab", tabId);
713
714 // Select tab
715 var tabList = container.querySelector("[role='tablist']");
716 if (!tabList)
717 return null;
718
719 var previousTab = tabList.querySelector("[aria-selected]");
720 previousTab.removeAttribute("aria-selected");
721 previousTab.setAttribute("tabindex", -1);
722
723 var tab = tabList.querySelector("li[data-tab='" + tabId + "']");
724 tab.setAttribute("aria-selected", true);
725 tab.setAttribute("tabindex", 0);
726
727 var tabContentId = tab.getAttribute("aria-controls");
728 var tabContent = document.getElementById(tabContentId);
729
730 // Select sub tabs
731 if (tab.hasAttribute("data-subtab"))
732 selectTab(tab.getAttribute("data-subtab"), tabContent, false);
733
734 if (tab && focus)
735 tab.focus();
736
737 return tabContent;
738 }
739
740 // Select tab and parent tabs
741 var tabIds = hash.split("-");
742 var tabContent = document.body;
743 for (var i = 0; i < tabIds.length; i++)
744 {
745 var tabId = tabIds.slice(0, i + 1).join("-");
746 tabContent = selectTab(tabId, tabContent, true);
747 if (!tabContent)
748 break;
749 }
750 }
751
614 function onDOMLoaded() 752 function onDOMLoaded()
615 { 753 {
616 populateLists(); 754 populateLists();
617 function onFindLanguageKeyUp() 755 function onFindLanguageKeyUp()
618 { 756 {
619 var searchStyle = E("search-style"); 757 var searchStyle = E("search-style");
620 if (!this.value) 758 if (!this.value)
621 searchStyle.innerHTML = ""; 759 searchStyle.innerHTML = "";
622 else 760 else
623 searchStyle.innerHTML = "#all-lang-table li:not([data-search*=\"" + this .value.toLowerCase() + "\"]) { display: none; }"; 761 searchStyle.innerHTML = "#all-lang-table li:not([data-search*=\"" + this .value.toLowerCase() + "\"]) { display: none; }";
624 } 762 }
625 763
626 function getKey(e)
627 {
628 // e.keyCode has been deprecated so we attempt to use e.key
629 if ("key" in e)
630 return e.key;
631 return getKey.keys[e.keyCode];
632 }
633 getKey.keys = {
634 9: "Tab",
635 13: "Enter",
636 27: "Escape"
637 };
638
639 // Initialize navigation sidebar 764 // Initialize navigation sidebar
640 ext.backgroundPage.sendMessage( 765 ext.backgroundPage.sendMessage(
641 { 766 {
642 type: "app.get", 767 type: "app.get",
643 what: "addonVersion" 768 what: "addonVersion"
644 }, 769 },
645 function(addonVersion) 770 function(addonVersion)
646 { 771 {
647 E("abp-version").textContent = addonVersion; 772 E("abp-version").textContent = addonVersion;
648 }); 773 });
649 getDocLink("releases", function(link) 774 getDocLink("releases", function(link)
650 { 775 {
651 E("link-version").setAttribute("href", link); 776 E("link-version").setAttribute("href", link);
652 }); 777 });
653 778
654 getDocLink("contribute", function(link)
655 {
656 document.querySelector("#tab-contribute a").setAttribute("href", link);
657 });
658
659 updateShareLink(); 779 updateShareLink();
660 updateTooltips(); 780 updateTooltips();
661 781
662 // Initialize interactive UI elements 782 // Initialize interactive UI elements
663 document.body.addEventListener("click", onClick, false); 783 document.body.addEventListener("click", onClick, false);
664 var placeholderValue = getMessage("options_dialog_language_find"); 784 var placeholderValue = getMessage("options_dialog_language_find");
665 E("find-language").setAttribute("placeholder", placeholderValue); 785 E("find-language").setAttribute("placeholder", placeholderValue);
666 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); 786 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false);
667 E("whitelisting-textbox").addEventListener("keypress", function(e) 787 E("whitelisting-textbox").addEventListener("keypress", function(e)
668 { 788 {
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
741 } 861 }
742 } 862 }
743 else if (e.target.classList.contains("focus-last")) 863 else if (e.target.classList.contains("focus-last"))
744 { 864 {
745 e.preventDefault(); 865 e.preventDefault();
746 this.querySelector(".focus-first").focus(); 866 this.querySelector(".focus-first").focus();
747 } 867 }
748 break; 868 break;
749 } 869 }
750 }, false); 870 }, false);
871
872 document.body.addEventListener("keyup", onKeyUp, true);
saroyanm 2016/07/25 17:13:34 Detail: I think this listener can be defined with
Thomas Greiner 2016/07/26 12:50:49 It's out of place here, I agree. I moved it to the
873 onHashChange();
751 } 874 }
752 875
753 var focusedBeforeDialog = null; 876 var focusedBeforeDialog = null;
754 function openDialog(name) 877 function openDialog(name)
755 { 878 {
756 var dialog = E("dialog"); 879 var dialog = E("dialog");
757 dialog.setAttribute("aria-hidden", false); 880 dialog.setAttribute("aria-hidden", false);
758 dialog.setAttribute("aria-labelledby", "dialog-title-" + name); 881 dialog.setAttribute("aria-labelledby", "dialog-title-" + name);
759 document.body.setAttribute("data-dialog", name); 882 document.body.setAttribute("data-dialog", name);
760 883
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after
998 case "safari_contentblocker": 1121 case "safari_contentblocker":
999 E("restart-safari").setAttribute("aria-hidden", value || initial); 1122 E("restart-safari").setAttribute("aria-hidden", value || initial);
1000 break; 1123 break;
1001 } 1124 }
1002 1125
1003 var checkbox = document.querySelector("[data-pref='" + key + "'] button[role ='checkbox']"); 1126 var checkbox = document.querySelector("[data-pref='" + key + "'] button[role ='checkbox']");
1004 if (checkbox) 1127 if (checkbox)
1005 checkbox.setAttribute("aria-checked", value); 1128 checkbox.setAttribute("aria-checked", value);
1006 } 1129 }
1007 1130
1008 function onShareLinkClick(e)
1009 {
1010 e.preventDefault();
1011
1012 getDocLink("share-general", openSharePopup);
1013 }
1014
1015 function updateShareLink() 1131 function updateShareLink()
1016 { 1132 {
1017 var shareResources = [ 1133 var shareResources = [
1018 "https://facebook.com/plugins/like.php?", 1134 "https://facebook.com/plugins/like.php?",
1019 "https://platform.twitter.com/widgets/", 1135 "https://platform.twitter.com/widgets/",
1020 "https://apis.google.com/se/0/_/+1/fastbutton?" 1136 "https://apis.google.com/se/0/_/+1/fastbutton?"
1021 ]; 1137 ];
1022 var isAnyBlocked = false; 1138 var isAnyBlocked = false;
1023 var checksRemaining = shareResources.length; 1139 var checksRemaining = shareResources.length;
1024 1140
1025 function onResult(isBlocked) 1141 function onResult(isBlocked)
1026 { 1142 {
1027 isAnyBlocked |= isBlocked; 1143 isAnyBlocked |= isBlocked;
1028 if (!--checksRemaining) 1144 if (!--checksRemaining)
1029 { 1145 {
1030 // Hide the share tab if a script on the share page would be blocked 1146 // Hide the share tab if a script on the share page would be blocked
1031 var tab = E("tab-share"); 1147 E("tab-share").hidden = isAnyBlocked;
1032 if (isAnyBlocked)
1033 {
1034 tab.hidden = true;
1035 tab.removeEventListener("click", onShareLinkClick, false);
1036 }
1037 else
1038 tab.addEventListener("click", onShareLinkClick, false);
1039 } 1148 }
1040 } 1149 }
1041 1150
1042 for (var i = 0; i < shareResources.length; i++) 1151 for (var i = 0; i < shareResources.length; i++)
1043 checkShareResource(shareResources[i], onResult); 1152 checkShareResource(shareResources[i], onResult);
1044 } 1153 }
1045 1154
1046 function getMessages(id) 1155 function getMessages(id)
1047 { 1156 {
1048 var messages = []; 1157 var messages = [];
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
1170 "shouldShowBlockElementMenu"] 1279 "shouldShowBlockElementMenu"]
1171 }); 1280 });
1172 ext.backgroundPage.sendMessage( 1281 ext.backgroundPage.sendMessage(
1173 { 1282 {
1174 type: "subscriptions.listen", 1283 type: "subscriptions.listen",
1175 filter: ["added", "disabled", "homepage", "lastDownload", "removed", 1284 filter: ["added", "disabled", "homepage", "lastDownload", "removed",
1176 "title", "downloadStatus", "downloading"] 1285 "title", "downloadStatus", "downloading"]
1177 }); 1286 });
1178 1287
1179 window.addEventListener("DOMContentLoaded", onDOMLoaded, false); 1288 window.addEventListener("DOMContentLoaded", onDOMLoaded, false);
1289 window.addEventListener("hashchange", onHashChange, false);
1180 })(); 1290 })();
OLDNEW

Powered by Google App Engine
This is Rietveld