(function () { var Dom = YAHOO.util.Dom, Event = YAHOO.util.Event, UA = YAHOO.env.ua, Lang = YAHOO.lang, TabViewPrototype = YAHOO.widget.TabView.prototype, fnTabViewInitAttributes = TabViewPrototype.initAttributes, fnTabViewAddTab = TabViewPrototype.addTab, m_bUseARIA = (UA.gecko && UA.gecko >= 1.9) || (UA.ie && UA.ie >= 8), m_oTabListItems = {}, m_oTabIndexMap = {}, // Private constants for strings _A = "A", _ARIA_PREFIX = "aria-", _USE_ARIA = "usearia", _ELEMENT = "element", _ACTIVE_TAB = "activeTab", _ROLE = "role", _PRESENTATION = "presentation", _TAB = "tab", _HREF = "href", _CONTENT_EL = "contentEl", _TAB_PANEL = "tabpanel", _LABELLED_BY = "labelledby", _DESCRIBED_BY = "describedby", _ACTIVE_INDEX = "activeIndex", _TAB_LIST = "tablist", _KEY_PRESS = "keypress", _KEY_DOWN = "keydown", _ID = "id"; var setARIARole = function (element, role) { element.setAttribute(_ROLE, role); }; var setARIAProperty = function (element, property, value) { element.setAttribute((_ARIA_PREFIX + property), value); }; // Returns the element representing each Tab in the TabView. var getTabAnchor = function (element) { var oTabAnchor; if (Dom.getAncestorByClassName(element, this.TAB_PARENT_CLASSNAME)) { if (element.nodeName.toUpperCase() === _A) { oTabAnchor = element; } else { oTabAnchor = Dom.getAncestorByTagName(element, _A); } } return oTabAnchor; }; var onKeyPress = function (event) { var oTabAnchor = getTabAnchor.call(this, Event.getTarget(event)), nCharCode = Event.getCharCode(event); if (oTabAnchor && (nCharCode === 13 || nCharCode === 32) && (oTabAnchor.parentNode !== this.get(_ACTIVE_TAB).get(_ELEMENT))) { this.set(_ACTIVE_INDEX, m_oTabIndexMap[this.get(_ID)][oTabAnchor.id]); } }; var onKeyDown = function (event) { var oCurrentTabAnchor = getTabAnchor.call(this, Event.getTarget(event)), aTabListItems = m_oTabListItems[this.get(_ID)], oCurrentTabLI, oNextTabLI, oNextTabAnchor; if (oCurrentTabAnchor) { oCurrentTabLI = oCurrentTabAnchor.parentNode; switch (Event.getCharCode(event)) { case 37: // Left case 38: // Up oNextTabLI = Dom.getPreviousSibling(oCurrentTabLI); if (!oNextTabLI) { oNextTabLI = aTabListItems[aTabListItems.length-1]; } break; case 39: // Right case 40: // Down oNextTabLI = Dom.getNextSibling(oCurrentTabLI); if (!oNextTabLI) { oNextTabLI = aTabListItems[0]; } break; } if (oNextTabLI) { oNextTabAnchor = Dom.getFirstChild(oNextTabLI); oCurrentTabAnchor.tabIndex = -1; oNextTabAnchor.tabIndex = 0; oNextTabAnchor.focus(); } } }; Lang.augmentObject(TabViewPrototype, { addTab: function (tab, index) { fnTabViewAddTab.apply(this, arguments); var sID = this.get(_ID), aTabListItems = m_oTabListItems[sID], oTabEl, oTabAnchor, oTabContentEl, sTabId; if (this.get(_USE_ARIA)) { oTabEl = tab.get(_ELEMENT); oTabAnchor = Dom.getFirstChild(oTabEl); // Set the "tabIndex" attribute of each Tab's element: The // "tabIndex" of the active Tab's element is set to 0, the others to -1. // This improves the keyboard accessibility of the TabView by placing // only one Tab in the browser's tab index by default, allowing the user // to easily skip over the control when navigating the page with the tab key. oTabAnchor.tabIndex = (this.get(_ACTIVE_TAB) === tab) ? 0 : -1; // Create a map that links the ids of each Tab's element to // the Tab's "index" attribute to make it possible to retrieve a Tab // instance reference by id. sTabId = oTabAnchor.id || Dom.generateId(oTabAnchor); m_oTabIndexMap[sID][sTabId] = this.getTabIndex(tab); // Need to set the "role" attribute of each Tab's
  • element to // "presentation" so that Window-Eyes recognizes that each Tab belongs to // the same TabList. Without this, Window-Eyes will announce each Tab as // being "1 of 1" as opposed to "1 of 3," or "2 of 3". setARIARole(oTabEl, _PRESENTATION); setARIARole(oTabAnchor, _TAB); // JAWS announces the value of the "href" attribute of each Tab's // element when it recieves focus. Ideally JAWS would allow the // applied "role" attribute of "tab" to take precedence over the default // role of the element like NVDA and Window-Eyes do. It is // possible to fix this problem by removing the "href" attribute from // the . oTabAnchor.removeAttribute(_HREF); oTabContentEl = tab.get(_CONTENT_EL); aTabListItems[aTabListItems.length] = oTabContentEl; setARIARole(oTabContentEl, _TAB_PANEL); // Set the "aria-labelledby" attribute for the TabPanel
  • element to // the id of its corresponding Tab's element. Doing so enables the // screen reader to announce the label of the Tab for each TabPanel when // the first element in a TabPanel receives focus, providing the user // with some context as to where they are. oTabContentEl.setAttribute(_LABELLED_BY, sTabId); } }, _setLabelledBy: function (id) { var oTabList = Dom.getFirstChild(this.get(_ELEMENT)); if (this.get(_USE_ARIA) && oTabList) { setARIAProperty(oTabList, _LABELLED_BY, id); } }, _setDescribedBy: function (id) { var oTabList = Dom.getFirstChild(this.get(_ELEMENT)); if (this.get(_USE_ARIA) && oTabList) { setARIAProperty(oTabList, _DESCRIBED_BY, id); } }, _setUseARIA: function (p_bUseARIA) { var oTabList, sID; if (p_bUseARIA) { sID = this.get(_ID); if (!sID) { this.set(_ID, Dom.generateId()); sID = this.get(_ID); } m_oTabListItems[sID] = []; m_oTabIndexMap[sID] = {}; oTabList = Dom.getFirstChild(this.get(_ELEMENT)); // Set the "role" attribute of the