494 lines
14 KiB
JavaScript
494 lines
14 KiB
JavaScript
/*
|
|
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
|
|
Available via Academic Free License >= 2.1 OR the modified BSD license.
|
|
see: http://dojotoolkit.org/license for details
|
|
*/
|
|
|
|
|
|
if(!dojo._hasResource["dijit._base.manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
|
dojo._hasResource["dijit._base.manager"] = true;
|
|
dojo.provide("dijit._base.manager");
|
|
|
|
|
|
dojo.declare("dijit.WidgetSet", null, {
|
|
// summary:
|
|
// A set of widgets indexed by id. A default instance of this class is
|
|
// available as `dijit.registry`
|
|
//
|
|
// example:
|
|
// Create a small list of widgets:
|
|
// | var ws = new dijit.WidgetSet();
|
|
// | ws.add(dijit.byId("one"));
|
|
// | ws.add(dijit.byId("two"));
|
|
// | // destroy both:
|
|
// | ws.forEach(function(w){ w.destroy(); });
|
|
//
|
|
// example:
|
|
// Using dijit.registry:
|
|
// | dijit.registry.forEach(function(w){ /* do something */ });
|
|
|
|
constructor: function(){
|
|
this._hash = {};
|
|
this.length = 0;
|
|
},
|
|
|
|
add: function(/*dijit._Widget*/ widget){
|
|
// summary:
|
|
// Add a widget to this list. If a duplicate ID is detected, a error is thrown.
|
|
//
|
|
// widget: dijit._Widget
|
|
// Any dijit._Widget subclass.
|
|
if(this._hash[widget.id]){
|
|
throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
|
|
}
|
|
this._hash[widget.id] = widget;
|
|
this.length++;
|
|
},
|
|
|
|
remove: function(/*String*/ id){
|
|
// summary:
|
|
// Remove a widget from this WidgetSet. Does not destroy the widget; simply
|
|
// removes the reference.
|
|
if(this._hash[id]){
|
|
delete this._hash[id];
|
|
this.length--;
|
|
}
|
|
},
|
|
|
|
forEach: function(/*Function*/ func, /* Object? */thisObj){
|
|
// summary:
|
|
// Call specified function for each widget in this set.
|
|
//
|
|
// func:
|
|
// A callback function to run for each item. Is passed the widget, the index
|
|
// in the iteration, and the full hash, similar to `dojo.forEach`.
|
|
//
|
|
// thisObj:
|
|
// An optional scope parameter
|
|
//
|
|
// example:
|
|
// Using the default `dijit.registry` instance:
|
|
// | dijit.registry.forEach(function(widget){
|
|
// | console.log(widget.declaredClass);
|
|
// | });
|
|
//
|
|
// returns:
|
|
// Returns self, in order to allow for further chaining.
|
|
|
|
thisObj = thisObj || dojo.global;
|
|
var i = 0, id;
|
|
for(id in this._hash){
|
|
func.call(thisObj, this._hash[id], i++, this._hash);
|
|
}
|
|
return this; // dijit.WidgetSet
|
|
},
|
|
|
|
filter: function(/*Function*/ filter, /* Object? */thisObj){
|
|
// summary:
|
|
// Filter down this WidgetSet to a smaller new WidgetSet
|
|
// Works the same as `dojo.filter` and `dojo.NodeList.filter`
|
|
//
|
|
// filter:
|
|
// Callback function to test truthiness. Is passed the widget
|
|
// reference and the pseudo-index in the object.
|
|
//
|
|
// thisObj: Object?
|
|
// Option scope to use for the filter function.
|
|
//
|
|
// example:
|
|
// Arbitrary: select the odd widgets in this list
|
|
// | dijit.registry.filter(function(w, i){
|
|
// | return i % 2 == 0;
|
|
// | }).forEach(function(w){ /* odd ones */ });
|
|
|
|
thisObj = thisObj || dojo.global;
|
|
var res = new dijit.WidgetSet(), i = 0, id;
|
|
for(id in this._hash){
|
|
var w = this._hash[id];
|
|
if(filter.call(thisObj, w, i++, this._hash)){
|
|
res.add(w);
|
|
}
|
|
}
|
|
return res; // dijit.WidgetSet
|
|
},
|
|
|
|
byId: function(/*String*/ id){
|
|
// summary:
|
|
// Find a widget in this list by it's id.
|
|
// example:
|
|
// Test if an id is in a particular WidgetSet
|
|
// | var ws = new dijit.WidgetSet();
|
|
// | ws.add(dijit.byId("bar"));
|
|
// | var t = ws.byId("bar") // returns a widget
|
|
// | var x = ws.byId("foo"); // returns undefined
|
|
|
|
return this._hash[id]; // dijit._Widget
|
|
},
|
|
|
|
byClass: function(/*String*/ cls){
|
|
// summary:
|
|
// Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
|
|
//
|
|
// cls: String
|
|
// The Class to scan for. Full dot-notated string.
|
|
//
|
|
// example:
|
|
// Find all `dijit.TitlePane`s in a page:
|
|
// | dijit.registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
|
|
|
|
var res = new dijit.WidgetSet(), id, widget;
|
|
for(id in this._hash){
|
|
widget = this._hash[id];
|
|
if(widget.declaredClass == cls){
|
|
res.add(widget);
|
|
}
|
|
}
|
|
return res; // dijit.WidgetSet
|
|
},
|
|
|
|
toArray: function(){
|
|
// summary:
|
|
// Convert this WidgetSet into a true Array
|
|
//
|
|
// example:
|
|
// Work with the widget .domNodes in a real Array
|
|
// | dojo.map(dijit.registry.toArray(), function(w){ return w.domNode; });
|
|
|
|
var ar = [];
|
|
for(var id in this._hash){
|
|
ar.push(this._hash[id]);
|
|
}
|
|
return ar; // dijit._Widget[]
|
|
},
|
|
|
|
map: function(/* Function */func, /* Object? */thisObj){
|
|
// summary:
|
|
// Create a new Array from this WidgetSet, following the same rules as `dojo.map`
|
|
// example:
|
|
// | var nodes = dijit.registry.map(function(w){ return w.domNode; });
|
|
//
|
|
// returns:
|
|
// A new array of the returned values.
|
|
return dojo.map(this.toArray(), func, thisObj); // Array
|
|
},
|
|
|
|
every: function(func, thisObj){
|
|
// summary:
|
|
// A synthetic clone of `dojo.every` acting explicitly on this WidgetSet
|
|
//
|
|
// func: Function
|
|
// A callback function run for every widget in this list. Exits loop
|
|
// when the first false return is encountered.
|
|
//
|
|
// thisObj: Object?
|
|
// Optional scope parameter to use for the callback
|
|
|
|
thisObj = thisObj || dojo.global;
|
|
var x = 0, i;
|
|
for(i in this._hash){
|
|
if(!func.call(thisObj, this._hash[i], x++, this._hash)){
|
|
return false; // Boolean
|
|
}
|
|
}
|
|
return true; // Boolean
|
|
},
|
|
|
|
some: function(func, thisObj){
|
|
// summary:
|
|
// A synthetic clone of `dojo.some` acting explictly on this WidgetSet
|
|
//
|
|
// func: Function
|
|
// A callback function run for every widget in this list. Exits loop
|
|
// when the first true return is encountered.
|
|
//
|
|
// thisObj: Object?
|
|
// Optional scope parameter to use for the callback
|
|
|
|
thisObj = thisObj || dojo.global;
|
|
var x = 0, i;
|
|
for(i in this._hash){
|
|
if(func.call(thisObj, this._hash[i], x++, this._hash)){
|
|
return true; // Boolean
|
|
}
|
|
}
|
|
return false; // Boolean
|
|
}
|
|
|
|
});
|
|
|
|
(function(){
|
|
|
|
/*=====
|
|
dijit.registry = {
|
|
// summary:
|
|
// A list of widgets on a page.
|
|
// description:
|
|
// Is an instance of `dijit.WidgetSet`
|
|
};
|
|
=====*/
|
|
dijit.registry = new dijit.WidgetSet();
|
|
|
|
var hash = dijit.registry._hash,
|
|
attr = dojo.attr,
|
|
hasAttr = dojo.hasAttr,
|
|
style = dojo.style;
|
|
|
|
dijit.byId = function(/*String|dijit._Widget*/ id){
|
|
// summary:
|
|
// Returns a widget by it's id, or if passed a widget, no-op (like dojo.byId())
|
|
return typeof id == "string" ? hash[id] : id; // dijit._Widget
|
|
};
|
|
|
|
var _widgetTypeCtr = {};
|
|
dijit.getUniqueId = function(/*String*/widgetType){
|
|
// summary:
|
|
// Generates a unique id for a given widgetType
|
|
|
|
var id;
|
|
do{
|
|
id = widgetType + "_" +
|
|
(widgetType in _widgetTypeCtr ?
|
|
++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0);
|
|
}while(hash[id]);
|
|
return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String
|
|
};
|
|
|
|
dijit.findWidgets = function(/*DomNode*/ root){
|
|
// summary:
|
|
// Search subtree under root returning widgets found.
|
|
// Doesn't search for nested widgets (ie, widgets inside other widgets).
|
|
|
|
var outAry = [];
|
|
|
|
function getChildrenHelper(root){
|
|
for(var node = root.firstChild; node; node = node.nextSibling){
|
|
if(node.nodeType == 1){
|
|
var widgetId = node.getAttribute("widgetId");
|
|
if(widgetId){
|
|
var widget = hash[widgetId];
|
|
if(widget){ // may be null on page w/multiple dojo's loaded
|
|
outAry.push(widget);
|
|
}
|
|
}else{
|
|
getChildrenHelper(node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
getChildrenHelper(root);
|
|
return outAry;
|
|
};
|
|
|
|
dijit._destroyAll = function(){
|
|
// summary:
|
|
// Code to destroy all widgets and do other cleanup on page unload
|
|
|
|
// Clean up focus manager lingering references to widgets and nodes
|
|
dijit._curFocus = null;
|
|
dijit._prevFocus = null;
|
|
dijit._activeStack = [];
|
|
|
|
// Destroy all the widgets, top down
|
|
dojo.forEach(dijit.findWidgets(dojo.body()), function(widget){
|
|
// Avoid double destroy of widgets like Menu that are attached to <body>
|
|
// even though they are logically children of other widgets.
|
|
if(!widget._destroyed){
|
|
if(widget.destroyRecursive){
|
|
widget.destroyRecursive();
|
|
}else if(widget.destroy){
|
|
widget.destroy();
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
if(dojo.isIE){
|
|
// Only run _destroyAll() for IE because we think it's only necessary in that case,
|
|
// and because it causes problems on FF. See bug #3531 for details.
|
|
dojo.addOnWindowUnload(function(){
|
|
dijit._destroyAll();
|
|
});
|
|
}
|
|
|
|
dijit.byNode = function(/*DOMNode*/ node){
|
|
// summary:
|
|
// Returns the widget corresponding to the given DOMNode
|
|
return hash[node.getAttribute("widgetId")]; // dijit._Widget
|
|
};
|
|
|
|
dijit.getEnclosingWidget = function(/*DOMNode*/ node){
|
|
// summary:
|
|
// Returns the widget whose DOM tree contains the specified DOMNode, or null if
|
|
// the node is not contained within the DOM tree of any widget
|
|
while(node){
|
|
var id = node.getAttribute && node.getAttribute("widgetId");
|
|
if(id){
|
|
return hash[id];
|
|
}
|
|
node = node.parentNode;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
var shown = (dijit._isElementShown = function(/*Element*/ elem){
|
|
var s = style(elem);
|
|
return (s.visibility != "hidden")
|
|
&& (s.visibility != "collapsed")
|
|
&& (s.display != "none")
|
|
&& (attr(elem, "type") != "hidden");
|
|
});
|
|
|
|
dijit.hasDefaultTabStop = function(/*Element*/ elem){
|
|
// summary:
|
|
// Tests if element is tab-navigable even without an explicit tabIndex setting
|
|
|
|
// No explicit tabIndex setting, need to investigate node type
|
|
switch(elem.nodeName.toLowerCase()){
|
|
case "a":
|
|
// An <a> w/out a tabindex is only navigable if it has an href
|
|
return hasAttr(elem, "href");
|
|
case "area":
|
|
case "button":
|
|
case "input":
|
|
case "object":
|
|
case "select":
|
|
case "textarea":
|
|
// These are navigable by default
|
|
return true;
|
|
case "iframe":
|
|
// If it's an editor <iframe> then it's tab navigable.
|
|
var body;
|
|
try{
|
|
// non-IE
|
|
var contentDocument = elem.contentDocument;
|
|
if("designMode" in contentDocument && contentDocument.designMode == "on"){
|
|
return true;
|
|
}
|
|
body = contentDocument.body;
|
|
}catch(e1){
|
|
// contentWindow.document isn't accessible within IE7/8
|
|
// if the iframe.src points to a foreign url and this
|
|
// page contains an element, that could get focus
|
|
try{
|
|
body = elem.contentWindow.document.body;
|
|
}catch(e2){
|
|
return false;
|
|
}
|
|
}
|
|
return body.contentEditable == 'true' || (body.firstChild && body.firstChild.contentEditable == 'true');
|
|
default:
|
|
return elem.contentEditable == 'true';
|
|
}
|
|
};
|
|
|
|
var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){
|
|
// summary:
|
|
// Tests if an element is tab-navigable
|
|
|
|
// TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
|
|
if(attr(elem, "disabled")){
|
|
return false;
|
|
}else if(hasAttr(elem, "tabIndex")){
|
|
// Explicit tab index setting
|
|
return attr(elem, "tabIndex") >= 0; // boolean
|
|
}else{
|
|
// No explicit tabIndex setting, so depends on node type
|
|
return dijit.hasDefaultTabStop(elem);
|
|
}
|
|
});
|
|
|
|
dijit._getTabNavigable = function(/*DOMNode*/ root){
|
|
// summary:
|
|
// Finds descendants of the specified root node.
|
|
//
|
|
// description:
|
|
// Finds the following descendants of the specified root node:
|
|
// * the first tab-navigable element in document order
|
|
// without a tabIndex or with tabIndex="0"
|
|
// * the last tab-navigable element in document order
|
|
// without a tabIndex or with tabIndex="0"
|
|
// * the first element in document order with the lowest
|
|
// positive tabIndex value
|
|
// * the last element in document order with the highest
|
|
// positive tabIndex value
|
|
var first, last, lowest, lowestTabindex, highest, highestTabindex, radioSelected = {};
|
|
function radioName(node) {
|
|
// If this element is part of a radio button group, return the name for that group.
|
|
return node && node.tagName.toLowerCase() == "input" &&
|
|
node.type && node.type.toLowerCase() == "radio" &&
|
|
node.name && node.name.toLowerCase();
|
|
}
|
|
var walkTree = function(/*DOMNode*/parent){
|
|
dojo.query("> *", parent).forEach(function(child){
|
|
// Skip hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
|
|
// since show() invokes getAttribute("type"), which crash on VML nodes in IE.
|
|
if((dojo.isIE && child.scopeName!=="HTML") || !shown(child)){
|
|
return;
|
|
}
|
|
|
|
if(isTabNavigable(child)){
|
|
var tabindex = attr(child, "tabIndex");
|
|
if(!hasAttr(child, "tabIndex") || tabindex == 0){
|
|
if(!first){ first = child; }
|
|
last = child;
|
|
}else if(tabindex > 0){
|
|
if(!lowest || tabindex < lowestTabindex){
|
|
lowestTabindex = tabindex;
|
|
lowest = child;
|
|
}
|
|
if(!highest || tabindex >= highestTabindex){
|
|
highestTabindex = tabindex;
|
|
highest = child;
|
|
}
|
|
}
|
|
var rn = radioName(child);
|
|
if(dojo.attr(child, "checked") && rn) {
|
|
radioSelected[rn] = child;
|
|
}
|
|
}
|
|
if(child.nodeName.toUpperCase() != 'SELECT'){
|
|
walkTree(child);
|
|
}
|
|
});
|
|
};
|
|
if(shown(root)){ walkTree(root) }
|
|
function rs(node) {
|
|
// substitute checked radio button for unchecked one, if there is a checked one with the same name.
|
|
return radioSelected[radioName(node)] || node;
|
|
}
|
|
return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) };
|
|
}
|
|
dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){
|
|
// summary:
|
|
// Finds the descendant of the specified root node
|
|
// that is first in the tabbing order
|
|
var elems = dijit._getTabNavigable(dojo.byId(root));
|
|
return elems.lowest ? elems.lowest : elems.first; // DomNode
|
|
};
|
|
|
|
dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){
|
|
// summary:
|
|
// Finds the descendant of the specified root node
|
|
// that is last in the tabbing order
|
|
var elems = dijit._getTabNavigable(dojo.byId(root));
|
|
return elems.last ? elems.last : elems.highest; // DomNode
|
|
};
|
|
|
|
/*=====
|
|
dojo.mixin(dijit, {
|
|
// defaultDuration: Integer
|
|
// The default animation speed (in ms) to use for all Dijit
|
|
// transitional animations, unless otherwise specified
|
|
// on a per-instance basis. Defaults to 200, overrided by
|
|
// `djConfig.defaultDuration`
|
|
defaultDuration: 200
|
|
});
|
|
=====*/
|
|
|
|
dijit.defaultDuration = dojo.config["defaultDuration"] || 200;
|
|
|
|
})();
|
|
|
|
}
|