source: Dev/branches/rest-dojo-ui/client/dijit/a11y.js @ 274

Last change on this file since 274 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 5.8 KB
Line 
1define([
2        "dojo/_base/array", // array.forEach array.map
3        "dojo/_base/config", // defaultDuration
4        "dojo/_base/declare", // declare
5        "dojo/dom",                     // dom.byId
6        "dojo/dom-attr", // domAttr.attr domAttr.has
7        "dojo/dom-style", // style.style
8        "dojo/_base/sniff", // has("ie")
9        "./_base/manager",      // manager._isElementShown
10        "."     // for exporting methods to dijit namespace
11], function(array, config, declare, dom, domAttr, domStyle, has, manager, dijit){
12
13        // module:
14        //              dijit/a11y
15        // summary:
16        //              Accessibility utility functions (keyboard, tab stops, etc.)
17
18        var shown = (dijit._isElementShown = function(/*Element*/ elem){
19                var s = domStyle.get(elem);
20                return (s.visibility != "hidden")
21                        && (s.visibility != "collapsed")
22                        && (s.display != "none")
23                        && (domAttr.get(elem, "type") != "hidden");
24        });
25
26        dijit.hasDefaultTabStop = function(/*Element*/ elem){
27                // summary:
28                //              Tests if element is tab-navigable even without an explicit tabIndex setting
29
30                // No explicit tabIndex setting, need to investigate node type
31                switch(elem.nodeName.toLowerCase()){
32                        case "a":
33                                // An <a> w/out a tabindex is only navigable if it has an href
34                                return domAttr.has(elem, "href");
35                        case "area":
36                        case "button":
37                        case "input":
38                        case "object":
39                        case "select":
40                        case "textarea":
41                                // These are navigable by default
42                                return true;
43                        case "iframe":
44                                // If it's an editor <iframe> then it's tab navigable.
45                                var body;
46                                try{
47                                        // non-IE
48                                        var contentDocument = elem.contentDocument;
49                                        if("designMode" in contentDocument && contentDocument.designMode == "on"){
50                                                return true;
51                                        }
52                                        body = contentDocument.body;
53                                }catch(e1){
54                                        // contentWindow.document isn't accessible within IE7/8
55                                        // if the iframe.src points to a foreign url and this
56                                        // page contains an element, that could get focus
57                                        try{
58                                                body = elem.contentWindow.document.body;
59                                        }catch(e2){
60                                                return false;
61                                        }
62                                }
63                                return body && (body.contentEditable == 'true' ||
64                                        (body.firstChild && body.firstChild.contentEditable == 'true'));
65                        default:
66                                return elem.contentEditable == 'true';
67                }
68        };
69
70        var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){
71                // summary:
72                //              Tests if an element is tab-navigable
73
74                // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
75                if(domAttr.get(elem, "disabled")){
76                        return false;
77                }else if(domAttr.has(elem, "tabIndex")){
78                        // Explicit tab index setting
79                        return domAttr.get(elem, "tabIndex") >= 0; // boolean
80                }else{
81                        // No explicit tabIndex setting, so depends on node type
82                        return dijit.hasDefaultTabStop(elem);
83                }
84        });
85
86        dijit._getTabNavigable = function(/*DOMNode*/ root){
87                // summary:
88                //              Finds descendants of the specified root node.
89                //
90                // description:
91                //              Finds the following descendants of the specified root node:
92                //              * the first tab-navigable element in document order
93                //                without a tabIndex or with tabIndex="0"
94                //              * the last tab-navigable element in document order
95                //                without a tabIndex or with tabIndex="0"
96                //              * the first element in document order with the lowest
97                //                positive tabIndex value
98                //              * the last element in document order with the highest
99                //                positive tabIndex value
100                var first, last, lowest, lowestTabindex, highest, highestTabindex, radioSelected = {};
101
102                function radioName(node){
103                        // If this element is part of a radio button group, return the name for that group.
104                        return node && node.tagName.toLowerCase() == "input" &&
105                                node.type && node.type.toLowerCase() == "radio" &&
106                                node.name && node.name.toLowerCase();
107                }
108
109                var walkTree = function(/*DOMNode*/parent){
110                        for(var child = parent.firstChild; child; child = child.nextSibling){
111                                // Skip text elements, hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
112                                // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
113                                if(child.nodeType != 1 || (has("ie") && child.scopeName !== "HTML") || !shown(child)){
114                                        continue;
115                                }
116
117                                if(isTabNavigable(child)){
118                                        var tabindex = domAttr.get(child, "tabIndex");
119                                        if(!domAttr.has(child, "tabIndex") || tabindex == 0){
120                                                if(!first){
121                                                        first = child;
122                                                }
123                                                last = child;
124                                        }else if(tabindex > 0){
125                                                if(!lowest || tabindex < lowestTabindex){
126                                                        lowestTabindex = tabindex;
127                                                        lowest = child;
128                                                }
129                                                if(!highest || tabindex >= highestTabindex){
130                                                        highestTabindex = tabindex;
131                                                        highest = child;
132                                                }
133                                        }
134                                        var rn = radioName(child);
135                                        if(domAttr.get(child, "checked") && rn){
136                                                radioSelected[rn] = child;
137                                        }
138                                }
139                                if(child.nodeName.toUpperCase() != 'SELECT'){
140                                        walkTree(child);
141                                }
142                        }
143                };
144                if(shown(root)){
145                        walkTree(root);
146                }
147                function rs(node){
148                        // substitute checked radio button for unchecked one, if there is a checked one with the same name.
149                        return radioSelected[radioName(node)] || node;
150                }
151
152                return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) };
153        };
154        dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){
155                // summary:
156                //              Finds the descendant of the specified root node
157                //              that is first in the tabbing order
158                var elems = dijit._getTabNavigable(dom.byId(root));
159                return elems.lowest ? elems.lowest : elems.first; // DomNode
160        };
161
162        dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){
163                // summary:
164                //              Finds the descendant of the specified root node
165                //              that is last in the tabbing order
166                var elems = dijit._getTabNavigable(dom.byId(root));
167                return elems.last ? elems.last : elems.highest; // DomNode
168        };
169
170        return {
171                hasDefaultTabStop: dijit.hasDefaultTabStop,
172                isTabNavigable: dijit.isTabNavigable,
173                _getTabNavigable: dijit._getTabNavigable,
174                getFirstInTabbingOrder: dijit.getFirstInTabbingOrder,
175                getLastInTabbingOrder: dijit.getLastInTabbingOrder
176        };
177});
Note: See TracBrowser for help on using the repository browser.