Yahoo! UI Library

Menu  2.9.0

Yahoo! UI Library > menu > menuariaplugin.js (source view)
Search:
 
Filters
(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 onFocus = function (type, args) {
    
        var oEvent = args[0],
            oTarget = Event.getTarget(oEvent);	// The currently focused element

        // 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;

    };
    

    var onBlur = function (type, args) {

        var oEvent = args[0],
            oTarget = Event.getTarget(oEvent);	// The currently focused element

        // If the focus has moved to an element on the page that is not a 
        // part of the Menu, restore the Menu 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 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]);
    
    };


    Lang.augmentObject(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()) {
                    this.subscribe(_FOCUS, onFocus);
                    this.subscribe(_BLUR, onBlur);
                }
                
                this.subscribe(_ITEM_ADDED, onItemAdded);
        
            }	
    
        },
    
    
        initDefaultConfig: function () {
        
            fnMenuInitDefaultConfig.call(this);
        
            this.cfg.addProperty(
                _USE_ARIA, 
                {
                    handler: this.configUseARIA, 
                    value: (UA.gecko && UA.gecko >= 1.9) || (UA.ie && UA.ie >= 8), 
                    validator: Lang.isBoolean
                }
             );
    
        }
    
    }, "initDefaultConfig", "configUseARIA");



    // 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);
    
    };

}());

Copyright © 2011 Yahoo! Inc. All rights reserved.