]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/_WidgetBase.js.uncompressed.js
1 define("dijit/_WidgetBase", [
2 "require", // require.toUrl
3 "dojo/_base/array", // array.forEach array.map
5 "dojo/_base/config", // config.blankGif
6 "dojo/_base/connect", // connect.connect
7 "dojo/_base/declare", // declare
8 "dojo/dom", // dom.byId
9 "dojo/dom-attr", // domAttr.set domAttr.remove
10 "dojo/dom-class", // domClass.add domClass.replace
11 "dojo/dom-construct", // domConstruct.create domConstruct.destroy domConstruct.place
12 "dojo/dom-geometry", // isBodyLtr
13 "dojo/dom-style", // domStyle.set, domStyle.get
15 "dojo/_base/lang", // mixin(), isArray(), etc.
18 "dojo/Stateful", // Stateful
20 "dojo/_base/window", // win.doc.createTextNode
21 "./registry" // registry.getUniqueId(), registry.findWidgets()
22 ], function(require
, array
, aspect
, config
, connect
, declare
,
23 dom
, domAttr
, domClass
, domConstruct
, domGeometry
, domStyle
, kernel
,
24 lang
, on
, ready
, Stateful
, topic
, win
, registry
){
27 var Stateful = dojo.Stateful;
33 // Future base class for all Dijit widgets.
35 // For back-compat, remove in 2.0.
38 var requires
= ["dijit/_base/manager"];
39 require(requires
); // use indirection so modules not rolled into a build
43 // Nested hash listing attributes for each tag, all strings in lowercase.
44 // ex: {"div": {"style": true, "tabindex" true}, "form": { ...
46 function getAttrs(obj
){
49 ret
[attr
.toLowerCase()] = true;
54 function nonEmptyAttrToDom(attr
){
56 // Returns a setter function that copies the attribute to this.domNode,
57 // or removes the attribute from this.domNode, depending on whether the
58 // value is defined or not.
60 domAttr
[val
? "set" : "remove"](this.domNode
, attr
, val
);
65 return declare("dijit._WidgetBase", Stateful
, {
67 // Future base class for all Dijit widgets.
69 // Future base class for all Dijit widgets.
70 // _Widget extends this class adding support for various features needed by desktop.
72 // Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(),
73 // postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch().
75 // Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value).
76 // For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr().
78 // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes:
80 // - DOM node attribute
81 // | _setFocusAttr: {node: "focusNode", type: "attribute"}
82 // | _setFocusAttr: "focusNode" (shorthand)
83 // | _setFocusAttr: "" (shorthand, maps to this.domNode)
84 // Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus
86 // - DOM node innerHTML
87 // | _setTitleAttr: { node: "titleNode", type: "innerHTML" }
88 // Maps this.title to this.titleNode.innerHTML
90 // - DOM node innerText
91 // | _setTitleAttr: { node: "titleNode", type: "innerText" }
92 // Maps this.title to this.titleNode.innerText
94 // - DOM node CSS class
95 // | _setMyClassAttr: { node: "domNode", type: "class" }
96 // Maps this.myClass to this.domNode.className
98 // If the value of _setXXXAttr is an array, then each element in the array matches one of the
99 // formats of the above list.
101 // If the custom setter is null, no action is performed other than saving the new value
102 // in the widget (in this).
104 // If no custom setter is defined for an attribute, then it will be copied
105 // to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise.
106 // That's only done though for attributes that match DOMNode attributes (title,
107 // alt, aria-labelledby, etc.)
109 // id: [const] String
110 // A unique, opaque ID string that can be assigned by users or by the
111 // system. If the developer passes an ID which is known not to be
112 // unique, the specified ID is ignored and the system-generated ID is
115 _setIdAttr
: "domNode", // to copy to this.domNode even for auto-generated id's
117 // lang: [const] String
118 // Rarely used. Overrides the default Dojo locale used to render this widget,
119 // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
120 // Value must be among the list of locales specified during by the Dojo bootstrap,
121 // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
123 // set on domNode even when there's a focus node. but don't set lang="", since that's invalid.
124 _setLangAttr
: nonEmptyAttrToDom("lang"),
126 // dir: [const] String
127 // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
128 // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's
129 // default direction.
131 // set on domNode even when there's a focus node. but don't set dir="", since that's invalid.
132 _setDirAttr
: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node
135 // Bi-directional support, the main variable which is responsible for the direction of the text.
136 // The text direction can be different than the GUI direction by using this parameter in creation
141 // 3. "auto" - contextual the direction of a text defined by first strong letter.
142 // By default is as the page direction.
146 // HTML class attribute
148 _setClassAttr
: { node
: "domNode", type
: "class" },
150 // style: String||Object
151 // HTML style attributes as cssText string or name/value hash
155 // HTML title attribute.
157 // For form widgets this specifies a tooltip to display when hovering over
158 // the widget (just like the native HTML title attribute).
160 // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
161 // etc., it's used to specify the tab label, accordion pane title, etc.
165 // When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
166 // this specifies the tooltip to appear when the mouse is hovered over that text.
169 // baseClass: [protected] String
170 // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
174 // srcNodeRef: [readonly] DomNode
175 // pointer to original DOM node
178 // domNode: [readonly] DomNode
179 // This is our visible representation of the widget! Other DOM
180 // Nodes may by assigned to other properties, usually through the
181 // template system's data-dojo-attach-point syntax, but the domNode
182 // property is the canonical "top level" node in widget UI.
185 // containerNode: [readonly] DomNode
186 // Designates where children of the source DOM node will be placed.
187 // "Children" in this case refers to both DOM nodes and widgets.
188 // For example, for myWidget:
190 // | <div data-dojo-type=myWidget>
191 // | <b> here's a plain DOM node
192 // | <span data-dojo-type=subWidget>and a widget</span>
193 // | <i> and another plain DOM node </i>
196 // containerNode would point to:
198 // | <b> here's a plain DOM node
199 // | <span data-dojo-type=subWidget>and a widget</span>
200 // | <i> and another plain DOM node </i>
202 // In templated widgets, "containerNode" is set via a
203 // data-dojo-attach-point assignment.
205 // containerNode must be defined for any widget that accepts innerHTML
206 // (like ContentPane or BorderContainer or even Button), and conversely
207 // is null for widgets that don't, like TextBox.
212 // startup() has completed.
216 // attributeMap: [protected] Object
217 // Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute
218 // for each XXX attribute to be mapped to the DOM.
220 // attributeMap sets up a "binding" between attributes (aka properties)
221 // of the widget and the widget's DOM.
222 // Changes to widget attributes listed in attributeMap will be
223 // reflected into the DOM.
225 // For example, calling set('title', 'hello')
226 // on a TitlePane will automatically cause the TitlePane's DOM to update
227 // with the new title.
229 // attributeMap is a hash where the key is an attribute of the widget,
230 // and the value reflects a binding to a:
232 // - DOM node attribute
233 // | focus: {node: "focusNode", type: "attribute"}
234 // Maps this.focus to this.focusNode.focus
236 // - DOM node innerHTML
237 // | title: { node: "titleNode", type: "innerHTML" }
238 // Maps this.title to this.titleNode.innerHTML
240 // - DOM node innerText
241 // | title: { node: "titleNode", type: "innerText" }
242 // Maps this.title to this.titleNode.innerText
244 // - DOM node CSS class
245 // | myClass: { node: "domNode", type: "class" }
246 // Maps this.myClass to this.domNode.className
248 // If the value is an array, then each element in the array matches one of the
249 // formats of the above list.
251 // There are also some shorthands for backwards compatibility:
252 // - string --> { node: string, type: "attribute" }, for example:
253 // | "focusNode" ---> { node: "focusNode", type: "attribute" }
254 // - "" --> { node: "domNode", type: "attribute" }
257 // _blankGif: [protected] String
258 // Path to a blank 1x1 image.
259 // Used by <img> nodes in templates that really get their image via CSS background-image.
260 _blankGif
: config
.blankGif
|| require
.toUrl("dojo/resources/blank.gif"),
262 //////////// INITIALIZATION METHODS ///////////////////////////////////////
264 postscript: function(/*Object?*/params
, /*DomNode|String*/srcNodeRef
){
266 // Kicks off widget instantiation. See create() for details.
269 this.create(params
, srcNodeRef
);
272 create: function(/*Object?*/params
, /*DomNode|String?*/srcNodeRef
){
274 // Kick off the life-cycle of a widget
276 // Hash of initialization parameters for widget, including
277 // scalar values (like title, duration etc.) and functions,
278 // typically callbacks like onClick.
280 // If a srcNodeRef (DOM node) is specified:
281 // - use srcNodeRef.innerHTML as my contents
282 // - if this is a behavioral widget then apply behavior
283 // to that srcNodeRef
284 // - otherwise, replace srcNodeRef with my generated DOM
287 // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
288 // etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html
289 // for a discussion of the widget creation lifecycle.
291 // Of course, adventurous developers could override create entirely, but this should
292 // only be done as a last resort.
296 // store pointer to original DOM tree
297 this.srcNodeRef
= dom
.byId(srcNodeRef
);
299 // For garbage collection. An array of listener handles returned by this.connect() / this.subscribe()
302 // For widgets internal to this widget, invisible to calling code
303 this._supportingWidgets
= [];
305 // this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test)
306 if(this.srcNodeRef
&& (typeof this.srcNodeRef
.id
== "string")){ this.id
= this.srcNodeRef
.id
; }
308 // mix in our passed parameters
310 this.params
= params
;
311 lang
.mixin(this, params
);
313 this.postMixInProperties();
315 // generate an id for the widget if one wasn't specified
316 // (be sure to do this before buildRendering() because that function might
317 // expect the id to be there.)
319 this.id
= registry
.getUniqueId(this.declaredClass
.replace(/\./g,"_"));
323 this.buildRendering();
326 // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
327 // Also calls custom setters for all attributes with custom setters.
328 this._applyAttributes();
330 // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
331 // For 2.0, move this after postCreate(). postCreate() shouldn't depend on the
332 // widget being attached to the DOM since it isn't when a widget is created programmatically like
333 // new MyWidget({}). See #11635.
334 var source
= this.srcNodeRef
;
335 if(source
&& source
.parentNode
&& this.domNode
!== source
){
336 source
.parentNode
.replaceChild(this.domNode
, source
);
341 // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
342 // assuming that dojo._scopeName even exists in 2.0
343 this.domNode
.setAttribute("widgetId", this.id
);
347 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
348 if(this.srcNodeRef
&& !this.srcNodeRef
.parentNode
){
349 delete this.srcNodeRef
;
352 this._created
= true;
355 _applyAttributes: function(){
357 // Step during widget creation to copy widget attributes to the
358 // DOM according to attributeMap and _setXXXAttr objects, and also to call
359 // custom _setXXXAttr() methods.
361 // Skips over blank/false attribute values, unless they were explicitly specified
362 // as parameters to the widget, since those are the default anyway,
363 // and setting tabIndex="" is different than not setting tabIndex at all.
365 // For backwards-compatibility reasons attributeMap overrides _setXXXAttr when
366 // _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap.
370 // Get list of attributes where this.set(name, value) will do something beyond
371 // setting this[name] = value. Specifically, attributes that have:
372 // - associated _setXXXAttr() method/hash/string/array
373 // - entries in attributeMap.
374 var ctor
= this.constructor,
375 list
= ctor
._setterAttrs
;
377 list
= (ctor
._setterAttrs
= []);
378 for(var attr
in this.attributeMap
){
382 var proto
= ctor
.prototype;
383 for(var fxName
in proto
){
384 if(fxName
in this.attributeMap
){ continue; }
385 var setterName
= "_set" + fxName
.replace(/^[a-z]|-[a-zA-Z]/g, function(c
){ return c
.charAt(c
.length
-1).toUpperCase(); }) + "Attr";
386 if(setterName
in proto
){
392 // Call this.set() for each attribute that was either specified as parameter to constructor,
393 // or was found above and has a default non-null value. For correlated attributes like value and displayedValue, the one
394 // specified as a parameter should take precedence, so apply attributes in this.params last.
395 // Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is
396 // NaN and thus is not ignored like a default value of "".
397 array
.forEach(list
, function(attr
){
398 if(this.params
&& attr
in this.params
){
399 // skip this one, do it below
400 }else if(this[attr
]){
401 this.set(attr
, this[attr
]);
404 for(var param
in this.params
){
405 this.set(param
, this[param
]);
409 postMixInProperties: function(){
411 // Called after the parameters to the widget have been read-in,
412 // but before the widget template is instantiated. Especially
413 // useful to set properties that are referenced in the widget
419 buildRendering: function(){
421 // Construct the UI for this widget, setting this.domNode.
422 // Most widgets will mixin `dijit._TemplatedMixin`, which implements this method.
427 // Create root node if it wasn't created by _Templated
428 this.domNode
= this.srcNodeRef
|| domConstruct
.create('div');
431 // baseClass is a single class name or occasionally a space-separated list of names.
432 // Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix.
433 // TODO: make baseClass custom setter
435 var classes
= this.baseClass
.split(" ");
436 if(!this.isLeftToRight()){
437 classes
= classes
.concat( array
.map(classes
, function(name
){ return name
+"Rtl"; }));
439 domClass
.add(this.domNode
, classes
);
443 postCreate: function(){
445 // Processing after the DOM fragment is created
447 // Called after the DOM fragment has been created, but not necessarily
448 // added to the document. Do not include any operations which rely on
449 // node dimensions or placement.
456 // Processing after the DOM fragment is added to the document
458 // Called after a widget and its children have been created and added to the page,
459 // and all related widgets have finished their create() cycle, up through postCreate().
460 // This is useful for composite widgets that need to control or layout sub-widgets.
461 // Many layout widgets can use this as a wiring phase.
462 if(this._started
){ return; }
463 this._started
= true;
464 array
.forEach(this.getChildren(), function(obj
){
465 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
472 //////////// DESTROY FUNCTIONS ////////////////////////////////
474 destroyRecursive: function(/*Boolean?*/ preserveDom
){
476 // Destroy this widget and its descendants
478 // This is the generic "destructor" function that all widget users
479 // should call to cleanly discard with a widget. Once a widget is
480 // destroyed, it is removed from the manager object.
482 // If true, this method will leave the original DOM structure
483 // alone of descendant Widgets. Note: This will NOT work with
484 // dijit._Templated widgets.
486 this._beingDestroyed
= true;
487 this.destroyDescendants(preserveDom
);
488 this.destroy(preserveDom
);
491 destroy: function(/*Boolean*/ preserveDom
){
493 // Destroy this widget, but not its descendants.
494 // This method will, however, destroy internal widgets such as those used within a template.
495 // preserveDom: Boolean
496 // If true, this method will leave the original DOM structure alone.
497 // Note: This will not yet work with _Templated widgets
499 this._beingDestroyed
= true;
502 // remove this.connect() and this.subscribe() listeners
504 while((c
= this._connects
.pop())){
508 // destroy widgets created as part of template, etc.
510 while((w
= this._supportingWidgets
.pop())){
511 if(w
.destroyRecursive
){
512 w
.destroyRecursive();
518 this.destroyRendering(preserveDom
);
519 registry
.remove(this.id
);
520 this._destroyed
= true;
523 destroyRendering: function(/*Boolean?*/ preserveDom
){
525 // Destroys the DOM nodes associated with this widget
527 // If true, this method will leave the original DOM structure alone
528 // during tear-down. Note: this will not work with _Templated
534 this.bgIframe
.destroy(preserveDom
);
535 delete this.bgIframe
;
540 domAttr
.remove(this.domNode
, "widgetId");
542 domConstruct
.destroy(this.domNode
);
549 domConstruct
.destroy(this.srcNodeRef
);
551 delete this.srcNodeRef
;
555 destroyDescendants: function(/*Boolean?*/ preserveDom
){
557 // Recursively destroy the children of this widget and their
560 // If true, the preserveDom attribute is passed to all descendant
561 // widget's .destroy() method. Not for use with _Templated
564 // get all direct descendants and destroy them recursively
565 array
.forEach(this.getChildren(), function(widget
){
566 if(widget
.destroyRecursive
){
567 widget
.destroyRecursive(preserveDom
);
572 uninitialize: function(){
574 // Stub function. Override to implement custom widget tear-down
581 ////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
583 _setStyleAttr: function(/*String||Object*/ value
){
585 // Sets the style attribute of the widget according to value,
586 // which is either a hash like {height: "5px", width: "3px"}
589 // Determines which node to set the style on based on style setting
594 var mapNode
= this.domNode
;
596 // Note: technically we should revert any style setting made in a previous call
597 // to his method, but that's difficult to keep track of.
599 if(lang
.isObject(value
)){
600 domStyle
.set(mapNode
, value
);
602 if(mapNode
.style
.cssText
){
603 mapNode
.style
.cssText
+= "; " + value
;
605 mapNode
.style
.cssText
= value
;
609 this._set("style", value
);
612 _attrToDom: function(/*String*/ attr
, /*String*/ value
, /*Object?*/ commands
){
614 // Reflect a widget attribute (title, tabIndex, duration etc.) to
615 // the widget DOM, as specified by commands parameter.
616 // If commands isn't specified then it's looked up from attributeMap.
617 // Note some attributes like "type"
618 // cannot be processed this way as they are not mutable.
623 commands
= arguments
.length
>= 3 ? commands
: this.attributeMap
[attr
];
625 array
.forEach(lang
.isArray(commands
) ? commands
: [commands
], function(command
){
627 // Get target node and what we are doing to that node
628 var mapNode
= this[command
.node
|| command
|| "domNode"]; // DOM node
629 var type
= command
.type
|| "attribute"; // class, innerHTML, innerText, or attribute
633 if(lang
.isFunction(value
)){ // functions execute in the context of the widget
634 value
= lang
.hitch(this, value
);
637 // Get the name of the DOM node attribute; usually it's the same
638 // as the name of the attribute in the widget (attr), but can be overridden.
639 // Also maps handler names to lowercase, like onSubmit --> onsubmit
640 var attrName
= command
.attribute
? command
.attribute
:
641 (/^on[A-Z][a-zA-Z]*$/.test(attr
) ? attr
.toLowerCase() : attr
);
643 domAttr
.set(mapNode
, attrName
, value
);
646 mapNode
.innerHTML
= "";
647 mapNode
.appendChild(win
.doc
.createTextNode(value
));
650 mapNode
.innerHTML
= value
;
653 domClass
.replace(mapNode
, value
, this[attr
]);
661 // Get a property from a widget.
663 // The property to get.
665 // Get a named property from a widget. The property may
666 // potentially be retrieved via a getter method. If no getter is defined, this
667 // just retrieves the object's property.
669 // For example, if the widget has properties `foo` and `bar`
670 // and a method named `_getFooAttr()`, calling:
671 // `myWidget.get("foo")` would be equivalent to calling
672 // `widget._getFooAttr()` and `myWidget.get("bar")`
673 // would be equivalent to the expression
675 var names
= this._getAttrNames(name
);
676 return this[names
.g
] ? this[names
.g
]() : this[name
];
679 set: function(name
, value
){
681 // Set a property on a widget
683 // The property to set.
685 // The value to set in the property.
687 // Sets named properties on a widget which may potentially be handled by a
688 // setter in the widget.
690 // For example, if the widget has properties `foo` and `bar`
691 // and a method named `_setFooAttr()`, calling
692 // `myWidget.set("foo", "Howdy!")` would be equivalent to calling
693 // `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)`
694 // would be equivalent to the statement `widget.bar = 3;`
696 // set() may also be called with a hash of name/value pairs, ex:
703 // This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)`
705 if(typeof name
=== "object"){
707 this.set(x
, name
[x
]);
711 var names
= this._getAttrNames(name
),
712 setter
= this[names
.s
];
713 if(lang
.isFunction(setter
)){
714 // use the explicit setter
715 var result
= setter
.apply(this, Array
.prototype.slice
.call(arguments
, 1));
717 // Mapping from widget attribute to DOMNode attribute/value/etc.
719 // 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0)
720 // 2. _setFooAttr: {...} type attribute in the widget (if one exists)
721 // 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick.
722 // Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar
723 // attribute name (ex: accept-charset attribute matches jsObject.acceptCharset).
724 // Note also that Tree.focusNode() is a function not a DOMNode, so test for that.
725 var defaultNode
= this.focusNode
&& !lang
.isFunction(this.focusNode
) ? "focusNode" : "domNode",
726 tag
= this[defaultNode
].tagName
,
727 attrsForTag
= tagAttrs
[tag
] || (tagAttrs
[tag
] = getAttrs(this[defaultNode
])),
728 map
= name
in this.attributeMap
? this.attributeMap
[name
] :
729 names
.s
in this ? this[names
.s
] :
730 ((names
.l
in attrsForTag
&& typeof value
!= "function") ||
731 /^aria-|^data-|^role$/.test(name
)) ? defaultNode
: null;
733 this._attrToDom(name
, value
, map
);
735 this._set(name
, value
);
737 return result
|| this;
740 _attrPairNames
: {}, // shared between all widgets
741 _getAttrNames: function(name
){
743 // Helper function for get() and set().
744 // Caches attribute name values so we don't do the string ops every time.
748 var apn
= this._attrPairNames
;
749 if(apn
[name
]){ return apn
[name
]; }
750 var uc
= name
.replace(/^[a-z]|-[a-zA-Z]/g, function(c
){ return c
.charAt(c
.length
-1).toUpperCase(); });
751 return (apn
[name
] = {
753 s
: "_set"+uc
+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr
755 l
: uc
.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset
759 _set: function(/*String*/ name
, /*anything*/ value
){
761 // Helper function to set new value for specified attribute, and call handlers
762 // registered with watch() if the value has changed.
763 var oldValue
= this[name
];
765 if(this._watchCallbacks
&& this._created
&& value
!== oldValue
){
766 this._watchCallbacks(name
, oldValue
, value
);
770 on: function(/*String*/ type
, /*Function*/ func
){
772 // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }).
774 // Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`.
775 // Note that the function is not run in any particular scope, so if (for example) you want it to run in the
776 // widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`.
778 return aspect
.after(this, this._onMap(type
), func
, true);
781 _onMap: function(/*String*/ type
){
783 // Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove")
784 var ctor
= this.constructor, map
= ctor
._onMap
;
786 map
= (ctor
._onMap
= {});
787 for(var attr
in ctor
.prototype){
788 if(/^on/.test(attr
)){
789 map
[attr
.replace(/^on/, "").toLowerCase()] = attr
;
793 return map
[type
.toLowerCase()]; // String
796 toString: function(){
798 // Returns a string that represents the widget
800 // When a widget is cast to a string, this method will be used to generate the
801 // output. Currently, it does not implement any sort of reversible
803 return '[Widget ' + this.declaredClass
+ ', ' + (this.id
|| 'NO ID') + ']'; // String
806 getChildren: function(){
808 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
809 // Does not return nested widgets, nor widgets that are part of this widget's template.
810 return this.containerNode
? registry
.findWidgets(this.containerNode
) : []; // dijit._Widget[]
813 getParent: function(){
815 // Returns the parent widget of this widget
816 return registry
.getEnclosingWidget(this.domNode
.parentNode
);
821 /*String|Function*/ event
,
822 /*String|Function*/ method
){
824 // Connects specified obj/event to specified method of this object
825 // and registers for disconnect() on widget destroy.
827 // Provide widget-specific analog to dojo.connect, except with the
828 // implicit use of this widget as the target object.
829 // Events connected with `this.connect` are disconnected upon
832 // A handle that can be passed to `disconnect` in order to disconnect before
833 // the widget is destroyed.
835 // | var btn = new dijit.form.Button();
836 // | // when foo.bar() is called, call the listener we're going to
837 // | // provide in the scope of btn
838 // | btn.connect(foo, "bar", function(){
839 // | console.debug(this.toString());
844 var handle
= connect
.connect(obj
, event
, this, method
);
845 this._connects
.push(handle
);
846 return handle
; // _Widget.Handle
849 disconnect: function(handle
){
851 // Disconnects handle created by `connect`.
852 // Also removes handle from this widget's list of connects.
855 var i
= array
.indexOf(this._connects
, handle
);
858 this._connects
.splice(i
, 1);
862 subscribe: function(t
, method
){
864 // Subscribes to the specified topic and calls the specified method
865 // of this object and registers for unsubscribe() on widget destroy.
867 // Provide widget-specific analog to dojo.subscribe, except with the
868 // implicit use of this widget as the target object.
874 // | var btn = new dijit.form.Button();
875 // | // when /my/topic is published, this button changes its label to
876 // | // be the parameter of the topic.
877 // | btn.subscribe("/my/topic", function(v){
878 // | this.set("label", v);
882 var handle
= topic
.subscribe(t
, lang
.hitch(this, method
));
883 this._connects
.push(handle
);
884 return handle
; // _Widget.Handle
887 unsubscribe: function(/*Object*/ handle
){
889 // Unsubscribes handle created by this.subscribe.
890 // Also removes handle from this widget's list of subscriptions
893 this.disconnect(handle
);
896 isLeftToRight: function(){
898 // Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
901 return this.dir
? (this.dir
== "ltr") : domGeometry
.isBodyLtr(); //Boolean
904 isFocusable: function(){
906 // Return true if this widget can currently be focused
908 return this.focus
&& (domStyle
.get(this.domNode
, "display") != "none");
911 placeAt: function(/* String|DomNode|_Widget */reference
, /* String?|Int? */position
){
913 // Place this widget's domNode reference somewhere in the DOM based
914 // on standard domConstruct.place conventions, or passing a Widget reference that
915 // contains and addChild member.
918 // A convenience function provided in all _Widgets, providing a simple
919 // shorthand mechanism to put an existing (or newly created) Widget
920 // somewhere in the dom, and allow chaining.
923 // The String id of a domNode, a domNode reference, or a reference to a Widget possessing
924 // an addChild method.
927 // If passed a string or domNode reference, the position argument
928 // accepts a string just as domConstruct.place does, one of: "first", "last",
929 // "before", or "after".
931 // If passed a _Widget reference, and that widget reference has an ".addChild" method,
932 // it will be called passing this widget instance into that method, supplying the optional
933 // position index passed.
937 // Provides a useful return of the newly created dijit._Widget instance so you
938 // can "chain" this function by instantiating, placing, then saving the return value
942 // | // create a Button with no srcNodeRef, and place it in the body:
943 // | var button = new dijit.form.Button({ label:"click" }).placeAt(win.body());
944 // | // now, 'button' is still the widget reference to the newly created button
945 // | button.on("click", function(e){ console.log('click'); }));
948 // | // create a button out of a node with id="src" and append it to id="wrapper":
949 // | var button = new dijit.form.Button({},"src").placeAt("wrapper");
952 // | // place a new button as the first element of some div
953 // | var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first");
956 // | // create a contentpane and add it to a TabContainer
957 // | var tc = dijit.byId("myTabs");
958 // | new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
960 if(reference
.declaredClass
&& reference
.addChild
){
961 reference
.addChild(this, position
);
963 domConstruct
.place(this.domNode
, reference
, position
);
968 getTextDir: function(/*String*/ text
,/*String*/ originalDir
){
970 // Return direction of the text.
971 // The function overridden in the _BidiSupport module,
972 // its main purpose is to calculate the direction of the
973 // text, if was defined by the programmer through textDir.
979 applyTextDir: function(/*===== element, text =====*/){
981 // The function overridden in the _BidiSupport module,
982 // originally used for setting element.dir according to this.textDir.
983 // In this case does nothing.
990 defer: function(fcn
, delay
){
992 // Wrapper to setTimeout to avoid deferred functions executing
993 // after the originating widget has been destroyed.
994 // Returns an object handle with a remove method (that returns null) (replaces clearTimeout).
995 // fcn: function reference
996 // delay: Optional number (defaults to 0)
999 var timer
= setTimeout(lang
.hitch(this,
1002 if(!this._destroyed
){
1003 lang
.hitch(this, fcn
)();
1011 clearTimeout(timer
);
1014 return null; // so this works well: handle = handle.remove();