1 if(!dojo._hasResource["dijit._Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dijit._Container"] = true;
3 dojo.provide("dijit._Container");
5 dojo.declare("dijit._Contained",
9 // Mixin for widgets that are children of a container widget
12 // | // make a basic custom widget that knows about it's parents
13 // | dojo.declare("my.customClass",[dijit._Widget,dijit._Contained],{});
15 getParent: function(){
17 // Returns the parent widget of this widget, assuming the parent
18 // implements dijit._Container
19 for(var p=this.domNode.parentNode; p; p=p.parentNode){
20 var id = p.getAttribute && p.getAttribute("widgetId");
22 var parent = dijit.byId(id);
23 return parent.isContainer ? parent : null;
29 _getSibling: function(which){
30 var node = this.domNode;
32 node = node[which+"Sibling"];
33 }while(node && node.nodeType != 1);
34 if(!node){ return null; } // null
35 var id = node.getAttribute("widgetId");
36 return dijit.byId(id);
39 getPreviousSibling: function(){
41 // Returns null if this is the first child of the parent,
42 // otherwise returns the next element sibling to the "left".
44 return this._getSibling("previous"); // Mixed
47 getNextSibling: function(){
49 // Returns null if this is the last child of the parent,
50 // otherwise returns the next element sibling to the "right".
52 return this._getSibling("next"); // Mixed
57 dojo.declare("dijit._Container",
61 // Mixin for widgets that contain a list of children.
63 // Use this mixin when the widget needs to know about and
64 // keep track of it's widget children. Widgets like SplitContainer
69 addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
71 // Process the given child widget, inserting it's dom node as
72 // a child of our dom node
74 if(insertIndex === undefined){
77 var refNode = this.containerNode || this.domNode;
78 if(insertIndex && typeof insertIndex == "number"){
79 var children = dojo.query("> [widgetid]", refNode);
80 if(children && children.length >= insertIndex){
81 refNode = children[insertIndex-1]; insertIndex = "after";
84 dojo.place(widget.domNode, refNode, insertIndex);
86 // If I've been started but the child widget hasn't been started,
87 // start it now. Make sure to do this after widget has been
88 // inserted into the DOM tree, so it can see that it's being controlled by me,
89 // so it doesn't try to size itself.
90 if(this._started && !widget._started){
95 removeChild: function(/*Widget*/ widget){
97 // Removes the passed widget instance from this widget but does
99 var node = widget.domNode;
100 node.parentNode.removeChild(node); // detach but don't destroy
103 _nextElement: function(node){
105 node = node.nextSibling;
106 }while(node && node.nodeType != 1);
110 _firstElement: function(node){
111 node = node.firstChild;
112 if(node && node.nodeType != 1){
113 node = this._nextElement(node);
118 getChildren: function(){
120 // Returns array of children widgets
121 return dojo.query("> [widgetId]", this.containerNode || this.domNode).map(dijit.byNode); // Array
124 hasChildren: function(){
126 // Returns true if widget has children
127 var cn = this.containerNode || this.domNode;
128 return !!this._firstElement(cn); // Boolean
131 _getSiblingOfChild: function(/*Widget*/ child, /*int*/ dir){
133 // Get the next or previous widget sibling of child
135 // if 1, get the next sibling
136 // if -1, get the previous sibling
137 var node = child.domNode;
138 var which = (dir>0 ? "nextSibling" : "previousSibling");
141 }while(node && (node.nodeType != 1 || !dijit.byNode(node)));
142 return node ? dijit.byNode(node) : null;
147 dojo.declare("dijit._KeyNavContainer",
151 // summary: A _Container with keyboard navigation of its children.
153 // To use this mixin, call connectKeyNavHandlers() in
154 // postCreate() and call startupKeyNavChildren() in startup().
155 // It provides normalized keyboard and focusing code for Container
158 // focusedChild: Widget
159 // The currently focused child widget, or null if there isn't one
165 connectKeyNavHandlers: function(/*Array*/ prevKeyCodes, /*Array*/ nextKeyCodes){
167 // Call in postCreate() to attach the keyboard handlers
169 // preKeyCodes: Array
170 // Key codes for navigating to the previous child.
171 // nextKeyCodes: Array
172 // Key codes for navigating to the next child.
174 var keyCodes = this._keyNavCodes = {};
175 var prev = dojo.hitch(this, this.focusPrev);
176 var next = dojo.hitch(this, this.focusNext);
177 dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev });
178 dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next });
179 this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
180 this.connect(this.domNode, "onfocus", "_onContainerFocus");
183 startupKeyNavChildren: function(){
185 // Call in startup() to set child tabindexes to -1
186 dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild"));
189 addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
190 // summary: Add a child to our _Container
191 dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
192 this._startupChild(widget);
196 // summary: Default focus() implementation: focus the first child.
197 this.focusFirstChild();
200 focusFirstChild: function(){
201 // summary: Focus the first focusable child in the container.
202 this.focusChild(this._getFirstFocusableChild());
205 focusNext: function(){
206 // summary: Focus the next widget or focal node (for widgets
207 // with multiple focal nodes) within this container.
208 if(this.focusedChild && this.focusedChild.hasNextFocalNode
209 && this.focusedChild.hasNextFocalNode()){
210 this.focusedChild.focusNext();
213 var child = this._getNextFocusableChild(this.focusedChild, 1);
214 if(child.getFocalNodes){
215 this.focusChild(child, child.getFocalNodes()[0]);
217 this.focusChild(child);
221 focusPrev: function(){
222 // summary: Focus the previous widget or focal node (for widgets
223 // with multiple focal nodes) within this container.
224 if(this.focusedChild && this.focusedChild.hasPrevFocalNode
225 && this.focusedChild.hasPrevFocalNode()){
226 this.focusedChild.focusPrev();
229 var child = this._getNextFocusableChild(this.focusedChild, -1);
230 if(child.getFocalNodes){
231 var nodes = child.getFocalNodes();
232 this.focusChild(child, nodes[nodes.length-1]);
234 this.focusChild(child);
238 focusChild: function(/*Widget*/ widget, /*Node?*/ node){
239 // summary: Focus widget. Optionally focus 'node' within widget.
241 if(this.focusedChild && widget !== this.focusedChild){
242 this._onChildBlur(this.focusedChild);
244 this.focusedChild = widget;
245 if(node && widget.focusFocalNode){
246 widget.focusFocalNode(node);
253 _startupChild: function(/*Widget*/ widget){
255 // Set tabindex="-1" on focusable widgets so that we
256 // can focus them programmatically and by clicking.
257 // Connect focus and blur handlers.
258 if(widget.getFocalNodes){
259 dojo.forEach(widget.getFocalNodes(), function(node){
260 dojo.attr(node, "tabindex", -1);
261 this._connectNode(node);
264 var node = widget.focusNode || widget.domNode;
265 if(widget.isFocusable()){
266 dojo.attr(node, "tabindex", -1);
268 this._connectNode(node);
272 _connectNode: function(/*Element*/ node){
273 this.connect(node, "onfocus", "_onNodeFocus");
274 this.connect(node, "onblur", "_onNodeBlur");
277 _onContainerFocus: function(evt){
278 // focus bubbles on Firefox,
279 // so just make sure that focus has really gone to the container
280 if(evt.target === this.domNode){
281 this.focusFirstChild();
285 _onContainerKeypress: function(evt){
286 if(evt.ctrlKey || evt.altKey){ return; }
287 var func = this._keyNavCodes[evt.keyCode];
294 _onNodeFocus: function(evt){
295 // while focus is on a child,
296 // take the container out of the tab order so that
297 // we can shift-tab to the element before the container
298 dojo.attr(this.domNode, "tabindex", -1);
299 // record the child that has been focused
300 var widget = dijit.getEnclosingWidget(evt.target);
301 if(widget && widget.isFocusable()){
302 this.focusedChild = widget;
307 _onNodeBlur: function(evt){
308 // when focus leaves a child,
309 // reinstate the container's tabindex
311 dojo.attr(this.domNode, "tabindex", this.tabIndex);
316 _onChildBlur: function(/*Widget*/ widget){
318 // Called when focus leaves a child widget to go
319 // to a sibling widget.
322 _getFirstFocusableChild: function(){
323 return this._getNextFocusableChild(null, 1);
326 _getNextFocusableChild: function(child, dir){
328 child = this._getSiblingOfChild(child, dir);
330 var children = this.getChildren();
331 for(var i=0; i < children.length; i++){
333 child = children[(dir>0) ? 0 : (children.length-1)];
335 if(child.isFocusable()){
338 child = this._getSiblingOfChild(child, dir);
340 // no focusable child found