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

Delta Between Two Patch Sets: options.js

Issue 29332808: Issue 2408 - Improved accessibility of checkboxes in options page (Closed)
Left Patch Set: Created Dec. 16, 2015, 1:31 p.m.
Right Patch Set: Reverted styles for Advanced tab Created Jan. 25, 2016, 6:02 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 | « options.html ('k') | skin/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-2015 Eyeo GmbH 3 * Copyright (C) 2006-2015 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 13 matching lines...) Expand all
24 var filtersMap = Object.create(null); 24 var filtersMap = Object.create(null);
25 var collections = Object.create(null); 25 var collections = Object.create(null);
26 var maxLabelId = 0; 26 var maxLabelId = 0;
27 27
28 function Collection(details) 28 function Collection(details)
29 { 29 {
30 this.details = details; 30 this.details = details;
31 this.items = []; 31 this.items = [];
32 } 32 }
33 33
34 Collection.prototype._setEmpty = function(table, text)
35 {
36 var placeholder = table.querySelector(".empty-placeholder");
37 if (text && !placeholder)
38 {
39 placeholder = document.createElement("li");
40 placeholder.className = "empty-placeholder";
41 placeholder.textContent = ext.i18n.getMessage(text);
42 table.appendChild(placeholder);
43 }
44 else if (placeholder)
45 table.removeChild(placeholder);
46 }
47
34 Collection.prototype.addItems = function() 48 Collection.prototype.addItems = function()
35 { 49 {
36 var length = Array.prototype.push.apply(this.items, arguments); 50 var length = Array.prototype.push.apply(this.items, arguments);
37 if (length == 0) 51 if (length == 0)
38 return; 52 return;
39 53
40 this.items.sort(function(a, b) 54 this.items.sort(function(a, b)
41 { 55 {
42 var aValue = (a.title || a.text || a.url).toLowerCase(); 56 var aValue = (a.title || a.text || a.url).toLowerCase();
43 var bValue = (b.title || b.text || b.url).toLowerCase(); 57 var bValue = (b.title || b.text || b.url).toLowerCase();
44 return aValue.localeCompare(bValue); 58 return aValue.localeCompare(bValue);
45 }); 59 });
46 60
47 for (var j = 0; j < this.details.length; j++) 61 for (var j = 0; j < this.details.length; j++)
48 { 62 {
49 var table = E(this.details[j].id); 63 var table = E(this.details[j].id);
50 var template = table.querySelector("template"); 64 var template = table.querySelector("template");
51 for (var i = 0; i < arguments.length; i++) 65 for (var i = 0; i < arguments.length; i++)
52 { 66 {
53 var item = arguments[i]; 67 var item = arguments[i];
54 var listItem = document.createElement("li"); 68 var listItem = document.createElement("li");
55 listItem.appendChild(document.importNode(template.content, true)); 69 listItem.appendChild(document.importNode(template.content, true));
56 listItem.setAttribute("data-access", item.url || item.text); 70 listItem.setAttribute("data-access", item.url || item.text);
57 71
58 var labelId = "label-" + (++maxLabelId); 72 var labelId = "label-" + (++maxLabelId);
59 var label = listItem.querySelector(".display"); 73 listItem.querySelector(".display").setAttribute("id", labelId);
60 label.setAttribute("id", labelId);
61 var control = listItem.querySelector(".control"); 74 var control = listItem.querySelector(".control");
62 if (control) 75 if (control)
63 { 76 {
64 // We use aria-labelledby to avoid triggering the control when 77 // We use aria-labelledby to avoid triggering the control when
65 // interacting with the label 78 // interacting with the label
66 control.setAttribute("aria-labelledby", labelId); 79 control.setAttribute("aria-labelledby", labelId);
67 control.addEventListener("click", this.details[j].onClick, false); 80 control.addEventListener("click", this.details[j].onClick, false);
68 } 81 }
69 82
83 this._setEmpty(table, null);
70 if (table.hasChildNodes()) 84 if (table.hasChildNodes())
71 table.insertBefore(listItem, table.childNodes[this.items.indexOf(item) ]); 85 table.insertBefore(listItem, table.childNodes[this.items.indexOf(item) ]);
72 else 86 else
73 table.appendChild(listItem); 87 table.appendChild(listItem);
74 this.updateItem(item); 88 this.updateItem(item);
75 } 89 }
76 } 90 }
77 return length; 91 return length;
78 }; 92 };
79 93
80 Collection.prototype.removeItem = function(item) 94 Collection.prototype.removeItem = function(item)
81 { 95 {
82 var index = this.items.indexOf(item); 96 var index = this.items.indexOf(item);
83 if (index == -1) 97 if (index == -1)
84 return; 98 return;
85 99
86 this.items.splice(index, 1); 100 this.items.splice(index, 1);
87 var access = (item.url || item.text).replace(/'/g, "\\'");
88 for (var i = 0; i < this.details.length; i++) 101 for (var i = 0; i < this.details.length; i++)
89 { 102 {
90 var table = E(this.details[i].id); 103 var table = E(this.details[i].id);
91 var element = table.querySelector("[data-access='" + access + "']"); 104 var element = table.childNodes[index];
92 105
93 // Element gets removed so make sure to handle focus appropriately 106 // Element gets removed so make sure to handle focus appropriately
94 var control = element.querySelector(".control"); 107 var control = element.querySelector(".control");
95 if (control && control == document.activeElement) 108 if (control && control == document.activeElement)
96 { 109 {
97 if (!focusNextElement(element.parentElement, control)) 110 if (!focusNextElement(element.parentElement, control))
98 { 111 {
99 // Fall back to next focusable element within same tab 112 // Fall back to next focusable element within same tab or dialog
100 var tab = element.parentElement; 113 var focusableElement = element.parentElement;
101 while (true) 114 while (focusableElement)
102 { 115 {
103 if (tab.classList.contains("tab-content")) 116 if (focusableElement.classList.contains("tab-content")
117 || focusableElement.classList.contains("dialog-content"))
104 break; 118 break;
105 119
106 tab = tab.parentElement; 120 focusableElement = focusableElement.parentElement;
107 if (!tab)
108 {
109 tab = document;
110 break;
111 }
112 } 121 }
113 focusNextElement(tab, control); 122 focusNextElement(focusableElement || document, control);
114 } 123 }
115 } 124 }
116 125
117 element.parentElement.removeChild(element); 126 element.parentElement.removeChild(element);
127 if (this.items.length == 0)
128 this._setEmpty(table, this.details[i].emptyText);
118 } 129 }
119 }; 130 };
120 131
121 Collection.prototype.updateItem = function(item) 132 Collection.prototype.updateItem = function(item)
122 { 133 {
123 var access = (item.url || item.text).replace(/'/g, "\\'"); 134 var access = (item.url || item.text).replace(/'/g, "\\'");
124 for (var i = 0; i < this.details.length; i++) 135 for (var i = 0; i < this.details.length; i++)
125 { 136 {
126 var table = E(this.details[i].id); 137 var table = E(this.details[i].id);
127 var element = table.querySelector("[data-access='" + access + "']"); 138 var element = table.querySelector("[data-access='" + access + "']");
128 if (!element) 139 if (!element)
129 continue; 140 continue;
130 141
131 var text = item.title || item.url || item.text; 142 var text = item.title || item.url || item.text;
132 element.querySelector(".display").textContent = text; 143 element.querySelector(".display").textContent = text;
133 if (text) 144 if (text)
134 element.setAttribute("data-search", text.toLowerCase()); 145 element.setAttribute("data-search", text.toLowerCase());
135 var control = element.querySelector(".control[role='checkbox']"); 146 var control = element.querySelector(".control[role='checkbox']");
136 if (control) 147 if (control)
137 control.setAttribute("aria-checked", item.disabled == false); 148 control.setAttribute("aria-checked", item.disabled == false);
138 } 149 }
139 }; 150 };
140 151
141 Collection.prototype.clearAll = function() 152 Collection.prototype.clearAll = function()
142 { 153 {
154 this.items = [];
143 for (var i = 0; i < this.details.length; i++) 155 for (var i = 0; i < this.details.length; i++)
144 { 156 {
145 var table = E(this.details[i].id); 157 var table = E(this.details[i].id);
146 var template = table.querySelector("template"); 158 var template = table.querySelector("template");
147 table.innerHTML = ""; 159 table.innerHTML = "";
148 table.appendChild(template); 160 table.appendChild(template);
149 } 161 this._setEmpty(table, this.details[i].emptyText);
150 this.items.length = 0; 162 }
151 }; 163 };
152 164
153 function focusNextElement(container, currentElement) 165 function focusNextElement(container, currentElement)
154 { 166 {
155 var focusables = container.querySelectorAll("a, button, .control"); 167 var focusables = container.querySelectorAll("a, button, input, .control");
156 focusables = Array.prototype.slice.call(focusables); 168 focusables = Array.prototype.slice.call(focusables);
157 var index = focusables.indexOf(currentElement); 169 var index = focusables.indexOf(currentElement);
158 if (index + 1 < focusables.length) 170 index += (index == focusables.length - 1) ? -1 : 1;
159 index += 1;
160 else if (index < focusables.length)
161 index -= 1;
162 171
163 var nextElement = focusables[index]; 172 var nextElement = focusables[index];
164 if (!nextElement) 173 if (!nextElement)
165 return false; 174 return false;
166 175
167 nextElement.focus(); 176 nextElement.focus();
168 return true; 177 return true;
169 } 178 }
170 179
171 function onToggleSubscriptionClick(e) 180 function onToggleSubscriptionClick(e)
172 { 181 {
173 e.preventDefault(); 182 e.preventDefault();
174 var checkbox = e.target; 183 var checkbox = e.target;
175 var subscriptionUrl = checkbox.parentElement.getAttribute("data-access"); 184 var subscriptionUrl = checkbox.parentElement.getAttribute("data-access");
176 if (checkbox.getAttribute("aria-checked") == "true") 185 if (checkbox.getAttribute("aria-checked") == "true")
177 { 186 {
178 ext.backgroundPage.sendMessage( 187 ext.backgroundPage.sendMessage({
179 {
180 type: "subscriptions.remove", 188 type: "subscriptions.remove",
181 url: subscriptionUrl 189 url: subscriptionUrl
182 }); 190 });
183 } 191 }
184 else 192 else
185 addEnableSubscription(subscriptionUrl); 193 addEnableSubscription(subscriptionUrl);
186 } 194 }
187 195
188 function onAddLanguageSubscriptionClick(e) 196 function onAddLanguageSubscriptionClick(e)
189 { 197 {
(...skipping 16 matching lines...) Expand all
206 [ 214 [
207 { 215 {
208 id: "recommend-list-table", 216 id: "recommend-list-table",
209 onClick: onToggleSubscriptionClick 217 onClick: onToggleSubscriptionClick
210 } 218 }
211 ]); 219 ]);
212 collections.langs = new Collection( 220 collections.langs = new Collection(
213 [ 221 [
214 { 222 {
215 id: "blocking-languages-table", 223 id: "blocking-languages-table",
224 emptyText: "options_dialog_language_added_empty",
216 onClick: onToggleSubscriptionClick 225 onClick: onToggleSubscriptionClick
217 }, 226 },
218 { 227 {
219 id: "blocking-languages-dialog-table" 228 id: "blocking-languages-dialog-table",
229 emptyText: "options_dialog_language_added_empty"
220 } 230 }
221 ]); 231 ]);
222 collections.allLangs = new Collection( 232 collections.allLangs = new Collection(
223 [ 233 [
224 { 234 {
225 id: "all-lang-table", 235 id: "all-lang-table",
236 emptyText: "options_dialog_language_other_empty",
226 onClick: onAddLanguageSubscriptionClick 237 onClick: onAddLanguageSubscriptionClick
227 } 238 }
228 ]); 239 ]);
229 collections.acceptableAds = new Collection( 240 collections.acceptableAds = new Collection(
230 [ 241 [
231 { 242 {
232 id: "acceptableads-table", 243 id: "acceptableads-table",
233 onClick: onToggleSubscriptionClick 244 onClick: onToggleSubscriptionClick
234 } 245 }
235 ]); 246 ]);
236 collections.custom = new Collection( 247 collections.custom = new Collection(
237 [ 248 [
238 { 249 {
239 id: "custom-list-table", 250 id: "custom-list-table",
240 onClick: onToggleSubscriptionClick 251 onClick: onToggleSubscriptionClick
241 } 252 }
242 ]); 253 ]);
243 collections.whitelist = new Collection( 254 collections.whitelist = new Collection(
244 [ 255 [
245 { 256 {
246 id: "whitelisting-table", 257 id: "whitelisting-table",
258 emptyText: "options_whitelisted_empty",
247 onClick: onRemoveFilterClick 259 onClick: onRemoveFilterClick
248 } 260 }
249 ]); 261 ]);
250 collections.customFilters = new Collection( 262 collections.customFilters = new Collection(
251 [ 263 [
252 { 264 {
253 id: "custom-filters-table" 265 id: "custom-filters-table",
266 emptyText: "options_customFilters_empty"
254 } 267 }
255 ]); 268 ]);
256 269
257 function updateSubscription(subscription) 270 function updateSubscription(subscription)
258 { 271 {
259 var subscriptionUrl = subscription.url; 272 var subscriptionUrl = subscription.url;
260 var knownSubscription = subscriptionsMap[subscriptionUrl]; 273 var knownSubscription = subscriptionsMap[subscriptionUrl];
261 if (knownSubscription) 274 if (knownSubscription)
262 knownSubscription.disabled = subscription.disabled; 275 knownSubscription.disabled = subscription.disabled;
263 else 276 else
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 var title = dialog.querySelector("h3").textContent; 427 var title = dialog.querySelector("h3").textContent;
415 var url = dialog.querySelector(".url").textContent; 428 var url = dialog.querySelector(".url").textContent;
416 addEnableSubscription(url, title); 429 addEnableSubscription(url, title);
417 closeDialog(); 430 closeDialog();
418 break; 431 break;
419 case "cancel-custom-filters": 432 case "cancel-custom-filters":
420 E("custom-filters").classList.remove("mode-edit"); 433 E("custom-filters").classList.remove("mode-edit");
421 break; 434 break;
422 case "cancel-domain-exception": 435 case "cancel-domain-exception":
423 E("whitelisting-textbox").value = ""; 436 E("whitelisting-textbox").value = "";
437 document.querySelector("#whitelisting .controls").classList.remove("mo de-edit");
424 break; 438 break;
425 case "close-dialog": 439 case "close-dialog":
426 closeDialog(); 440 closeDialog();
427 break; 441 break;
428 case "edit-custom-filters": 442 case "edit-custom-filters":
429 E("custom-filters").classList.add("mode-edit"); 443 E("custom-filters").classList.add("mode-edit");
430 editCustomFilters(); 444 editCustomFilters();
445 break;
446 case "edit-domain-exception":
447 document.querySelector("#whitelisting .controls").classList.add("mode- edit");
448 E("whitelisting-textbox").focus();
431 break; 449 break;
432 case "import-subscription": 450 case "import-subscription":
433 var url = E("blockingList-textbox").value; 451 var url = E("blockingList-textbox").value;
434 addEnableSubscription(url); 452 addEnableSubscription(url);
435 closeDialog(); 453 closeDialog();
436 break; 454 break;
437 case "open-dialog": 455 case "open-dialog":
438 openDialog(element.getAttribute("data-dialog")); 456 openDialog(element.getAttribute("data-dialog"));
439 break; 457 break;
440 case "save-custom-filters": 458 case "save-custom-filters":
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
652 if (domain.value) 670 if (domain.value)
653 { 671 {
654 ext.backgroundPage.sendMessage( 672 ext.backgroundPage.sendMessage(
655 { 673 {
656 type: "filters.add", 674 type: "filters.add",
657 text: "@@||" + domain.value.toLowerCase() + "^$document" 675 text: "@@||" + domain.value.toLowerCase() + "^$document"
658 }); 676 });
659 } 677 }
660 678
661 domain.value = ""; 679 domain.value = "";
680 document.querySelector("#whitelisting .controls").classList.remove("mode-edi t");
662 } 681 }
663 682
664 function editCustomFilters() 683 function editCustomFilters()
665 { 684 {
666 var customFilterItems = collections.customFilters.items; 685 var customFilterItems = collections.customFilters.items;
667 var filterTexts = []; 686 var filterTexts = [];
668 for (var i = 0; i < customFilterItems.length; i++) 687 for (var i = 0; i < customFilterItems.length; i++)
669 filterTexts.push(customFilterItems[i].text); 688 filterTexts.push(customFilterItems[i].text);
670 E("custom-filters-raw").value = filterTexts.join("\n"); 689 E("custom-filters-raw").value = filterTexts.join("\n");
671 } 690 }
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after
848 filter: ["added", "loaded", "removed"] 867 filter: ["added", "loaded", "removed"]
849 }); 868 });
850 ext.backgroundPage.sendMessage( 869 ext.backgroundPage.sendMessage(
851 { 870 {
852 type: "subscriptions.listen", 871 type: "subscriptions.listen",
853 filter: ["added", "disabled", "homepage", "removed", "title"] 872 filter: ["added", "disabled", "homepage", "removed", "title"]
854 }); 873 });
855 874
856 window.addEventListener("DOMContentLoaded", onDOMLoaded, false); 875 window.addEventListener("DOMContentLoaded", onDOMLoaded, false);
857 })(); 876 })();
LEFTRIGHT

Powered by Google App Engine
This is Rietveld