data tables are going to need some work yet, but the other stuff seems to be working 100%
352 lines
No EOL
7 KiB
JavaScript
352 lines
No EOL
7 KiB
JavaScript
(function () {
|
|
|
|
var Event = YAHOO.util.Event,
|
|
Dom = YAHOO.util.Dom,
|
|
Lang = YAHOO.lang,
|
|
UA = YAHOO.env.ua,
|
|
ContextMenu = YAHOO.widget.ContextMenu,
|
|
|
|
MenuPrototype = YAHOO.widget.Menu.prototype,
|
|
fnMenuInitDefaultConfig = MenuPrototype.initDefaultConfig,
|
|
|
|
// The currently focused MenuItem label, or the MenuItem label that can be focused
|
|
// by the user.
|
|
|
|
m_oCurrentItemLabel,
|
|
|
|
|
|
// Private constants for strings
|
|
|
|
_ARIA_PREFIX = "aria-",
|
|
_HAS_POPUP = "haspopup",
|
|
_ROLE = "role",
|
|
_PRESENTATION = "presentation",
|
|
_MENUITEM = "menuitem",
|
|
_HREF = "href",
|
|
_SUBMENU = "submenu",
|
|
_MENU = "menu",
|
|
_MENUBAR = "menubar",
|
|
_LABELLED_BY = "labelledby",
|
|
_FOCUS = "focus",
|
|
_BLUR = "blur",
|
|
_ITEM_ADDED = "itemAdded",
|
|
_USE_ARIA = "usearia",
|
|
_TRIGGER = "trigger";
|
|
|
|
|
|
// Menu ARIA plugin
|
|
|
|
var setARIARole = function (element, role) {
|
|
|
|
element.setAttribute(_ROLE, role);
|
|
|
|
};
|
|
|
|
|
|
var setARIAProperty = function (element, property, value) {
|
|
|
|
element.setAttribute((_ARIA_PREFIX + property), value);
|
|
|
|
};
|
|
|
|
|
|
var addHasPopupRole = function (element) {
|
|
|
|
if (element.nodeType === 1) {
|
|
setARIAProperty(element, _HAS_POPUP, true);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var removeHasPopupRole = function (element) {
|
|
|
|
if (element.nodeType === 1) {
|
|
element.removeAttribute(_ARIA_PREFIX + _HAS_POPUP);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var onDocumentFocus = function (event) {
|
|
|
|
// The currently focused element
|
|
|
|
var oTarget = Event.getTarget(event);
|
|
|
|
|
|
if (Dom.isAncestor(this.element, oTarget)) {
|
|
|
|
/*
|
|
Modify value of the tabIndex attribute so that the currently
|
|
focused MenuItem label is in the browser's default tab order.
|
|
*/
|
|
|
|
if (m_oCurrentItemLabel) {
|
|
m_oCurrentItemLabel.tabIndex = -1;
|
|
}
|
|
|
|
m_oCurrentItemLabel = oTarget;
|
|
m_oCurrentItemLabel.tabIndex = 0;
|
|
|
|
}
|
|
else if (m_oCurrentItemLabel && this.getItems().length > 0) {
|
|
|
|
/*
|
|
If the focus has moved to an element on the page that is not a
|
|
part of the MenuBar, restore the MenuBar to its original state
|
|
so that the first item is in the browser's default tab index.
|
|
*/
|
|
|
|
m_oCurrentItemLabel.tabIndex = -1;
|
|
|
|
m_oCurrentItemLabel = Dom.getFirstChild(this.getItem(0).element);
|
|
m_oCurrentItemLabel.tabIndex = 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var onKeyPress = function (type, args) {
|
|
|
|
var oEvent = args[0],
|
|
oMenuItem = args[1],
|
|
oTarget = Event.getTarget(oEvent),
|
|
nCharCode = Event.getCharCode(oEvent);
|
|
|
|
|
|
if (oMenuItem && nCharCode === 13) {
|
|
|
|
if (UA.ie) {
|
|
oTarget.fireEvent("onclick");
|
|
}
|
|
else {
|
|
|
|
oEvent = document.createEvent("HTMLEvents");
|
|
oEvent.initEvent("click", true, true);
|
|
|
|
oTarget.dispatchEvent(oEvent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var onConfigSubmenu = function (type, args) {
|
|
|
|
var oSubmenu = args[0];
|
|
|
|
if (oSubmenu) {
|
|
addHasPopupRole(Dom.getFirstChild(this.element));
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var addMenuItemRole = function (menuitem) {
|
|
|
|
var oMenuItemEl = menuitem.element;
|
|
|
|
// For NVDA: add the role of "presentation" to reach LI and UL
|
|
// element to prevent NVDA from announcing the "list" role
|
|
// as well as the menu-related roles.
|
|
|
|
setARIARole(oMenuItemEl.parentNode, _PRESENTATION);
|
|
setARIARole(oMenuItemEl, _PRESENTATION);
|
|
|
|
|
|
// Retrieve a reference to the anchor element that serves as the
|
|
// label for each MenuItem.
|
|
|
|
var oMenuItemLabel = Dom.getFirstChild(oMenuItemEl);
|
|
|
|
|
|
// Set the "role" attribute of the label to "menuitem"
|
|
|
|
setARIARole(oMenuItemLabel, _MENUITEM);
|
|
|
|
|
|
// Remove the label from the browser's default tab order
|
|
|
|
oMenuItemLabel.tabIndex = -1;
|
|
|
|
|
|
// JAWS & NVDA announce the value of each anchor
|
|
// element's "href" attribute when it recieves focus, so remove
|
|
// the attribute so that its value isn't announced.
|
|
|
|
oMenuItemLabel.removeAttribute(_HREF);
|
|
|
|
|
|
// If the MenuItem has a submenu, set the "aria-haspopup"
|
|
// attribute to true so that the screen reader can announce
|
|
|
|
if (menuitem.cfg.getProperty(_SUBMENU)) {
|
|
addHasPopupRole(oMenuItemLabel);
|
|
}
|
|
else {
|
|
menuitem.cfg.subscribeToConfigEvent(_SUBMENU, onConfigSubmenu);
|
|
}
|
|
|
|
};
|
|
|
|
|
|
var onItemAdded = function (type, args) {
|
|
|
|
addMenuItemRole(args[0]);
|
|
|
|
};
|
|
|
|
|
|
MenuPrototype.configUseARIA = function (type, args) {
|
|
|
|
var bUseARIA = args[0],
|
|
oParent = this.parent,
|
|
oElement = this.element,
|
|
sMenuRole = (this instanceof YAHOO.widget.MenuBar) ? _MENUBAR : _MENU,
|
|
aMenuItems,
|
|
nMenuItems,
|
|
oMenuItem,
|
|
oMenuItemLabel,
|
|
sId,
|
|
i;
|
|
|
|
|
|
if (bUseARIA) {
|
|
|
|
// Apply the "role" attribute of "menu" or "menubar" depending on the
|
|
// type of the Menu control being rendered.
|
|
|
|
setARIARole(oElement, sMenuRole);
|
|
|
|
|
|
|
|
// Use the "aria-labelledby" attribute to label each submenu. This
|
|
// will provide the user with the name of the submenu the first time
|
|
// one of its items receives focus.
|
|
|
|
if (oParent) {
|
|
|
|
sId = Dom.generateId(Dom.getFirstChild(oParent.element));
|
|
|
|
setARIAProperty(oElement, _LABELLED_BY, sId);
|
|
|
|
}
|
|
|
|
|
|
// Apply the appropriate "role" and "aria-[state]" attributes to the
|
|
// label of each MenuItem instance.
|
|
|
|
aMenuItems = this.getItems();
|
|
nMenuItems = aMenuItems.length;
|
|
|
|
if (nMenuItems > 0) {
|
|
|
|
i = nMenuItems - 1;
|
|
|
|
|
|
do {
|
|
oMenuItem = aMenuItems[i];
|
|
addMenuItemRole(oMenuItem);
|
|
i = i - 1;
|
|
}
|
|
while ((i > -1));
|
|
|
|
|
|
// Set the "tabindex" of the first MenuItem's label to 0 so the user
|
|
// can easily tab into and out of the control.
|
|
|
|
if (this.getRoot() === this) {
|
|
m_oCurrentItemLabel = Dom.getFirstChild(this.getItem(0).element);
|
|
m_oCurrentItemLabel.tabIndex = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (this === this.getRoot()) {
|
|
Event.onFocus(document, onDocumentFocus, null, this);
|
|
}
|
|
|
|
|
|
this.subscribe(_ITEM_ADDED, onItemAdded);
|
|
this.subscribe("keypress", onKeyPress);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// ContextMenu ARIA plugin
|
|
|
|
var m_oTriggers = {};
|
|
|
|
var toggleARIAForTrigger = function () {
|
|
|
|
var sMenuId = this.element.id,
|
|
oTrigger = this.cfg.getProperty(_TRIGGER),
|
|
oCurrentTrigger = m_oTriggers[sMenuId],
|
|
oElement;
|
|
|
|
|
|
if (oCurrentTrigger) {
|
|
|
|
if (Lang.isString(oCurrentTrigger)) { // String id
|
|
|
|
oElement = Dom.get(oCurrentTrigger);
|
|
|
|
if (oElement) {
|
|
removeHasPopupRole(oElement);
|
|
}
|
|
|
|
}
|
|
else if (oCurrentTrigger.nodeType === 1) { // Element reference
|
|
removeHasPopupRole(oCurrentTrigger);
|
|
}
|
|
else if (oCurrentTrigger.length) { // NodeList or Array
|
|
Dom.batch(oCurrentTrigger, removeHasPopupRole);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (oTrigger) {
|
|
|
|
if (Lang.isString(oTrigger)) { // String id
|
|
|
|
oElement = Dom.get(oTrigger);
|
|
|
|
if (oElement) {
|
|
addHasPopupRole(oElement);
|
|
}
|
|
|
|
}
|
|
else if (oTrigger.nodeType === 1) { // Element reference
|
|
addHasPopupRole(oTrigger);
|
|
}
|
|
else if (oTrigger.length) { // NodeList or Array
|
|
Dom.batch(oTrigger, addHasPopupRole);
|
|
}
|
|
|
|
m_oTriggers[sMenuId] = oTrigger;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
ContextMenu.prototype.configUseARIA = function (type, args) {
|
|
|
|
ContextMenu.superclass.configUseARIA.apply(this, arguments);
|
|
|
|
toggleARIAForTrigger.call(this);
|
|
|
|
this.cfg.subscribeToConfigEvent(_TRIGGER, toggleARIAForTrigger);
|
|
|
|
};
|
|
|
|
}()); |