SemanticScuttle/includes/js/dijit/_Container.js

346 lines
9.8 KiB
JavaScript

if(!dojo._hasResource["dijit._Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit._Container"] = true;
dojo.provide("dijit._Container");
dojo.declare("dijit._Contained",
null,
{
// summary
// Mixin for widgets that are children of a container widget
//
// example:
// | // make a basic custom widget that knows about it's parents
// | dojo.declare("my.customClass",[dijit._Widget,dijit._Contained],{});
//
getParent: function(){
// summary:
// Returns the parent widget of this widget, assuming the parent
// implements dijit._Container
for(var p=this.domNode.parentNode; p; p=p.parentNode){
var id = p.getAttribute && p.getAttribute("widgetId");
if(id){
var parent = dijit.byId(id);
return parent.isContainer ? parent : null;
}
}
return null;
},
_getSibling: function(which){
var node = this.domNode;
do{
node = node[which+"Sibling"];
}while(node && node.nodeType != 1);
if(!node){ return null; } // null
var id = node.getAttribute("widgetId");
return dijit.byId(id);
},
getPreviousSibling: function(){
// summary:
// Returns null if this is the first child of the parent,
// otherwise returns the next element sibling to the "left".
return this._getSibling("previous"); // Mixed
},
getNextSibling: function(){
// summary:
// Returns null if this is the last child of the parent,
// otherwise returns the next element sibling to the "right".
return this._getSibling("next"); // Mixed
}
}
);
dojo.declare("dijit._Container",
null,
{
// summary:
// Mixin for widgets that contain a list of children.
// description:
// Use this mixin when the widget needs to know about and
// keep track of it's widget children. Widgets like SplitContainer
// and TabContainer.
isContainer: true,
addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
// summary:
// Process the given child widget, inserting it's dom node as
// a child of our dom node
if(insertIndex === undefined){
insertIndex = "last";
}
var refNode = this.containerNode || this.domNode;
if(insertIndex && typeof insertIndex == "number"){
var children = dojo.query("> [widgetid]", refNode);
if(children && children.length >= insertIndex){
refNode = children[insertIndex-1]; insertIndex = "after";
}
}
dojo.place(widget.domNode, refNode, insertIndex);
// If I've been started but the child widget hasn't been started,
// start it now. Make sure to do this after widget has been
// inserted into the DOM tree, so it can see that it's being controlled by me,
// so it doesn't try to size itself.
if(this._started && !widget._started){
widget.startup();
}
},
removeChild: function(/*Widget*/ widget){
// summary:
// Removes the passed widget instance from this widget but does
// not destroy it
var node = widget.domNode;
node.parentNode.removeChild(node); // detach but don't destroy
},
_nextElement: function(node){
do{
node = node.nextSibling;
}while(node && node.nodeType != 1);
return node;
},
_firstElement: function(node){
node = node.firstChild;
if(node && node.nodeType != 1){
node = this._nextElement(node);
}
return node;
},
getChildren: function(){
// summary:
// Returns array of children widgets
return dojo.query("> [widgetId]", this.containerNode || this.domNode).map(dijit.byNode); // Array
},
hasChildren: function(){
// summary:
// Returns true if widget has children
var cn = this.containerNode || this.domNode;
return !!this._firstElement(cn); // Boolean
},
_getSiblingOfChild: function(/*Widget*/ child, /*int*/ dir){
// summary:
// Get the next or previous widget sibling of child
// dir:
// if 1, get the next sibling
// if -1, get the previous sibling
var node = child.domNode;
var which = (dir>0 ? "nextSibling" : "previousSibling");
do{
node = node[which];
}while(node && (node.nodeType != 1 || !dijit.byNode(node)));
return node ? dijit.byNode(node) : null;
}
}
);
dojo.declare("dijit._KeyNavContainer",
[dijit._Container],
{
// summary: A _Container with keyboard navigation of its children.
// decscription:
// To use this mixin, call connectKeyNavHandlers() in
// postCreate() and call startupKeyNavChildren() in startup().
// It provides normalized keyboard and focusing code for Container
// widgets.
/*=====
// focusedChild: Widget
// The currently focused child widget, or null if there isn't one
focusedChild: null,
=====*/
_keyNavCodes: {},
connectKeyNavHandlers: function(/*Array*/ prevKeyCodes, /*Array*/ nextKeyCodes){
// summary:
// Call in postCreate() to attach the keyboard handlers
// to the container.
// preKeyCodes: Array
// Key codes for navigating to the previous child.
// nextKeyCodes: Array
// Key codes for navigating to the next child.
var keyCodes = this._keyNavCodes = {};
var prev = dojo.hitch(this, this.focusPrev);
var next = dojo.hitch(this, this.focusNext);
dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev });
dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next });
this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
this.connect(this.domNode, "onfocus", "_onContainerFocus");
},
startupKeyNavChildren: function(){
// summary:
// Call in startup() to set child tabindexes to -1
dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild"));
},
addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
// summary: Add a child to our _Container
dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
this._startupChild(widget);
},
focus: function(){
// summary: Default focus() implementation: focus the first child.
this.focusFirstChild();
},
focusFirstChild: function(){
// summary: Focus the first focusable child in the container.
this.focusChild(this._getFirstFocusableChild());
},
focusNext: function(){
// summary: Focus the next widget or focal node (for widgets
// with multiple focal nodes) within this container.
if(this.focusedChild && this.focusedChild.hasNextFocalNode
&& this.focusedChild.hasNextFocalNode()){
this.focusedChild.focusNext();
return;
}
var child = this._getNextFocusableChild(this.focusedChild, 1);
if(child.getFocalNodes){
this.focusChild(child, child.getFocalNodes()[0]);
}else{
this.focusChild(child);
}
},
focusPrev: function(){
// summary: Focus the previous widget or focal node (for widgets
// with multiple focal nodes) within this container.
if(this.focusedChild && this.focusedChild.hasPrevFocalNode
&& this.focusedChild.hasPrevFocalNode()){
this.focusedChild.focusPrev();
return;
}
var child = this._getNextFocusableChild(this.focusedChild, -1);
if(child.getFocalNodes){
var nodes = child.getFocalNodes();
this.focusChild(child, nodes[nodes.length-1]);
}else{
this.focusChild(child);
}
},
focusChild: function(/*Widget*/ widget, /*Node?*/ node){
// summary: Focus widget. Optionally focus 'node' within widget.
if(widget){
if(this.focusedChild && widget !== this.focusedChild){
this._onChildBlur(this.focusedChild);
}
this.focusedChild = widget;
if(node && widget.focusFocalNode){
widget.focusFocalNode(node);
}else{
widget.focus();
}
}
},
_startupChild: function(/*Widget*/ widget){
// summary:
// Set tabindex="-1" on focusable widgets so that we
// can focus them programmatically and by clicking.
// Connect focus and blur handlers.
if(widget.getFocalNodes){
dojo.forEach(widget.getFocalNodes(), function(node){
dojo.attr(node, "tabindex", -1);
this._connectNode(node);
}, this);
}else{
var node = widget.focusNode || widget.domNode;
if(widget.isFocusable()){
dojo.attr(node, "tabindex", -1);
}
this._connectNode(node);
}
},
_connectNode: function(/*Element*/ node){
this.connect(node, "onfocus", "_onNodeFocus");
this.connect(node, "onblur", "_onNodeBlur");
},
_onContainerFocus: function(evt){
// focus bubbles on Firefox,
// so just make sure that focus has really gone to the container
if(evt.target === this.domNode){
this.focusFirstChild();
}
},
_onContainerKeypress: function(evt){
if(evt.ctrlKey || evt.altKey){ return; }
var func = this._keyNavCodes[evt.keyCode];
if(func){
func();
dojo.stopEvent(evt);
}
},
_onNodeFocus: function(evt){
// while focus is on a child,
// take the container out of the tab order so that
// we can shift-tab to the element before the container
dojo.attr(this.domNode, "tabindex", -1);
// record the child that has been focused
var widget = dijit.getEnclosingWidget(evt.target);
if(widget && widget.isFocusable()){
this.focusedChild = widget;
}
dojo.stopEvent(evt);
},
_onNodeBlur: function(evt){
// when focus leaves a child,
// reinstate the container's tabindex
if(this.tabIndex){
dojo.attr(this.domNode, "tabindex", this.tabIndex);
}
dojo.stopEvent(evt);
},
_onChildBlur: function(/*Widget*/ widget){
// summary:
// Called when focus leaves a child widget to go
// to a sibling widget.
},
_getFirstFocusableChild: function(){
return this._getNextFocusableChild(null, 1);
},
_getNextFocusableChild: function(child, dir){
if(child){
child = this._getSiblingOfChild(child, dir);
}
var children = this.getChildren();
for(var i=0; i < children.length; i++){
if(!child){
child = children[(dir>0) ? 0 : (children.length-1)];
}
if(child.isFocusable()){
return child;
}
child = this._getSiblingOfChild(child, dir);
}
// no focusable child found
return null;
}
}
);
}