manager.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dijit._base.manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dijit._base.manager"] = true;
  8. dojo.provide("dijit._base.manager");
  9. dojo.declare("dijit.WidgetSet", null, {
  10. // summary:
  11. // A set of widgets indexed by id. A default instance of this class is
  12. // available as `dijit.registry`
  13. //
  14. // example:
  15. // Create a small list of widgets:
  16. // | var ws = new dijit.WidgetSet();
  17. // | ws.add(dijit.byId("one"));
  18. // | ws.add(dijit.byId("two"));
  19. // | // destroy both:
  20. // | ws.forEach(function(w){ w.destroy(); });
  21. //
  22. // example:
  23. // Using dijit.registry:
  24. // | dijit.registry.forEach(function(w){ /* do something */ });
  25. constructor: function(){
  26. this._hash = {};
  27. this.length = 0;
  28. },
  29. add: function(/*dijit._Widget*/ widget){
  30. // summary:
  31. // Add a widget to this list. If a duplicate ID is detected, a error is thrown.
  32. //
  33. // widget: dijit._Widget
  34. // Any dijit._Widget subclass.
  35. if(this._hash[widget.id]){
  36. throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
  37. }
  38. this._hash[widget.id] = widget;
  39. this.length++;
  40. },
  41. remove: function(/*String*/ id){
  42. // summary:
  43. // Remove a widget from this WidgetSet. Does not destroy the widget; simply
  44. // removes the reference.
  45. if(this._hash[id]){
  46. delete this._hash[id];
  47. this.length--;
  48. }
  49. },
  50. forEach: function(/*Function*/ func, /* Object? */thisObj){
  51. // summary:
  52. // Call specified function for each widget in this set.
  53. //
  54. // func:
  55. // A callback function to run for each item. Is passed the widget, the index
  56. // in the iteration, and the full hash, similar to `dojo.forEach`.
  57. //
  58. // thisObj:
  59. // An optional scope parameter
  60. //
  61. // example:
  62. // Using the default `dijit.registry` instance:
  63. // | dijit.registry.forEach(function(widget){
  64. // | console.log(widget.declaredClass);
  65. // | });
  66. //
  67. // returns:
  68. // Returns self, in order to allow for further chaining.
  69. thisObj = thisObj || dojo.global;
  70. var i = 0, id;
  71. for(id in this._hash){
  72. func.call(thisObj, this._hash[id], i++, this._hash);
  73. }
  74. return this; // dijit.WidgetSet
  75. },
  76. filter: function(/*Function*/ filter, /* Object? */thisObj){
  77. // summary:
  78. // Filter down this WidgetSet to a smaller new WidgetSet
  79. // Works the same as `dojo.filter` and `dojo.NodeList.filter`
  80. //
  81. // filter:
  82. // Callback function to test truthiness. Is passed the widget
  83. // reference and the pseudo-index in the object.
  84. //
  85. // thisObj: Object?
  86. // Option scope to use for the filter function.
  87. //
  88. // example:
  89. // Arbitrary: select the odd widgets in this list
  90. // | dijit.registry.filter(function(w, i){
  91. // | return i % 2 == 0;
  92. // | }).forEach(function(w){ /* odd ones */ });
  93. thisObj = thisObj || dojo.global;
  94. var res = new dijit.WidgetSet(), i = 0, id;
  95. for(id in this._hash){
  96. var w = this._hash[id];
  97. if(filter.call(thisObj, w, i++, this._hash)){
  98. res.add(w);
  99. }
  100. }
  101. return res; // dijit.WidgetSet
  102. },
  103. byId: function(/*String*/ id){
  104. // summary:
  105. // Find a widget in this list by it's id.
  106. // example:
  107. // Test if an id is in a particular WidgetSet
  108. // | var ws = new dijit.WidgetSet();
  109. // | ws.add(dijit.byId("bar"));
  110. // | var t = ws.byId("bar") // returns a widget
  111. // | var x = ws.byId("foo"); // returns undefined
  112. return this._hash[id]; // dijit._Widget
  113. },
  114. byClass: function(/*String*/ cls){
  115. // summary:
  116. // Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
  117. //
  118. // cls: String
  119. // The Class to scan for. Full dot-notated string.
  120. //
  121. // example:
  122. // Find all `dijit.TitlePane`s in a page:
  123. // | dijit.registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
  124. var res = new dijit.WidgetSet(), id, widget;
  125. for(id in this._hash){
  126. widget = this._hash[id];
  127. if(widget.declaredClass == cls){
  128. res.add(widget);
  129. }
  130. }
  131. return res; // dijit.WidgetSet
  132. },
  133. toArray: function(){
  134. // summary:
  135. // Convert this WidgetSet into a true Array
  136. //
  137. // example:
  138. // Work with the widget .domNodes in a real Array
  139. // | dojo.map(dijit.registry.toArray(), function(w){ return w.domNode; });
  140. var ar = [];
  141. for(var id in this._hash){
  142. ar.push(this._hash[id]);
  143. }
  144. return ar; // dijit._Widget[]
  145. },
  146. map: function(/* Function */func, /* Object? */thisObj){
  147. // summary:
  148. // Create a new Array from this WidgetSet, following the same rules as `dojo.map`
  149. // example:
  150. // | var nodes = dijit.registry.map(function(w){ return w.domNode; });
  151. //
  152. // returns:
  153. // A new array of the returned values.
  154. return dojo.map(this.toArray(), func, thisObj); // Array
  155. },
  156. every: function(func, thisObj){
  157. // summary:
  158. // A synthetic clone of `dojo.every` acting explicitly on this WidgetSet
  159. //
  160. // func: Function
  161. // A callback function run for every widget in this list. Exits loop
  162. // when the first false return is encountered.
  163. //
  164. // thisObj: Object?
  165. // Optional scope parameter to use for the callback
  166. thisObj = thisObj || dojo.global;
  167. var x = 0, i;
  168. for(i in this._hash){
  169. if(!func.call(thisObj, this._hash[i], x++, this._hash)){
  170. return false; // Boolean
  171. }
  172. }
  173. return true; // Boolean
  174. },
  175. some: function(func, thisObj){
  176. // summary:
  177. // A synthetic clone of `dojo.some` acting explictly on this WidgetSet
  178. //
  179. // func: Function
  180. // A callback function run for every widget in this list. Exits loop
  181. // when the first true return is encountered.
  182. //
  183. // thisObj: Object?
  184. // Optional scope parameter to use for the callback
  185. thisObj = thisObj || dojo.global;
  186. var x = 0, i;
  187. for(i in this._hash){
  188. if(func.call(thisObj, this._hash[i], x++, this._hash)){
  189. return true; // Boolean
  190. }
  191. }
  192. return false; // Boolean
  193. }
  194. });
  195. (function(){
  196. /*=====
  197. dijit.registry = {
  198. // summary:
  199. // A list of widgets on a page.
  200. // description:
  201. // Is an instance of `dijit.WidgetSet`
  202. };
  203. =====*/
  204. dijit.registry = new dijit.WidgetSet();
  205. var hash = dijit.registry._hash,
  206. attr = dojo.attr,
  207. hasAttr = dojo.hasAttr,
  208. style = dojo.style;
  209. dijit.byId = function(/*String|dijit._Widget*/ id){
  210. // summary:
  211. // Returns a widget by it's id, or if passed a widget, no-op (like dojo.byId())
  212. return typeof id == "string" ? hash[id] : id; // dijit._Widget
  213. };
  214. var _widgetTypeCtr = {};
  215. dijit.getUniqueId = function(/*String*/widgetType){
  216. // summary:
  217. // Generates a unique id for a given widgetType
  218. var id;
  219. do{
  220. id = widgetType + "_" +
  221. (widgetType in _widgetTypeCtr ?
  222. ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0);
  223. }while(hash[id]);
  224. return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String
  225. };
  226. dijit.findWidgets = function(/*DomNode*/ root){
  227. // summary:
  228. // Search subtree under root returning widgets found.
  229. // Doesn't search for nested widgets (ie, widgets inside other widgets).
  230. var outAry = [];
  231. function getChildrenHelper(root){
  232. for(var node = root.firstChild; node; node = node.nextSibling){
  233. if(node.nodeType == 1){
  234. var widgetId = node.getAttribute("widgetId");
  235. if(widgetId){
  236. var widget = hash[widgetId];
  237. if(widget){ // may be null on page w/multiple dojo's loaded
  238. outAry.push(widget);
  239. }
  240. }else{
  241. getChildrenHelper(node);
  242. }
  243. }
  244. }
  245. }
  246. getChildrenHelper(root);
  247. return outAry;
  248. };
  249. dijit._destroyAll = function(){
  250. // summary:
  251. // Code to destroy all widgets and do other cleanup on page unload
  252. // Clean up focus manager lingering references to widgets and nodes
  253. dijit._curFocus = null;
  254. dijit._prevFocus = null;
  255. dijit._activeStack = [];
  256. // Destroy all the widgets, top down
  257. dojo.forEach(dijit.findWidgets(dojo.body()), function(widget){
  258. // Avoid double destroy of widgets like Menu that are attached to <body>
  259. // even though they are logically children of other widgets.
  260. if(!widget._destroyed){
  261. if(widget.destroyRecursive){
  262. widget.destroyRecursive();
  263. }else if(widget.destroy){
  264. widget.destroy();
  265. }
  266. }
  267. });
  268. };
  269. if(dojo.isIE){
  270. // Only run _destroyAll() for IE because we think it's only necessary in that case,
  271. // and because it causes problems on FF. See bug #3531 for details.
  272. dojo.addOnWindowUnload(function(){
  273. dijit._destroyAll();
  274. });
  275. }
  276. dijit.byNode = function(/*DOMNode*/ node){
  277. // summary:
  278. // Returns the widget corresponding to the given DOMNode
  279. return hash[node.getAttribute("widgetId")]; // dijit._Widget
  280. };
  281. dijit.getEnclosingWidget = function(/*DOMNode*/ node){
  282. // summary:
  283. // Returns the widget whose DOM tree contains the specified DOMNode, or null if
  284. // the node is not contained within the DOM tree of any widget
  285. while(node){
  286. var id = node.getAttribute && node.getAttribute("widgetId");
  287. if(id){
  288. return hash[id];
  289. }
  290. node = node.parentNode;
  291. }
  292. return null;
  293. };
  294. var shown = (dijit._isElementShown = function(/*Element*/ elem){
  295. var s = style(elem);
  296. return (s.visibility != "hidden")
  297. && (s.visibility != "collapsed")
  298. && (s.display != "none")
  299. && (attr(elem, "type") != "hidden");
  300. });
  301. dijit.hasDefaultTabStop = function(/*Element*/ elem){
  302. // summary:
  303. // Tests if element is tab-navigable even without an explicit tabIndex setting
  304. // No explicit tabIndex setting, need to investigate node type
  305. switch(elem.nodeName.toLowerCase()){
  306. case "a":
  307. // An <a> w/out a tabindex is only navigable if it has an href
  308. return hasAttr(elem, "href");
  309. case "area":
  310. case "button":
  311. case "input":
  312. case "object":
  313. case "select":
  314. case "textarea":
  315. // These are navigable by default
  316. return true;
  317. case "iframe":
  318. // If it's an editor <iframe> then it's tab navigable.
  319. var body;
  320. try{
  321. // non-IE
  322. var contentDocument = elem.contentDocument;
  323. if("designMode" in contentDocument && contentDocument.designMode == "on"){
  324. return true;
  325. }
  326. body = contentDocument.body;
  327. }catch(e1){
  328. // contentWindow.document isn't accessible within IE7/8
  329. // if the iframe.src points to a foreign url and this
  330. // page contains an element, that could get focus
  331. try{
  332. body = elem.contentWindow.document.body;
  333. }catch(e2){
  334. return false;
  335. }
  336. }
  337. return body.contentEditable == 'true' || (body.firstChild && body.firstChild.contentEditable == 'true');
  338. default:
  339. return elem.contentEditable == 'true';
  340. }
  341. };
  342. var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){
  343. // summary:
  344. // Tests if an element is tab-navigable
  345. // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
  346. if(attr(elem, "disabled")){
  347. return false;
  348. }else if(hasAttr(elem, "tabIndex")){
  349. // Explicit tab index setting
  350. return attr(elem, "tabIndex") >= 0; // boolean
  351. }else{
  352. // No explicit tabIndex setting, so depends on node type
  353. return dijit.hasDefaultTabStop(elem);
  354. }
  355. });
  356. dijit._getTabNavigable = function(/*DOMNode*/ root){
  357. // summary:
  358. // Finds descendants of the specified root node.
  359. //
  360. // description:
  361. // Finds the following descendants of the specified root node:
  362. // * the first tab-navigable element in document order
  363. // without a tabIndex or with tabIndex="0"
  364. // * the last tab-navigable element in document order
  365. // without a tabIndex or with tabIndex="0"
  366. // * the first element in document order with the lowest
  367. // positive tabIndex value
  368. // * the last element in document order with the highest
  369. // positive tabIndex value
  370. var first, last, lowest, lowestTabindex, highest, highestTabindex, radioSelected = {};
  371. function radioName(node) {
  372. // If this element is part of a radio button group, return the name for that group.
  373. return node && node.tagName.toLowerCase() == "input" &&
  374. node.type && node.type.toLowerCase() == "radio" &&
  375. node.name && node.name.toLowerCase();
  376. }
  377. var walkTree = function(/*DOMNode*/parent){
  378. dojo.query("> *", parent).forEach(function(child){
  379. // Skip hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
  380. // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
  381. if((dojo.isIE <= 9 && child.scopeName !== "HTML") || !shown(child)){
  382. return;
  383. }
  384. if(isTabNavigable(child)){
  385. var tabindex = attr(child, "tabIndex");
  386. if(!hasAttr(child, "tabIndex") || tabindex == 0){
  387. if(!first){ first = child; }
  388. last = child;
  389. }else if(tabindex > 0){
  390. if(!lowest || tabindex < lowestTabindex){
  391. lowestTabindex = tabindex;
  392. lowest = child;
  393. }
  394. if(!highest || tabindex >= highestTabindex){
  395. highestTabindex = tabindex;
  396. highest = child;
  397. }
  398. }
  399. var rn = radioName(child);
  400. if(dojo.attr(child, "checked") && rn) {
  401. radioSelected[rn] = child;
  402. }
  403. }
  404. if(child.nodeName.toUpperCase() != 'SELECT'){
  405. walkTree(child);
  406. }
  407. });
  408. };
  409. if(shown(root)){ walkTree(root) }
  410. function rs(node) {
  411. // substitute checked radio button for unchecked one, if there is a checked one with the same name.
  412. return radioSelected[radioName(node)] || node;
  413. }
  414. return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) };
  415. }
  416. dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){
  417. // summary:
  418. // Finds the descendant of the specified root node
  419. // that is first in the tabbing order
  420. var elems = dijit._getTabNavigable(dojo.byId(root));
  421. return elems.lowest ? elems.lowest : elems.first; // DomNode
  422. };
  423. dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){
  424. // summary:
  425. // Finds the descendant of the specified root node
  426. // that is last in the tabbing order
  427. var elems = dijit._getTabNavigable(dojo.byId(root));
  428. return elems.last ? elems.last : elems.highest; // DomNode
  429. };
  430. /*=====
  431. dojo.mixin(dijit, {
  432. // defaultDuration: Integer
  433. // The default animation speed (in ms) to use for all Dijit
  434. // transitional animations, unless otherwise specified
  435. // on a per-instance basis. Defaults to 200, overrided by
  436. // `djConfig.defaultDuration`
  437. defaultDuration: 200
  438. });
  439. =====*/
  440. dijit.defaultDuration = dojo.config["defaultDuration"] || 200;
  441. })();
  442. }