_Widget.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  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._Widget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dijit._Widget"] = true;
  8. dojo.provide("dijit._Widget");
  9. dojo.require("dijit._WidgetBase");
  10. dojo.require("dijit._base");
  11. ////////////////// DEFERRED CONNECTS ///////////////////
  12. // This code is to assist deferring dojo.connect() calls in widgets (connecting to events on the widgets'
  13. // DOM nodes) until someone actually needs to monitor that event.
  14. dojo.connect(dojo, "_connect",
  15. function(/*dijit._Widget*/ widget, /*String*/ event){
  16. if(widget && dojo.isFunction(widget._onConnect)){
  17. widget._onConnect(event);
  18. }
  19. });
  20. dijit._connectOnUseEventHandler = function(/*Event*/ event){};
  21. ////////////////// ONDIJITCLICK SUPPORT ///////////////////
  22. // Keep track of where the last keydown event was, to help avoid generating
  23. // spurious ondijitclick events when:
  24. // 1. focus is on a <button> or <a>
  25. // 2. user presses then releases the ENTER key
  26. // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
  27. // 4. onkeyup event fires, causing the ondijitclick handler to fire
  28. dijit._lastKeyDownNode = null;
  29. if(dojo.isIE){
  30. (function(){
  31. var keydownCallback = function(evt){
  32. dijit._lastKeyDownNode = evt.srcElement;
  33. };
  34. dojo.doc.attachEvent('onkeydown', keydownCallback);
  35. dojo.addOnWindowUnload(function(){
  36. dojo.doc.detachEvent('onkeydown', keydownCallback);
  37. });
  38. })();
  39. }else{
  40. dojo.doc.addEventListener('keydown', function(evt){
  41. dijit._lastKeyDownNode = evt.target;
  42. }, true);
  43. }
  44. (function(){
  45. dojo.declare("dijit._Widget", dijit._WidgetBase, {
  46. // summary:
  47. // Base class for all Dijit widgets.
  48. //
  49. // Extends _WidgetBase, adding support for:
  50. // - deferred connections
  51. // A call like dojo.connect(myWidget, "onMouseMove", func)
  52. // will essentially do a dojo.connect(myWidget.domNode, "onMouseMove", func)
  53. // - ondijitclick
  54. // Support new dojoAttachEvent="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
  55. // - focus related functions
  56. // In particular, the onFocus()/onBlur() callbacks. Driven internally by
  57. // dijit/_base/focus.js.
  58. // - deprecated methods
  59. // - onShow(), onHide(), onClose()
  60. //
  61. // Also, by loading code in dijit/_base, turns on:
  62. // - browser sniffing (putting browser id like .dj_ie on <html> node)
  63. // - high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode)
  64. ////////////////// DEFERRED CONNECTS ///////////////////
  65. // _deferredConnects: [protected] Object
  66. // attributeMap addendum for event handlers that should be connected only on first use
  67. _deferredConnects: {
  68. onClick: "",
  69. onDblClick: "",
  70. onKeyDown: "",
  71. onKeyPress: "",
  72. onKeyUp: "",
  73. onMouseMove: "",
  74. onMouseDown: "",
  75. onMouseOut: "",
  76. onMouseOver: "",
  77. onMouseLeave: "",
  78. onMouseEnter: "",
  79. onMouseUp: ""
  80. },
  81. onClick: dijit._connectOnUseEventHandler,
  82. /*=====
  83. onClick: function(event){
  84. // summary:
  85. // Connect to this function to receive notifications of mouse click events.
  86. // event:
  87. // mouse Event
  88. // tags:
  89. // callback
  90. },
  91. =====*/
  92. onDblClick: dijit._connectOnUseEventHandler,
  93. /*=====
  94. onDblClick: function(event){
  95. // summary:
  96. // Connect to this function to receive notifications of mouse double click events.
  97. // event:
  98. // mouse Event
  99. // tags:
  100. // callback
  101. },
  102. =====*/
  103. onKeyDown: dijit._connectOnUseEventHandler,
  104. /*=====
  105. onKeyDown: function(event){
  106. // summary:
  107. // Connect to this function to receive notifications of keys being pressed down.
  108. // event:
  109. // key Event
  110. // tags:
  111. // callback
  112. },
  113. =====*/
  114. onKeyPress: dijit._connectOnUseEventHandler,
  115. /*=====
  116. onKeyPress: function(event){
  117. // summary:
  118. // Connect to this function to receive notifications of printable keys being typed.
  119. // event:
  120. // key Event
  121. // tags:
  122. // callback
  123. },
  124. =====*/
  125. onKeyUp: dijit._connectOnUseEventHandler,
  126. /*=====
  127. onKeyUp: function(event){
  128. // summary:
  129. // Connect to this function to receive notifications of keys being released.
  130. // event:
  131. // key Event
  132. // tags:
  133. // callback
  134. },
  135. =====*/
  136. onMouseDown: dijit._connectOnUseEventHandler,
  137. /*=====
  138. onMouseDown: function(event){
  139. // summary:
  140. // Connect to this function to receive notifications of when the mouse button is pressed down.
  141. // event:
  142. // mouse Event
  143. // tags:
  144. // callback
  145. },
  146. =====*/
  147. onMouseMove: dijit._connectOnUseEventHandler,
  148. /*=====
  149. onMouseMove: function(event){
  150. // summary:
  151. // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
  152. // event:
  153. // mouse Event
  154. // tags:
  155. // callback
  156. },
  157. =====*/
  158. onMouseOut: dijit._connectOnUseEventHandler,
  159. /*=====
  160. onMouseOut: function(event){
  161. // summary:
  162. // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
  163. // event:
  164. // mouse Event
  165. // tags:
  166. // callback
  167. },
  168. =====*/
  169. onMouseOver: dijit._connectOnUseEventHandler,
  170. /*=====
  171. onMouseOver: function(event){
  172. // summary:
  173. // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
  174. // event:
  175. // mouse Event
  176. // tags:
  177. // callback
  178. },
  179. =====*/
  180. onMouseLeave: dijit._connectOnUseEventHandler,
  181. /*=====
  182. onMouseLeave: function(event){
  183. // summary:
  184. // Connect to this function to receive notifications of when the mouse moves off of this widget.
  185. // event:
  186. // mouse Event
  187. // tags:
  188. // callback
  189. },
  190. =====*/
  191. onMouseEnter: dijit._connectOnUseEventHandler,
  192. /*=====
  193. onMouseEnter: function(event){
  194. // summary:
  195. // Connect to this function to receive notifications of when the mouse moves onto this widget.
  196. // event:
  197. // mouse Event
  198. // tags:
  199. // callback
  200. },
  201. =====*/
  202. onMouseUp: dijit._connectOnUseEventHandler,
  203. /*=====
  204. onMouseUp: function(event){
  205. // summary:
  206. // Connect to this function to receive notifications of when the mouse button is released.
  207. // event:
  208. // mouse Event
  209. // tags:
  210. // callback
  211. },
  212. =====*/
  213. create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
  214. // To avoid double-connects, remove entries from _deferredConnects
  215. // that have been setup manually by a subclass (ex, by dojoAttachEvent).
  216. // If a subclass has redefined a callback (ex: onClick) then assume it's being
  217. // connected to manually.
  218. this._deferredConnects = dojo.clone(this._deferredConnects);
  219. for(var attr in this.attributeMap){
  220. delete this._deferredConnects[attr]; // can't be in both attributeMap and _deferredConnects
  221. }
  222. for(attr in this._deferredConnects){
  223. if(this[attr] !== dijit._connectOnUseEventHandler){
  224. delete this._deferredConnects[attr]; // redefined, probably dojoAttachEvent exists
  225. }
  226. }
  227. this.inherited(arguments);
  228. if(this.domNode){
  229. // If the developer has specified a handler as a widget parameter
  230. // (ex: new Button({onClick: ...})
  231. // then naturally need to connect from DOM node to that handler immediately,
  232. for(attr in this.params){
  233. this._onConnect(attr);
  234. }
  235. }
  236. },
  237. _onConnect: function(/*String*/ event){
  238. // summary:
  239. // Called when someone connects to one of my handlers.
  240. // "Turn on" that handler if it isn't active yet.
  241. //
  242. // This is also called for every single initialization parameter
  243. // so need to do nothing for parameters like "id".
  244. // tags:
  245. // private
  246. if(event in this._deferredConnects){
  247. var mapNode = this[this._deferredConnects[event] || 'domNode'];
  248. this.connect(mapNode, event.toLowerCase(), event);
  249. delete this._deferredConnects[event];
  250. }
  251. },
  252. ////////////////// FOCUS RELATED ///////////////////
  253. // _onFocus() and _onBlur() are called by the focus manager
  254. // focused: [readonly] Boolean
  255. // This widget or a widget it contains has focus, or is "active" because
  256. // it was recently clicked.
  257. focused: false,
  258. isFocusable: function(){
  259. // summary:
  260. // Return true if this widget can currently be focused
  261. // and false if not
  262. return this.focus && (dojo.style(this.domNode, "display") != "none");
  263. },
  264. onFocus: function(){
  265. // summary:
  266. // Called when the widget becomes "active" because
  267. // it or a widget inside of it either has focus, or has recently
  268. // been clicked.
  269. // tags:
  270. // callback
  271. },
  272. onBlur: function(){
  273. // summary:
  274. // Called when the widget stops being "active" because
  275. // focus moved to something outside of it, or the user
  276. // clicked somewhere outside of it, or the widget was
  277. // hidden.
  278. // tags:
  279. // callback
  280. },
  281. _onFocus: function(e){
  282. // summary:
  283. // This is where widgets do processing for when they are active,
  284. // such as changing CSS classes. See onFocus() for more details.
  285. // tags:
  286. // protected
  287. this.onFocus();
  288. },
  289. _onBlur: function(){
  290. // summary:
  291. // This is where widgets do processing for when they stop being active,
  292. // such as changing CSS classes. See onBlur() for more details.
  293. // tags:
  294. // protected
  295. this.onBlur();
  296. },
  297. ////////////////// DEPRECATED METHODS ///////////////////
  298. setAttribute: function(/*String*/ attr, /*anything*/ value){
  299. // summary:
  300. // Deprecated. Use set() instead.
  301. // tags:
  302. // deprecated
  303. dojo.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
  304. this.set(attr, value);
  305. },
  306. attr: function(/*String|Object*/name, /*Object?*/value){
  307. // summary:
  308. // Set or get properties on a widget instance.
  309. // name:
  310. // The property to get or set. If an object is passed here and not
  311. // a string, its keys are used as names of attributes to be set
  312. // and the value of the object as values to set in the widget.
  313. // value:
  314. // Optional. If provided, attr() operates as a setter. If omitted,
  315. // the current value of the named property is returned.
  316. // description:
  317. // This method is deprecated, use get() or set() directly.
  318. // Print deprecation warning but only once per calling function
  319. if(dojo.config.isDebug){
  320. var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
  321. caller = (arguments.callee.caller || "unknown caller").toString();
  322. if(!alreadyCalledHash[caller]){
  323. dojo.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
  324. caller, "", "2.0");
  325. alreadyCalledHash[caller] = true;
  326. }
  327. }
  328. var args = arguments.length;
  329. if(args >= 2 || typeof name === "object"){ // setter
  330. return this.set.apply(this, arguments);
  331. }else{ // getter
  332. return this.get(name);
  333. }
  334. },
  335. ////////////////// ONDIJITCLICK SUPPORT ///////////////////
  336. // nodesWithKeyClick: [private] String[]
  337. // List of nodes that correctly handle click events via native browser support,
  338. // and don't need dijit's help
  339. nodesWithKeyClick: ["input", "button"],
  340. connect: function(
  341. /*Object|null*/ obj,
  342. /*String|Function*/ event,
  343. /*String|Function*/ method){
  344. // summary:
  345. // Connects specified obj/event to specified method of this object
  346. // and registers for disconnect() on widget destroy.
  347. // description:
  348. // Provide widget-specific analog to dojo.connect, except with the
  349. // implicit use of this widget as the target object.
  350. // This version of connect also provides a special "ondijitclick"
  351. // event which triggers on a click or space or enter keyup.
  352. // Events connected with `this.connect` are disconnected upon
  353. // destruction.
  354. // returns:
  355. // A handle that can be passed to `disconnect` in order to disconnect before
  356. // the widget is destroyed.
  357. // example:
  358. // | var btn = new dijit.form.Button();
  359. // | // when foo.bar() is called, call the listener we're going to
  360. // | // provide in the scope of btn
  361. // | btn.connect(foo, "bar", function(){
  362. // | console.debug(this.toString());
  363. // | });
  364. // tags:
  365. // protected
  366. var d = dojo,
  367. dc = d._connect,
  368. handles = this.inherited(arguments, [obj, event == "ondijitclick" ? "onclick" : event, method]);
  369. if(event == "ondijitclick"){
  370. // add key based click activation for unsupported nodes.
  371. // do all processing onkey up to prevent spurious clicks
  372. // for details see comments at top of this file where _lastKeyDownNode is defined
  373. if(d.indexOf(this.nodesWithKeyClick, obj.nodeName.toLowerCase()) == -1){ // is NOT input or button
  374. var m = d.hitch(this, method);
  375. handles.push(
  376. dc(obj, "onkeydown", this, function(e){
  377. //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode));
  378. if((e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) &&
  379. !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
  380. // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
  381. dijit._lastKeyDownNode = e.target;
  382. // Stop event to prevent scrolling on space key in IE.
  383. // But don't do this for _HasDropDown because it surpresses the onkeypress
  384. // event needed to open the drop down when the user presses the SPACE key.
  385. if(!("openDropDown" in this && obj == this._buttonNode)){
  386. e.preventDefault();
  387. }
  388. }
  389. }),
  390. dc(obj, "onkeyup", this, function(e){
  391. //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode));
  392. if( (e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) &&
  393. e.target == dijit._lastKeyDownNode && // === breaks greasemonkey
  394. !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
  395. //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
  396. dijit._lastKeyDownNode = null;
  397. return m(e);
  398. }
  399. })
  400. );
  401. }
  402. }
  403. return handles; // _Widget.Handle
  404. },
  405. ////////////////// MISCELLANEOUS METHODS ///////////////////
  406. _onShow: function(){
  407. // summary:
  408. // Internal method called when this widget is made visible.
  409. // See `onShow` for details.
  410. this.onShow();
  411. },
  412. onShow: function(){
  413. // summary:
  414. // Called when this widget becomes the selected pane in a
  415. // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
  416. // `dijit.layout.AccordionContainer`, etc.
  417. //
  418. // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
  419. // tags:
  420. // callback
  421. },
  422. onHide: function(){
  423. // summary:
  424. // Called when another widget becomes the selected pane in a
  425. // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
  426. // `dijit.layout.AccordionContainer`, etc.
  427. //
  428. // Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
  429. // tags:
  430. // callback
  431. },
  432. onClose: function(){
  433. // summary:
  434. // Called when this widget is being displayed as a popup (ex: a Calendar popped
  435. // up from a DateTextBox), and it is hidden.
  436. // This is called from the dijit.popup code, and should not be called directly.
  437. //
  438. // Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses.
  439. // Callback if a user tries to close the child. Child will be closed if this function returns true.
  440. // tags:
  441. // extension
  442. return true; // Boolean
  443. }
  444. });
  445. })();
  446. }