Index: options.js |
=================================================================== |
--- a/options.js |
+++ b/options.js |
@@ -23,6 +23,7 @@ |
var recommendationsMap = Object.create(null); |
var filtersMap = Object.create(null); |
var collections = Object.create(null); |
+ var maxLabelId = 0; |
function Collection(details) |
{ |
@@ -50,25 +51,27 @@ |
for (var i = 0; i < arguments.length; i++) |
{ |
var item = arguments[i]; |
- var text = item.title || item.url || item.text; |
var listItem = document.createElement("li"); |
listItem.appendChild(document.importNode(template.content, true)); |
listItem.setAttribute("data-access", item.url || item.text); |
- listItem.querySelector(".display").textContent = text; |
- if (text) |
- listItem.setAttribute("data-search", text.toLowerCase()); |
+ var labelId = "label-" + (++maxLabelId); |
+ var label = listItem.querySelector(".display"); |
+ label.setAttribute("id", labelId); |
var control = listItem.querySelector(".control"); |
if (control) |
{ |
+ // We use aria-labelledby to avoid triggering the control when |
+ // interacting with the label |
+ control.setAttribute("aria-labelledby", labelId); |
control.addEventListener("click", this.details[j].onClick, false); |
- control.checked = item.disabled == false; |
} |
if (table.hasChildNodes()) |
table.insertBefore(listItem, table.childNodes[this.items.indexOf(item)]); |
else |
table.appendChild(listItem); |
+ this.updateItem(item); |
} |
} |
return length; |
@@ -86,10 +89,55 @@ |
{ |
var table = E(this.details[i].id); |
var element = table.querySelector("[data-access='" + access + "']"); |
+ |
+ // Element gets removed so make sure to handle focus appropriately |
+ var control = element.querySelector(".control"); |
+ if (control && control == document.activeElement) |
+ { |
+ if (!focusNextElement(element.parentElement, control)) |
+ { |
+ // Fall back to next focusable element within same tab |
+ var tab = element.parentElement; |
+ while (true) |
+ { |
+ if (tab.classList.contains("tab-content")) |
+ break; |
+ |
+ tab = tab.parentElement; |
+ if (!tab) |
+ { |
+ tab = document; |
+ break; |
+ } |
+ } |
+ focusNextElement(tab, control); |
+ } |
+ } |
+ |
element.parentElement.removeChild(element); |
} |
}; |
+ Collection.prototype.updateItem = function(item) |
+ { |
+ var access = (item.url || item.text).replace(/'/g, "\\'"); |
+ for (var i = 0; i < this.details.length; i++) |
+ { |
+ var table = E(this.details[i].id); |
+ var element = table.querySelector("[data-access='" + access + "']"); |
+ if (!element) |
+ continue; |
+ |
+ var text = item.title || item.url || item.text; |
+ element.querySelector(".display").textContent = text; |
+ if (text) |
+ element.setAttribute("data-search", text.toLowerCase()); |
+ var control = element.querySelector(".control[role='checkbox']"); |
+ if (control) |
+ control.setAttribute("aria-checked", item.disabled == false); |
+ } |
+ }; |
+ |
Collection.prototype.clearAll = function() |
{ |
for (var i = 0; i < this.details.length; i++) |
@@ -102,17 +150,36 @@ |
this.items.length = 0; |
}; |
+ function focusNextElement(container, currentElement) |
+ { |
+ var focusables = container.querySelectorAll("a, button, .control"); |
+ focusables = Array.prototype.slice.call(focusables); |
+ var index = focusables.indexOf(currentElement); |
+ if (index + 1 < focusables.length) |
+ index += 1; |
+ else if (index < focusables.length) |
+ index -= 1; |
+ |
+ var nextElement = focusables[index]; |
+ if (!nextElement) |
+ return false; |
+ |
+ nextElement.focus(); |
+ return true; |
+ } |
+ |
function onToggleSubscriptionClick(e) |
{ |
e.preventDefault(); |
- var subscriptionUrl = e.target.parentNode.getAttribute("data-access"); |
- if (!e.target.checked) |
+ var checkbox = e.target; |
+ var subscriptionUrl = checkbox.parentElement.getAttribute("data-access"); |
+ if (checkbox.getAttribute("aria-checked") == "true") |
{ |
- ext.backgroundPage.sendMessage( |
- { |
- type: "subscriptions.remove", |
- url: subscriptionUrl |
- }); |
+ ext.backgroundPage.sendMessage( |
+ { |
+ type: "subscriptions.remove", |
+ url: subscriptionUrl |
+ }); |
} |
else |
addEnableSubscription(subscriptionUrl); |
@@ -155,28 +222,28 @@ |
collections.allLangs = new Collection( |
[ |
{ |
- id: "all-lang-table", |
+ id: "all-lang-table", |
onClick: onAddLanguageSubscriptionClick |
} |
]); |
collections.acceptableAds = new Collection( |
[ |
{ |
- id: "acceptableads-table", |
+ id: "acceptableads-table", |
onClick: onToggleSubscriptionClick |
} |
]); |
collections.custom = new Collection( |
[ |
{ |
- id: "custom-list-table", |
+ id: "custom-list-table", |
onClick: onToggleSubscriptionClick |
} |
]); |
collections.whitelist = new Collection( |
[ |
{ |
- id: "whitelisting-table", |
+ id: "whitelisting-table", |
onClick: onRemoveFilterClick |
} |
]); |
@@ -199,30 +266,21 @@ |
{ |
function onObjectChanged() |
{ |
- var access = (subscriptionUrl || subscription.text).replace(/'/g, "\\'"); |
- var elements = document.querySelectorAll("[data-access='" + access + "']"); |
- for (var i = 0; i < elements.length; i++) |
+ for (var i in collections) |
+ collections[i].updateItem(subscription); |
+ |
+ var recommendation = recommendationsMap[subscriptionUrl]; |
+ if (recommendation && recommendation.type == "ads") |
{ |
- var element = elements[i]; |
- var control = element.querySelector(".control"); |
- if (control.localName == "input") |
- control.checked = subscription.disabled == false; |
- if (subscriptionUrl in recommendationsMap) |
+ if (subscription.disabled == false) |
{ |
- var recommendation = recommendationsMap[subscriptionUrl]; |
- if (recommendation.type == "ads") |
- { |
- if (subscription.disabled == false) |
- { |
- collections.allLangs.removeItem(subscription); |
- collections.langs.addItems(subscription); |
- } |
- else |
- { |
- collections.allLangs.addItems(subscription); |
- collections.langs.removeItem(subscription); |
- } |
- } |
+ collections.allLangs.removeItem(subscription); |
+ collections.langs.addItems(subscription); |
+ } |
+ else |
+ { |
+ collections.allLangs.addItems(subscription); |
+ collections.langs.removeItem(subscription); |
} |
} |
} |