]>
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.destroy domConstruct.place
12 "dojo/dom-geometry", // isBodyLtr
13 "dojo/dom-style", // domStyle.set, domStyle.get
16 "dojo/_base/lang", // mixin(), isArray(), etc.
19 "dojo/Stateful", // Stateful
21 "dojo/_base/window", // win.doc, win.body()
23 "./registry" // registry.getUniqueId(), registry.findWidgets()
24 ], function(require
, array
, aspect
, config
, connect
, declare
,
25 dom
, domAttr
, domClass
, domConstruct
, domGeometry
, domStyle
, has
, kernel
,
26 lang
, on
, ready
, Stateful
, topic
, win
, Destroyable
, registry
){
31 // Flag to make dijit load modules the app didn't explicitly request, for backwards compatibility
32 has
.add("dijit-legacy-requires", !kernel
.isAsync
);
34 // For back-compat, remove in 2.0.
35 if(has("dijit-legacy-requires")){
37 var requires
= ["dijit/_base/manager"];
38 require(requires
); // use indirection so modules not rolled into a build
42 // Nested hash listing attributes for each tag, all strings in lowercase.
43 // ex: {"div": {"style": true, "tabindex" true}, "form": { ...
45 function getAttrs(obj
){
48 ret
[attr
.toLowerCase()] = true;
53 function nonEmptyAttrToDom(attr
){
55 // Returns a setter function that copies the attribute to this.domNode,
56 // or removes the attribute from this.domNode, depending on whether the
57 // value is defined or not.
59 domAttr
[val
? "set" : "remove"](this.domNode
, attr
, val
);
64 return declare("dijit._WidgetBase", [Stateful
, Destroyable
], {
66 // Future base class for all Dijit widgets.
68 // Future base class for all Dijit widgets.
69 // _Widget extends this class adding support for various features needed by desktop.
71 // Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(),
72 // postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch().
74 // Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value).
75 // For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr().
77 // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes:
79 // - DOM node attribute
80 // | _setFocusAttr: {node: "focusNode", type: "attribute"}
81 // | _setFocusAttr: "focusNode" (shorthand)
82 // | _setFocusAttr: "" (shorthand, maps to this.domNode)
83 // Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus
85 // - DOM node innerHTML
86 // | _setTitleAttr: { node: "titleNode", type: "innerHTML" }
87 // Maps this.title to this.titleNode.innerHTML
89 // - DOM node innerText
90 // | _setTitleAttr: { node: "titleNode", type: "innerText" }
91 // Maps this.title to this.titleNode.innerText
93 // - DOM node CSS class
94 // | _setMyClassAttr: { node: "domNode", type: "class" }
95 // Maps this.myClass to this.domNode.className
97 // If the value of _setXXXAttr is an array, then each element in the array matches one of the
98 // formats of the above list.
100 // If the custom setter is null, no action is performed other than saving the new value
101 // in the widget (in this).
103 // If no custom setter is defined for an attribute, then it will be copied
104 // to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise.
105 // That's only done though for attributes that match DOMNode attributes (title,
106 // alt, aria-labelledby, etc.)
108 // id: [const] String
109 // A unique, opaque ID string that can be assigned by users or by the
110 // system. If the developer passes an ID which is known not to be
111 // unique, the specified ID is ignored and the system-generated ID is
114 _setIdAttr
: "domNode", // to copy to this.domNode even for auto-generated id's
116 // lang: [const] String
117 // Rarely used. Overrides the default Dojo locale used to render this widget,
118 // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
119 // Value must be among the list of locales specified during by the Dojo bootstrap,
120 // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
122 // set on domNode even when there's a focus node. but don't set lang="", since that's invalid.
123 _setLangAttr
: nonEmptyAttrToDom("lang"),
125 // dir: [const] String
126 // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
127 // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's
128 // default direction.
130 // set on domNode even when there's a focus node. but don't set dir="", since that's invalid.
131 _setDirAttr
: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node
134 // Bi-directional support, the main variable which is responsible for the direction of the text.
135 // The text direction can be different than the GUI direction by using this parameter in creation
142 // 3. "auto" - contextual the direction of a text defined by first strong letter.
144 // By default is as the page direction.
148 // HTML class attribute
150 _setClassAttr
: { node
: "domNode", type
: "class" },
152 // style: String||Object
153 // HTML style attributes as cssText string or name/value hash
157 // HTML title attribute.
159 // For form widgets this specifies a tooltip to display when hovering over
160 // the widget (just like the native HTML title attribute).
162 // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
163 // etc., it's used to specify the tab label, accordion pane title, etc.
167 // When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
168 // this specifies the tooltip to appear when the mouse is hovered over that text.
171 // baseClass: [protected] String
172 // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
176 // srcNodeRef: [readonly] DomNode
177 // pointer to original DOM node
180 // domNode: [readonly] DomNode
181 // This is our visible representation of the widget! Other DOM
182 // Nodes may by assigned to other properties, usually through the
183 // template system's data-dojo-attach-point syntax, but the domNode
184 // property is the canonical "top level" node in widget UI.
187 // containerNode: [readonly] DomNode
188 // Designates where children of the source DOM node will be placed.
189 // "Children" in this case refers to both DOM nodes and widgets.
190 // For example, for myWidget:
192 // | <div data-dojo-type=myWidget>
193 // | <b> here's a plain DOM node
194 // | <span data-dojo-type=subWidget>and a widget</span>
195 // | <i> and another plain DOM node </i>
198 // containerNode would point to:
200 // | <b> here's a plain DOM node
201 // | <span data-dojo-type=subWidget>and a widget</span>
202 // | <i> and another plain DOM node </i>
204 // In templated widgets, "containerNode" is set via a
205 // data-dojo-attach-point assignment.
207 // containerNode must be defined for any widget that accepts innerHTML
208 // (like ContentPane or BorderContainer or even Button), and conversely
209 // is null for widgets that don't, like TextBox.
212 // ownerDocument: [const] Document?
213 // The document this widget belongs to. If not specified to constructor, will default to
214 // srcNodeRef.ownerDocument, or if no sourceRef specified, then to dojo/_base/window::doc
216 _setOwnerDocumentAttr: function(val
){
217 // this setter is merely to avoid automatically trying to set this.domNode.ownerDocument
218 this._set("ownerDocument", val
);
222 // _started: [readonly] Boolean
223 // startup() has completed.
227 // attributeMap: [protected] Object
228 // Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute
229 // for each XXX attribute to be mapped to the DOM.
231 // attributeMap sets up a "binding" between attributes (aka properties)
232 // of the widget and the widget's DOM.
233 // Changes to widget attributes listed in attributeMap will be
234 // reflected into the DOM.
236 // For example, calling set('title', 'hello')
237 // on a TitlePane will automatically cause the TitlePane's DOM to update
238 // with the new title.
240 // attributeMap is a hash where the key is an attribute of the widget,
241 // and the value reflects a binding to a:
243 // - DOM node attribute
244 // | focus: {node: "focusNode", type: "attribute"}
245 // Maps this.focus to this.focusNode.focus
247 // - DOM node innerHTML
248 // | title: { node: "titleNode", type: "innerHTML" }
249 // Maps this.title to this.titleNode.innerHTML
251 // - DOM node innerText
252 // | title: { node: "titleNode", type: "innerText" }
253 // Maps this.title to this.titleNode.innerText
255 // - DOM node CSS class
256 // | myClass: { node: "domNode", type: "class" }
257 // Maps this.myClass to this.domNode.className
259 // If the value is an array, then each element in the array matches one of the
260 // formats of the above list.
262 // There are also some shorthands for backwards compatibility:
264 // - string --> { node: string, type: "attribute" }, for example:
266 // | "focusNode" ---> { node: "focusNode", type: "attribute" }
268 // - "" --> { node: "domNode", type: "attribute" }
271 // _blankGif: [protected] String
272 // Path to a blank 1x1 image.
273 // Used by `<img>` nodes in templates that really get their image via CSS background-image.
274 _blankGif
: config
.blankGif
|| require
.toUrl("dojo/resources/blank.gif"),
276 //////////// INITIALIZATION METHODS ///////////////////////////////////////
279 constructor: function(params, srcNodeRef){
281 // Create the widget.
282 // params: Object|null
283 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
284 // and functions, typically callbacks like onClick.
285 // The hash can contain any of the widget's properties, excluding read-only properties.
286 // srcNodeRef: DOMNode|String?
287 // If a srcNodeRef (DOM node) is specified:
289 // - use srcNodeRef.innerHTML as my contents
290 // - if this is a behavioral widget then apply behavior to that srcNodeRef
291 // - otherwise, replace srcNodeRef with my generated DOM tree
295 postscript: function(/*Object?*/params
, /*DomNode|String*/srcNodeRef
){
297 // Kicks off widget instantiation. See create() for details.
300 this.create(params
, srcNodeRef
);
303 create: function(params
, srcNodeRef
){
305 // Kick off the life-cycle of a widget
307 // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
308 // etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html
309 // for a discussion of the widget creation lifecycle.
311 // Of course, adventurous developers could override create entirely, but this should
312 // only be done as a last resort.
313 // params: Object|null
314 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
315 // and functions, typically callbacks like onClick.
316 // The hash can contain any of the widget's properties, excluding read-only properties.
317 // srcNodeRef: DOMNode|String?
318 // If a srcNodeRef (DOM node) is specified:
320 // - use srcNodeRef.innerHTML as my contents
321 // - if this is a behavioral widget then apply behavior to that srcNodeRef
322 // - otherwise, replace srcNodeRef with my generated DOM tree
326 // store pointer to original DOM tree
327 this.srcNodeRef
= dom
.byId(srcNodeRef
);
329 // No longer used, remove for 2.0.
331 this._supportingWidgets
= [];
333 // this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test)
334 if(this.srcNodeRef
&& (typeof this.srcNodeRef
.id
== "string")){ this.id
= this.srcNodeRef
.id
; }
336 // mix in our passed parameters
338 this.params
= params
;
339 lang
.mixin(this, params
);
341 this.postMixInProperties();
343 // Generate an id for the widget if one wasn't specified, or it was specified as id: undefined.
344 // Do this before buildRendering() because it might expect the id to be there.
346 this.id
= registry
.getUniqueId(this.declaredClass
.replace(/\./g,"_"));
348 // if params contains {id: undefined}, prevent _applyAttributes() from processing it
349 delete this.params
.id
;
353 // The document and <body> node this widget is associated with
354 this.ownerDocument
= this.ownerDocument
|| (this.srcNodeRef
? this.srcNodeRef
.ownerDocument
: win
.doc
);
355 this.ownerDocumentBody
= win
.body(this.ownerDocument
);
359 this.buildRendering();
361 var deleteSrcNodeRef
;
364 // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
365 // Also calls custom setters for all attributes with custom setters.
366 this._applyAttributes();
368 // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
369 // For 2.0, move this after postCreate(). postCreate() shouldn't depend on the
370 // widget being attached to the DOM since it isn't when a widget is created programmatically like
371 // new MyWidget({}). See #11635.
372 var source
= this.srcNodeRef
;
373 if(source
&& source
.parentNode
&& this.domNode
!== source
){
374 source
.parentNode
.replaceChild(this.domNode
, source
);
375 deleteSrcNodeRef
= true;
378 // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
379 // assuming that dojo._scopeName even exists in 2.0
380 this.domNode
.setAttribute("widgetId", this.id
);
384 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
385 // I think for back-compatibility it isn't deleting srcNodeRef until after postCreate() has run.
386 if(deleteSrcNodeRef
){
387 delete this.srcNodeRef
;
390 this._created
= true;
393 _applyAttributes: function(){
395 // Step during widget creation to copy widget attributes to the
396 // DOM according to attributeMap and _setXXXAttr objects, and also to call
397 // custom _setXXXAttr() methods.
399 // Skips over blank/false attribute values, unless they were explicitly specified
400 // as parameters to the widget, since those are the default anyway,
401 // and setting tabIndex="" is different than not setting tabIndex at all.
403 // For backwards-compatibility reasons attributeMap overrides _setXXXAttr when
404 // _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap.
408 // Get list of attributes where this.set(name, value) will do something beyond
409 // setting this[name] = value. Specifically, attributes that have:
410 // - associated _setXXXAttr() method/hash/string/array
411 // - entries in attributeMap (remove this for 2.0);
412 var ctor
= this.constructor,
413 list
= ctor
._setterAttrs
;
415 list
= (ctor
._setterAttrs
= []);
416 for(var attr
in this.attributeMap
){
420 var proto
= ctor
.prototype;
421 for(var fxName
in proto
){
422 if(fxName
in this.attributeMap
){ continue; }
423 var setterName
= "_set" + fxName
.replace(/^[a-z]|-[a-zA-Z]/g, function(c
){ return c
.charAt(c
.length
-1).toUpperCase(); }) + "Attr";
424 if(setterName
in proto
){
430 // Call this.set() for each property that was either specified as parameter to constructor,
431 // or is in the list found above. For correlated properties like value and displayedValue, the one
432 // specified as a parameter should take precedence.
433 // Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is
434 // NaN and thus is not ignored like a default value of "".
436 // Step 1: Save the current values of the widget properties that were specified as parameters to the constructor.
437 // Generally this.foo == this.params.foo, except if postMixInProperties() changed the value of this.foo.
439 for(var key
in this.params
|| {}){
440 params
[key
] = this[key
];
443 // Step 2: Call set() for each property that wasn't passed as a parameter to the constructor
444 array
.forEach(list
, function(attr
){
446 // skip this one, do it below
447 }else if(this[attr
]){
448 this.set(attr
, this[attr
]);
452 // Step 3: Call set() for each property that was specified as parameter to constructor.
453 // Use params hash created above to ignore side effects from step #2 above.
455 this.set(key
, params
[key
]);
459 postMixInProperties: function(){
461 // Called after the parameters to the widget have been read-in,
462 // but before the widget template is instantiated. Especially
463 // useful to set properties that are referenced in the widget
469 buildRendering: function(){
471 // Construct the UI for this widget, setting this.domNode.
472 // Most widgets will mixin `dijit._TemplatedMixin`, which implements this method.
477 // Create root node if it wasn't created by _Templated
478 this.domNode
= this.srcNodeRef
|| this.ownerDocument
.createElement("div");
481 // baseClass is a single class name or occasionally a space-separated list of names.
482 // Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix.
483 // TODO: make baseClass custom setter
485 var classes
= this.baseClass
.split(" ");
486 if(!this.isLeftToRight()){
487 classes
= classes
.concat( array
.map(classes
, function(name
){ return name
+"Rtl"; }));
489 domClass
.add(this.domNode
, classes
);
493 postCreate: function(){
495 // Processing after the DOM fragment is created
497 // Called after the DOM fragment has been created, but not necessarily
498 // added to the document. Do not include any operations which rely on
499 // node dimensions or placement.
506 // Processing after the DOM fragment is added to the document
508 // Called after a widget and its children have been created and added to the page,
509 // and all related widgets have finished their create() cycle, up through postCreate().
510 // This is useful for composite widgets that need to control or layout sub-widgets.
511 // Many layout widgets can use this as a wiring phase.
512 if(this._started
){ return; }
513 this._started
= true;
514 array
.forEach(this.getChildren(), function(obj
){
515 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
522 //////////// DESTROY FUNCTIONS ////////////////////////////////
524 destroyRecursive: function(/*Boolean?*/ preserveDom
){
526 // Destroy this widget and its descendants
528 // This is the generic "destructor" function that all widget users
529 // should call to cleanly discard with a widget. Once a widget is
530 // destroyed, it is removed from the manager object.
532 // If true, this method will leave the original DOM structure
533 // alone of descendant Widgets. Note: This will NOT work with
534 // dijit._Templated widgets.
536 this._beingDestroyed
= true;
537 this.destroyDescendants(preserveDom
);
538 this.destroy(preserveDom
);
541 destroy: function(/*Boolean*/ preserveDom
){
543 // Destroy this widget, but not its descendants.
544 // This method will, however, destroy internal widgets such as those used within a template.
545 // preserveDom: Boolean
546 // If true, this method will leave the original DOM structure alone.
547 // Note: This will not yet work with _Templated widgets
549 this._beingDestroyed
= true;
553 if(w
.destroyRecursive
){
554 w
.destroyRecursive(preserveDom
);
556 w
.destroy(preserveDom
);
560 // Back-compat, remove for 2.0
561 array
.forEach(this._connects
, lang
.hitch(this, "disconnect"));
562 array
.forEach(this._supportingWidgets
, destroy
);
564 // Destroy supporting widgets, but not child widgets under this.containerNode (for 2.0, destroy child widgets
565 // here too). if() statement is to guard against exception if destroy() called multiple times (see #15815).
567 array
.forEach(registry
.findWidgets(this.domNode
, this.containerNode
), destroy
);
570 this.destroyRendering(preserveDom
);
571 registry
.remove(this.id
);
572 this._destroyed
= true;
575 destroyRendering: function(/*Boolean?*/ preserveDom
){
577 // Destroys the DOM nodes associated with this widget
579 // If true, this method will leave the original DOM structure alone
580 // during tear-down. Note: this will not work with _Templated
586 this.bgIframe
.destroy(preserveDom
);
587 delete this.bgIframe
;
592 domAttr
.remove(this.domNode
, "widgetId");
594 domConstruct
.destroy(this.domNode
);
601 domConstruct
.destroy(this.srcNodeRef
);
603 delete this.srcNodeRef
;
607 destroyDescendants: function(/*Boolean?*/ preserveDom
){
609 // Recursively destroy the children of this widget and their
612 // If true, the preserveDom attribute is passed to all descendant
613 // widget's .destroy() method. Not for use with _Templated
616 // get all direct descendants and destroy them recursively
617 array
.forEach(this.getChildren(), function(widget
){
618 if(widget
.destroyRecursive
){
619 widget
.destroyRecursive(preserveDom
);
624 uninitialize: function(){
626 // Deprecated. Override destroy() instead to implement custom widget tear-down
633 ////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
635 _setStyleAttr: function(/*String||Object*/ value
){
637 // Sets the style attribute of the widget according to value,
638 // which is either a hash like {height: "5px", width: "3px"}
641 // Determines which node to set the style on based on style setting
646 var mapNode
= this.domNode
;
648 // Note: technically we should revert any style setting made in a previous call
649 // to his method, but that's difficult to keep track of.
651 if(lang
.isObject(value
)){
652 domStyle
.set(mapNode
, value
);
654 if(mapNode
.style
.cssText
){
655 mapNode
.style
.cssText
+= "; " + value
;
657 mapNode
.style
.cssText
= value
;
661 this._set("style", value
);
664 _attrToDom: function(/*String*/ attr
, /*String*/ value
, /*Object?*/ commands
){
666 // Reflect a widget attribute (title, tabIndex, duration etc.) to
667 // the widget DOM, as specified by commands parameter.
668 // If commands isn't specified then it's looked up from attributeMap.
669 // Note some attributes like "type"
670 // cannot be processed this way as they are not mutable.
672 // Name of member variable (ex: "focusNode" maps to this.focusNode) pointing
673 // to DOMNode inside the widget, or alternately pointing to a subwidget
677 commands
= arguments
.length
>= 3 ? commands
: this.attributeMap
[attr
];
679 array
.forEach(lang
.isArray(commands
) ? commands
: [commands
], function(command
){
681 // Get target node and what we are doing to that node
682 var mapNode
= this[command
.node
|| command
|| "domNode"]; // DOM node
683 var type
= command
.type
|| "attribute"; // class, innerHTML, innerText, or attribute
687 if(lang
.isFunction(value
)){ // functions execute in the context of the widget
688 value
= lang
.hitch(this, value
);
691 // Get the name of the DOM node attribute; usually it's the same
692 // as the name of the attribute in the widget (attr), but can be overridden.
693 // Also maps handler names to lowercase, like onSubmit --> onsubmit
694 var attrName
= command
.attribute
? command
.attribute
:
695 (/^on[A-Z][a-zA-Z]*$/.test(attr
) ? attr
.toLowerCase() : attr
);
698 // Normal case, mapping to a DOMNode. Note that modern browsers will have a mapNode.set()
699 // method, but for consistency we still call domAttr
700 domAttr
.set(mapNode
, attrName
, value
);
702 // mapping to a sub-widget
703 mapNode
.set(attrName
, value
);
707 mapNode
.innerHTML
= "";
708 mapNode
.appendChild(this.ownerDocument
.createTextNode(value
));
711 mapNode
.innerHTML
= value
;
714 domClass
.replace(mapNode
, value
, this[attr
]);
722 // Get a property from a widget.
724 // The property to get.
726 // Get a named property from a widget. The property may
727 // potentially be retrieved via a getter method. If no getter is defined, this
728 // just retrieves the object's property.
730 // For example, if the widget has properties `foo` and `bar`
731 // and a method named `_getFooAttr()`, calling:
732 // `myWidget.get("foo")` would be equivalent to calling
733 // `widget._getFooAttr()` and `myWidget.get("bar")`
734 // would be equivalent to the expression
736 var names
= this._getAttrNames(name
);
737 return this[names
.g
] ? this[names
.g
]() : this[name
];
740 set: function(name
, value
){
742 // Set a property on a widget
744 // The property to set.
746 // The value to set in the property.
748 // Sets named properties on a widget which may potentially be handled by a
749 // setter in the widget.
751 // For example, if the widget has properties `foo` and `bar`
752 // and a method named `_setFooAttr()`, calling
753 // `myWidget.set("foo", "Howdy!")` would be equivalent to calling
754 // `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)`
755 // would be equivalent to the statement `widget.bar = 3;`
757 // set() may also be called with a hash of name/value pairs, ex:
764 // This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)`
766 if(typeof name
=== "object"){
768 this.set(x
, name
[x
]);
772 var names
= this._getAttrNames(name
),
773 setter
= this[names
.s
];
774 if(lang
.isFunction(setter
)){
775 // use the explicit setter
776 var result
= setter
.apply(this, Array
.prototype.slice
.call(arguments
, 1));
778 // Mapping from widget attribute to DOMNode/subwidget attribute/value/etc.
780 // 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0)
781 // 2. _setFooAttr: {...} type attribute in the widget (if one exists)
782 // 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick.
783 // Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar
784 // attribute name (ex: accept-charset attribute matches jsObject.acceptCharset).
785 // Note also that Tree.focusNode() is a function not a DOMNode, so test for that.
786 var defaultNode
= this.focusNode
&& !lang
.isFunction(this.focusNode
) ? "focusNode" : "domNode",
787 tag
= this[defaultNode
].tagName
,
788 attrsForTag
= tagAttrs
[tag
] || (tagAttrs
[tag
] = getAttrs(this[defaultNode
])),
789 map
= name
in this.attributeMap
? this.attributeMap
[name
] :
790 names
.s
in this ? this[names
.s
] :
791 ((names
.l
in attrsForTag
&& typeof value
!= "function") ||
792 /^aria-|^data-|^role$/.test(name
)) ? defaultNode
: null;
794 this._attrToDom(name
, value
, map
);
796 this._set(name
, value
);
798 return result
|| this;
801 _attrPairNames
: {}, // shared between all widgets
802 _getAttrNames: function(name
){
804 // Helper function for get() and set().
805 // Caches attribute name values so we don't do the string ops every time.
809 var apn
= this._attrPairNames
;
810 if(apn
[name
]){ return apn
[name
]; }
811 var uc
= name
.replace(/^[a-z]|-[a-zA-Z]/g, function(c
){ return c
.charAt(c
.length
-1).toUpperCase(); });
812 return (apn
[name
] = {
814 s
: "_set"+uc
+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr
816 l
: uc
.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset
820 _set: function(/*String*/ name
, /*anything*/ value
){
822 // Helper function to set new value for specified attribute, and call handlers
823 // registered with watch() if the value has changed.
824 var oldValue
= this[name
];
826 if(this._created
&& value
!== oldValue
){
827 if(this._watchCallbacks
){
828 this._watchCallbacks(name
, oldValue
, value
);
830 this.emit("attrmodified-" + name
, {
839 emit: function(/*String*/ type
, /*Object?*/ eventObj
, /*Array?*/ callbackArgs
){
841 // Used by widgets to signal that a synthetic event occurred, ex:
842 // | myWidget.emit("attrmodified-selectedChildWidget", {}).
844 // Emits an event on this.domNode named type.toLowerCase(), based on eventObj.
845 // Also calls onType() method, if present, and returns value from that method.
846 // By default passes eventObj to callback, but will pass callbackArgs instead, if specified.
847 // Modifies eventObj by adding missing parameters (bubbles, cancelable, widget).
851 // Specify fallback values for bubbles, cancelable in case they are not set in eventObj.
852 // Also set pointer to widget, although since we can't add a pointer to the widget for native events
853 // (see #14729), maybe we shouldn't do it here?
854 eventObj
= eventObj
|| {};
855 if(eventObj
.bubbles
=== undefined){ eventObj
.bubbles
= true; }
856 if(eventObj
.cancelable
=== undefined){ eventObj
.cancelable
= true; }
857 if(!eventObj
.detail
){ eventObj
.detail
= {}; }
858 eventObj
.detail
.widget
= this;
860 var ret
, callback
= this["on"+type
];
862 ret
= callback
.apply(this, callbackArgs
? callbackArgs
: [eventObj
]);
865 // Emit event, but avoid spurious emit()'s as parent sets properties on child during startup/destroy
866 if(this._started
&& !this._beingDestroyed
){
867 on
.emit(this.domNode
, type
.toLowerCase(), eventObj
);
873 on: function(/*String|Function*/ type
, /*Function*/ func
){
875 // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }).
877 // Name of event (ex: "click") or extension event like touch.press.
879 // Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`.
880 // Note that the function is not run in any particular scope, so if (for example) you want it to run in the
881 // widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`.
883 // For backwards compatibility, if there's an onType() method in the widget then connect to that.
885 var widgetMethod
= this._onMap(type
);
887 return aspect
.after(this, widgetMethod
, func
, true);
890 // Otherwise, just listen for the event on this.domNode.
891 return this.own(on(this.domNode
, type
, func
))[0];
894 _onMap: function(/*String|Function*/ type
){
896 // Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove").
897 // If type is a synthetic event like touch.press then returns undefined.
898 var ctor
= this.constructor, map
= ctor
._onMap
;
900 map
= (ctor
._onMap
= {});
901 for(var attr
in ctor
.prototype){
902 if(/^on/.test(attr
)){
903 map
[attr
.replace(/^on/, "").toLowerCase()] = attr
;
907 return map
[typeof type
== "string" && type
.toLowerCase()]; // String
910 toString: function(){
912 // Returns a string that represents the widget
914 // When a widget is cast to a string, this method will be used to generate the
915 // output. Currently, it does not implement any sort of reversible
917 return '[Widget ' + this.declaredClass
+ ', ' + (this.id
|| 'NO ID') + ']'; // String
920 getChildren: function(){
922 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
923 // Does not return nested widgets, nor widgets that are part of this widget's template.
924 return this.containerNode
? registry
.findWidgets(this.containerNode
) : []; // dijit/_WidgetBase[]
927 getParent: function(){
929 // Returns the parent widget of this widget
930 return registry
.getEnclosingWidget(this.domNode
.parentNode
);
935 /*String|Function*/ event
,
936 /*String|Function*/ method
){
938 // Deprecated, will be removed in 2.0, use this.own(on(...)) or this.own(aspect.after(...)) instead.
940 // Connects specified obj/event to specified method of this object
941 // and registers for disconnect() on widget destroy.
943 // Provide widget-specific analog to dojo.connect, except with the
944 // implicit use of this widget as the target object.
945 // Events connected with `this.connect` are disconnected upon
948 // A handle that can be passed to `disconnect` in order to disconnect before
949 // the widget is destroyed.
951 // | var btn = new Button();
952 // | // when foo.bar() is called, call the listener we're going to
953 // | // provide in the scope of btn
954 // | btn.connect(foo, "bar", function(){
955 // | console.debug(this.toString());
960 return this.own(connect
.connect(obj
, event
, this, method
))[0]; // handle
963 disconnect: function(handle
){
965 // Deprecated, will be removed in 2.0, use handle.remove() instead.
967 // Disconnects handle created by `connect`.
974 subscribe: function(t
, method
){
976 // Deprecated, will be removed in 2.0, use this.own(topic.subscribe()) instead.
978 // Subscribes to the specified topic and calls the specified method
979 // of this object and registers for unsubscribe() on widget destroy.
981 // Provide widget-specific analog to dojo.subscribe, except with the
982 // implicit use of this widget as the target object.
988 // | var btn = new Button();
989 // | // when /my/topic is published, this button changes its label to
990 // | // be the parameter of the topic.
991 // | btn.subscribe("/my/topic", function(v){
992 // | this.set("label", v);
996 return this.own(topic
.subscribe(t
, lang
.hitch(this, method
)))[0]; // handle
999 unsubscribe: function(/*Object*/ handle
){
1001 // Deprecated, will be removed in 2.0, use handle.remove() instead.
1003 // Unsubscribes handle created by this.subscribe.
1004 // Also removes handle from this widget's list of subscriptions
1011 isLeftToRight: function(){
1013 // Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
1016 return this.dir
? (this.dir
== "ltr") : domGeometry
.isBodyLtr(this.ownerDocument
); //Boolean
1019 isFocusable: function(){
1021 // Return true if this widget can currently be focused
1023 return this.focus
&& (domStyle
.get(this.domNode
, "display") != "none");
1026 placeAt: function(/* String|DomNode|_Widget */ reference
, /* String|Int? */ position
){
1028 // Place this widget somewhere in the DOM based
1029 // on standard domConstruct.place() conventions.
1031 // A convenience function provided in all _Widgets, providing a simple
1032 // shorthand mechanism to put an existing (or newly created) Widget
1033 // somewhere in the dom, and allow chaining.
1035 // Widget, DOMNode, or id of widget or DOMNode
1037 // If reference is a widget (or id of widget), and that widget has an ".addChild" method,
1038 // it will be called passing this widget instance into that method, supplying the optional
1039 // position index passed. In this case position (if specified) should be an integer.
1041 // If reference is a DOMNode (or id matching a DOMNode but not a widget),
1042 // the position argument can be a numeric index or a string
1043 // "first", "last", "before", or "after", same as dojo/dom-construct::place().
1044 // returns: dijit/_WidgetBase
1045 // Provides a useful return of the newly created dijit._Widget instance so you
1046 // can "chain" this function by instantiating, placing, then saving the return value
1049 // | // create a Button with no srcNodeRef, and place it in the body:
1050 // | var button = new Button({ label:"click" }).placeAt(win.body());
1051 // | // now, 'button' is still the widget reference to the newly created button
1052 // | button.on("click", function(e){ console.log('click'); }));
1054 // | // create a button out of a node with id="src" and append it to id="wrapper":
1055 // | var button = new Button({},"src").placeAt("wrapper");
1057 // | // place a new button as the first element of some div
1058 // | var button = new Button({ label:"click" }).placeAt("wrapper","first");
1060 // | // create a contentpane and add it to a TabContainer
1061 // | var tc = dijit.byId("myTabs");
1062 // | new ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
1064 var refWidget
= !reference
.tagName
&& registry
.byId(reference
);
1065 if(refWidget
&& refWidget
.addChild
&& (!position
|| typeof position
=== "number")){
1066 // Adding this to refWidget and can use refWidget.addChild() to handle everything.
1067 refWidget
.addChild(this, position
);
1069 // "reference" is a plain DOMNode, or we can't use refWidget.addChild(). Use domConstruct.place() and
1070 // target refWidget.containerNode for nested placement (position==number, "first", "last", "only"), and
1071 // refWidget.domNode otherwise ("after"/"before"/"replace"). (But not supported officially, see #14946.)
1072 var ref
= refWidget
?
1073 (refWidget
.containerNode
&& !/after|before|replace/.test(position
||"") ?
1074 refWidget
.containerNode
: refWidget
.domNode
) : dom
.byId(reference
, this.ownerDocument
);
1075 domConstruct
.place(this.domNode
, ref
, position
);
1077 // Start this iff it has a parent widget that's already started.
1078 if(!this._started
&& (this.getParent() || {})._started
){
1085 getTextDir: function(/*String*/ text
,/*String*/ originalDir
){
1087 // Return direction of the text.
1088 // The function overridden in the _BidiSupport module,
1089 // its main purpose is to calculate the direction of the
1090 // text, if was defined by the programmer through textDir.
1096 applyTextDir: function(/*===== element, text =====*/){
1098 // The function overridden in the _BidiSupport module,
1099 // originally used for setting element.dir according to this.textDir.
1100 // In this case does nothing.
1107 defer: function(fcn
, delay
){
1109 // Wrapper to setTimeout to avoid deferred functions executing
1110 // after the originating widget has been destroyed.
1111 // Returns an object handle with a remove method (that returns null) (replaces clearTimeout).
1112 // fcn: function reference
1113 // delay: Optional number (defaults to 0)
1116 var timer
= setTimeout(lang
.hitch(this,
1119 if(!this._destroyed
){
1120 lang
.hitch(this, fcn
)();
1128 clearTimeout(timer
);
1131 return null; // so this works well: handle = handle.remove();