]>
Commit | Line | Data |
---|---|---|
f0cfe83e AD |
1 | define("dijit/_WidgetBase", [ |
2 | "require", // require.toUrl | |
3 | "dojo/_base/array", // array.forEach array.map | |
4 | "dojo/aspect", | |
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 | |
14 | "dojo/has", | |
15 | "dojo/_base/kernel", | |
16 | "dojo/_base/lang", // mixin(), isArray(), etc. | |
17 | "dojo/on", | |
18 | "dojo/ready", | |
19 | "dojo/Stateful", // Stateful | |
20 | "dojo/topic", | |
21 | "dojo/_base/window", // win.doc, win.body() | |
22 | "./Destroyable", | |
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){ | |
27 | ||
28 | // module: | |
29 | // dijit/_WidgetBase | |
30 | ||
31 | // Flag to make dijit load modules the app didn't explicitly request, for backwards compatibility | |
32 | has.add("dijit-legacy-requires", !kernel.isAsync); | |
33 | ||
34 | // For back-compat, remove in 2.0. | |
35 | if(has("dijit-legacy-requires")){ | |
36 | ready(0, function(){ | |
37 | var requires = ["dijit/_base/manager"]; | |
38 | require(requires); // use indirection so modules not rolled into a build | |
39 | }); | |
40 | } | |
41 | ||
42 | // Nested hash listing attributes for each tag, all strings in lowercase. | |
43 | // ex: {"div": {"style": true, "tabindex" true}, "form": { ... | |
44 | var tagAttrs = {}; | |
45 | function getAttrs(obj){ | |
46 | var ret = {}; | |
47 | for(var attr in obj){ | |
48 | ret[attr.toLowerCase()] = true; | |
49 | } | |
50 | return ret; | |
51 | } | |
52 | ||
53 | function nonEmptyAttrToDom(attr){ | |
54 | // summary: | |
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. | |
58 | return function(val){ | |
59 | domAttr[val ? "set" : "remove"](this.domNode, attr, val); | |
60 | this._set(attr, val); | |
61 | }; | |
62 | } | |
63 | ||
64 | return declare("dijit._WidgetBase", [Stateful, Destroyable], { | |
65 | // summary: | |
66 | // Future base class for all Dijit widgets. | |
67 | // description: | |
68 | // Future base class for all Dijit widgets. | |
69 | // _Widget extends this class adding support for various features needed by desktop. | |
70 | // | |
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(). | |
73 | // | |
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(). | |
76 | // | |
77 | // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes: | |
78 | // | |
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 | |
84 | // | |
85 | // - DOM node innerHTML | |
86 | // | _setTitleAttr: { node: "titleNode", type: "innerHTML" } | |
87 | // Maps this.title to this.titleNode.innerHTML | |
88 | // | |
89 | // - DOM node innerText | |
90 | // | _setTitleAttr: { node: "titleNode", type: "innerText" } | |
91 | // Maps this.title to this.titleNode.innerText | |
92 | // | |
93 | // - DOM node CSS class | |
94 | // | _setMyClassAttr: { node: "domNode", type: "class" } | |
95 | // Maps this.myClass to this.domNode.className | |
96 | // | |
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. | |
99 | // | |
100 | // If the custom setter is null, no action is performed other than saving the new value | |
101 | // in the widget (in this). | |
102 | // | |
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.) | |
107 | ||
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 | |
112 | // used instead. | |
113 | id: "", | |
114 | _setIdAttr: "domNode", // to copy to this.domNode even for auto-generated id's | |
115 | ||
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). | |
121 | lang: "", | |
122 | // set on domNode even when there's a focus node. but don't set lang="", since that's invalid. | |
123 | _setLangAttr: nonEmptyAttrToDom("lang"), | |
124 | ||
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. | |
129 | dir: "", | |
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 | |
132 | ||
133 | // textDir: String | |
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 | |
136 | // of a widget. | |
137 | // | |
138 | // Allowed values: | |
139 | // | |
140 | // 1. "ltr" | |
141 | // 2. "rtl" | |
142 | // 3. "auto" - contextual the direction of a text defined by first strong letter. | |
143 | // | |
144 | // By default is as the page direction. | |
145 | textDir: "", | |
146 | ||
147 | // class: String | |
148 | // HTML class attribute | |
149 | "class": "", | |
150 | _setClassAttr: { node: "domNode", type: "class" }, | |
151 | ||
152 | // style: String||Object | |
153 | // HTML style attributes as cssText string or name/value hash | |
154 | style: "", | |
155 | ||
156 | // title: String | |
157 | // HTML title attribute. | |
158 | // | |
159 | // For form widgets this specifies a tooltip to display when hovering over | |
160 | // the widget (just like the native HTML title attribute). | |
161 | // | |
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. | |
164 | title: "", | |
165 | ||
166 | // tooltip: String | |
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. | |
169 | tooltip: "", | |
170 | ||
171 | // baseClass: [protected] String | |
172 | // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate | |
173 | // widget state. | |
174 | baseClass: "", | |
175 | ||
176 | // srcNodeRef: [readonly] DomNode | |
177 | // pointer to original DOM node | |
178 | srcNodeRef: null, | |
179 | ||
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. | |
185 | domNode: null, | |
186 | ||
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: | |
191 | // | |
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> | |
196 | // | </div> | |
197 | // | |
198 | // containerNode would point to: | |
199 | // | |
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> | |
203 | // | |
204 | // In templated widgets, "containerNode" is set via a | |
205 | // data-dojo-attach-point assignment. | |
206 | // | |
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. | |
210 | containerNode: null, | |
211 | ||
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 | |
215 | ownerDocument: null, | |
216 | _setOwnerDocumentAttr: function(val){ | |
217 | // this setter is merely to avoid automatically trying to set this.domNode.ownerDocument | |
218 | this._set("ownerDocument", val); | |
219 | }, | |
220 | ||
221 | /*===== | |
222 | // _started: [readonly] Boolean | |
223 | // startup() has completed. | |
224 | _started: false, | |
225 | =====*/ | |
226 | ||
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. | |
230 | // | |
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. | |
235 | // | |
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. | |
239 | // | |
240 | // attributeMap is a hash where the key is an attribute of the widget, | |
241 | // and the value reflects a binding to a: | |
242 | // | |
243 | // - DOM node attribute | |
244 | // | focus: {node: "focusNode", type: "attribute"} | |
245 | // Maps this.focus to this.focusNode.focus | |
246 | // | |
247 | // - DOM node innerHTML | |
248 | // | title: { node: "titleNode", type: "innerHTML" } | |
249 | // Maps this.title to this.titleNode.innerHTML | |
250 | // | |
251 | // - DOM node innerText | |
252 | // | title: { node: "titleNode", type: "innerText" } | |
253 | // Maps this.title to this.titleNode.innerText | |
254 | // | |
255 | // - DOM node CSS class | |
256 | // | myClass: { node: "domNode", type: "class" } | |
257 | // Maps this.myClass to this.domNode.className | |
258 | // | |
259 | // If the value is an array, then each element in the array matches one of the | |
260 | // formats of the above list. | |
261 | // | |
262 | // There are also some shorthands for backwards compatibility: | |
263 | // | |
264 | // - string --> { node: string, type: "attribute" }, for example: | |
265 | // | |
266 | // | "focusNode" ---> { node: "focusNode", type: "attribute" } | |
267 | // | |
268 | // - "" --> { node: "domNode", type: "attribute" } | |
269 | attributeMap: {}, | |
270 | ||
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"), | |
275 | ||
276 | //////////// INITIALIZATION METHODS /////////////////////////////////////// | |
277 | ||
278 | /*===== | |
279 | constructor: function(params, srcNodeRef){ | |
280 | // summary: | |
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: | |
288 | // | |
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 | |
292 | }, | |
293 | =====*/ | |
294 | ||
295 | postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){ | |
296 | // summary: | |
297 | // Kicks off widget instantiation. See create() for details. | |
298 | // tags: | |
299 | // private | |
300 | this.create(params, srcNodeRef); | |
301 | }, | |
302 | ||
303 | create: function(params, srcNodeRef){ | |
304 | // summary: | |
305 | // Kick off the life-cycle of a widget | |
306 | // description: | |
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. | |
310 | // | |
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: | |
319 | // | |
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 | |
323 | // tags: | |
324 | // private | |
325 | ||
326 | // store pointer to original DOM tree | |
327 | this.srcNodeRef = dom.byId(srcNodeRef); | |
328 | ||
329 | // No longer used, remove for 2.0. | |
330 | this._connects = []; | |
331 | this._supportingWidgets = []; | |
332 | ||
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; } | |
335 | ||
336 | // mix in our passed parameters | |
337 | if(params){ | |
338 | this.params = params; | |
339 | lang.mixin(this, params); | |
340 | } | |
341 | this.postMixInProperties(); | |
342 | ||
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. | |
345 | if(!this.id){ | |
346 | this.id = registry.getUniqueId(this.declaredClass.replace(/\./g,"_")); | |
347 | if(this.params){ | |
348 | // if params contains {id: undefined}, prevent _applyAttributes() from processing it | |
349 | delete this.params.id; | |
350 | } | |
351 | } | |
352 | ||
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); | |
356 | ||
357 | registry.add(this); | |
358 | ||
359 | this.buildRendering(); | |
360 | ||
361 | var deleteSrcNodeRef; | |
362 | ||
363 | if(this.domNode){ | |
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(); | |
367 | ||
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; | |
376 | } | |
377 | ||
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); | |
381 | } | |
382 | this.postCreate(); | |
383 | ||
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; | |
388 | } | |
389 | ||
390 | this._created = true; | |
391 | }, | |
392 | ||
393 | _applyAttributes: function(){ | |
394 | // summary: | |
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. | |
398 | // | |
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. | |
402 | // | |
403 | // For backwards-compatibility reasons attributeMap overrides _setXXXAttr when | |
404 | // _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap. | |
405 | // tags: | |
406 | // private | |
407 | ||
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; | |
414 | if(!list){ | |
415 | list = (ctor._setterAttrs = []); | |
416 | for(var attr in this.attributeMap){ | |
417 | list.push(attr); | |
418 | } | |
419 | ||
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){ | |
425 | list.push(fxName); | |
426 | } | |
427 | } | |
428 | } | |
429 | ||
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 "". | |
435 | ||
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. | |
438 | var params = {}; | |
439 | for(var key in this.params || {}){ | |
440 | params[key] = this[key]; | |
441 | } | |
442 | ||
443 | // Step 2: Call set() for each property that wasn't passed as a parameter to the constructor | |
444 | array.forEach(list, function(attr){ | |
445 | if(attr in params){ | |
446 | // skip this one, do it below | |
447 | }else if(this[attr]){ | |
448 | this.set(attr, this[attr]); | |
449 | } | |
450 | }, this); | |
451 | ||
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. | |
454 | for(key in params){ | |
455 | this.set(key, params[key]); | |
456 | } | |
457 | }, | |
458 | ||
459 | postMixInProperties: function(){ | |
460 | // summary: | |
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 | |
464 | // template. | |
465 | // tags: | |
466 | // protected | |
467 | }, | |
468 | ||
469 | buildRendering: function(){ | |
470 | // summary: | |
471 | // Construct the UI for this widget, setting this.domNode. | |
472 | // Most widgets will mixin `dijit._TemplatedMixin`, which implements this method. | |
473 | // tags: | |
474 | // protected | |
475 | ||
476 | if(!this.domNode){ | |
477 | // Create root node if it wasn't created by _Templated | |
478 | this.domNode = this.srcNodeRef || this.ownerDocument.createElement("div"); | |
479 | } | |
480 | ||
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 | |
484 | if(this.baseClass){ | |
485 | var classes = this.baseClass.split(" "); | |
486 | if(!this.isLeftToRight()){ | |
487 | classes = classes.concat( array.map(classes, function(name){ return name+"Rtl"; })); | |
488 | } | |
489 | domClass.add(this.domNode, classes); | |
490 | } | |
491 | }, | |
492 | ||
493 | postCreate: function(){ | |
494 | // summary: | |
495 | // Processing after the DOM fragment is created | |
496 | // description: | |
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. | |
500 | // tags: | |
501 | // protected | |
502 | }, | |
503 | ||
504 | startup: function(){ | |
505 | // summary: | |
506 | // Processing after the DOM fragment is added to the document | |
507 | // description: | |
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)){ | |
516 | obj.startup(); | |
517 | obj._started = true; | |
518 | } | |
519 | }); | |
520 | }, | |
521 | ||
522 | //////////// DESTROY FUNCTIONS //////////////////////////////// | |
523 | ||
524 | destroyRecursive: function(/*Boolean?*/ preserveDom){ | |
525 | // summary: | |
526 | // Destroy this widget and its descendants | |
527 | // description: | |
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. | |
531 | // preserveDom: | |
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. | |
535 | ||
536 | this._beingDestroyed = true; | |
537 | this.destroyDescendants(preserveDom); | |
538 | this.destroy(preserveDom); | |
539 | }, | |
540 | ||
541 | destroy: function(/*Boolean*/ preserveDom){ | |
542 | // summary: | |
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 | |
548 | ||
549 | this._beingDestroyed = true; | |
550 | this.uninitialize(); | |
551 | ||
552 | function destroy(w){ | |
553 | if(w.destroyRecursive){ | |
554 | w.destroyRecursive(preserveDom); | |
555 | }else if(w.destroy){ | |
556 | w.destroy(preserveDom); | |
557 | } | |
558 | } | |
559 | ||
560 | // Back-compat, remove for 2.0 | |
561 | array.forEach(this._connects, lang.hitch(this, "disconnect")); | |
562 | array.forEach(this._supportingWidgets, destroy); | |
563 | ||
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). | |
566 | if(this.domNode){ | |
567 | array.forEach(registry.findWidgets(this.domNode, this.containerNode), destroy); | |
568 | } | |
569 | ||
570 | this.destroyRendering(preserveDom); | |
571 | registry.remove(this.id); | |
572 | this._destroyed = true; | |
573 | }, | |
574 | ||
575 | destroyRendering: function(/*Boolean?*/ preserveDom){ | |
576 | // summary: | |
577 | // Destroys the DOM nodes associated with this widget | |
578 | // preserveDom: | |
579 | // If true, this method will leave the original DOM structure alone | |
580 | // during tear-down. Note: this will not work with _Templated | |
581 | // widgets yet. | |
582 | // tags: | |
583 | // protected | |
584 | ||
585 | if(this.bgIframe){ | |
586 | this.bgIframe.destroy(preserveDom); | |
587 | delete this.bgIframe; | |
588 | } | |
589 | ||
590 | if(this.domNode){ | |
591 | if(preserveDom){ | |
592 | domAttr.remove(this.domNode, "widgetId"); | |
593 | }else{ | |
594 | domConstruct.destroy(this.domNode); | |
595 | } | |
596 | delete this.domNode; | |
597 | } | |
598 | ||
599 | if(this.srcNodeRef){ | |
600 | if(!preserveDom){ | |
601 | domConstruct.destroy(this.srcNodeRef); | |
602 | } | |
603 | delete this.srcNodeRef; | |
604 | } | |
605 | }, | |
606 | ||
607 | destroyDescendants: function(/*Boolean?*/ preserveDom){ | |
608 | // summary: | |
609 | // Recursively destroy the children of this widget and their | |
610 | // descendants. | |
611 | // preserveDom: | |
612 | // If true, the preserveDom attribute is passed to all descendant | |
613 | // widget's .destroy() method. Not for use with _Templated | |
614 | // widgets. | |
615 | ||
616 | // get all direct descendants and destroy them recursively | |
617 | array.forEach(this.getChildren(), function(widget){ | |
618 | if(widget.destroyRecursive){ | |
619 | widget.destroyRecursive(preserveDom); | |
620 | } | |
621 | }); | |
622 | }, | |
623 | ||
624 | uninitialize: function(){ | |
625 | // summary: | |
626 | // Deprecated. Override destroy() instead to implement custom widget tear-down | |
627 | // behavior. | |
628 | // tags: | |
629 | // protected | |
630 | return false; | |
631 | }, | |
632 | ||
633 | ////////////////// GET/SET, CUSTOM SETTERS, ETC. /////////////////// | |
634 | ||
635 | _setStyleAttr: function(/*String||Object*/ value){ | |
636 | // summary: | |
637 | // Sets the style attribute of the widget according to value, | |
638 | // which is either a hash like {height: "5px", width: "3px"} | |
639 | // or a plain string | |
640 | // description: | |
641 | // Determines which node to set the style on based on style setting | |
642 | // in attributeMap. | |
643 | // tags: | |
644 | // protected | |
645 | ||
646 | var mapNode = this.domNode; | |
647 | ||
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. | |
650 | ||
651 | if(lang.isObject(value)){ | |
652 | domStyle.set(mapNode, value); | |
653 | }else{ | |
654 | if(mapNode.style.cssText){ | |
655 | mapNode.style.cssText += "; " + value; | |
656 | }else{ | |
657 | mapNode.style.cssText = value; | |
658 | } | |
659 | } | |
660 | ||
661 | this._set("style", value); | |
662 | }, | |
663 | ||
664 | _attrToDom: function(/*String*/ attr, /*String*/ value, /*Object?*/ commands){ | |
665 | // summary: | |
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. | |
671 | // attr: | |
672 | // Name of member variable (ex: "focusNode" maps to this.focusNode) pointing | |
673 | // to DOMNode inside the widget, or alternately pointing to a subwidget | |
674 | // tags: | |
675 | // private | |
676 | ||
677 | commands = arguments.length >= 3 ? commands : this.attributeMap[attr]; | |
678 | ||
679 | array.forEach(lang.isArray(commands) ? commands : [commands], function(command){ | |
680 | ||
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 | |
684 | ||
685 | switch(type){ | |
686 | case "attribute": | |
687 | if(lang.isFunction(value)){ // functions execute in the context of the widget | |
688 | value = lang.hitch(this, value); | |
689 | } | |
690 | ||
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); | |
696 | ||
697 | if(mapNode.tagName){ | |
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); | |
701 | }else{ | |
702 | // mapping to a sub-widget | |
703 | mapNode.set(attrName, value); | |
704 | } | |
705 | break; | |
706 | case "innerText": | |
707 | mapNode.innerHTML = ""; | |
708 | mapNode.appendChild(this.ownerDocument.createTextNode(value)); | |
709 | break; | |
710 | case "innerHTML": | |
711 | mapNode.innerHTML = value; | |
712 | break; | |
713 | case "class": | |
714 | domClass.replace(mapNode, value, this[attr]); | |
715 | break; | |
716 | } | |
717 | }, this); | |
718 | }, | |
719 | ||
720 | get: function(name){ | |
721 | // summary: | |
722 | // Get a property from a widget. | |
723 | // name: | |
724 | // The property to get. | |
725 | // description: | |
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. | |
729 | // | |
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 | |
735 | // `widget.bar2` | |
736 | var names = this._getAttrNames(name); | |
737 | return this[names.g] ? this[names.g]() : this[name]; | |
738 | }, | |
739 | ||
740 | set: function(name, value){ | |
741 | // summary: | |
742 | // Set a property on a widget | |
743 | // name: | |
744 | // The property to set. | |
745 | // value: | |
746 | // The value to set in the property. | |
747 | // description: | |
748 | // Sets named properties on a widget which may potentially be handled by a | |
749 | // setter in the widget. | |
750 | // | |
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;` | |
756 | // | |
757 | // set() may also be called with a hash of name/value pairs, ex: | |
758 | // | |
759 | // | myWidget.set({ | |
760 | // | foo: "Howdy", | |
761 | // | bar: 3 | |
762 | // | }); | |
763 | // | |
764 | // This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)` | |
765 | ||
766 | if(typeof name === "object"){ | |
767 | for(var x in name){ | |
768 | this.set(x, name[x]); | |
769 | } | |
770 | return this; | |
771 | } | |
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)); | |
777 | }else{ | |
778 | // Mapping from widget attribute to DOMNode/subwidget attribute/value/etc. | |
779 | // Map according to: | |
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; | |
793 | if(map != null){ | |
794 | this._attrToDom(name, value, map); | |
795 | } | |
796 | this._set(name, value); | |
797 | } | |
798 | return result || this; | |
799 | }, | |
800 | ||
801 | _attrPairNames: {}, // shared between all widgets | |
802 | _getAttrNames: function(name){ | |
803 | // summary: | |
804 | // Helper function for get() and set(). | |
805 | // Caches attribute name values so we don't do the string ops every time. | |
806 | // tags: | |
807 | // private | |
808 | ||
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] = { | |
813 | n: name+"Node", | |
814 | s: "_set"+uc+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr | |
815 | g: "_get"+uc+"Attr", | |
816 | l: uc.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset | |
817 | }); | |
818 | }, | |
819 | ||
820 | _set: function(/*String*/ name, /*anything*/ value){ | |
821 | // summary: | |
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]; | |
825 | this[name] = value; | |
826 | if(this._created && value !== oldValue){ | |
827 | if(this._watchCallbacks){ | |
828 | this._watchCallbacks(name, oldValue, value); | |
829 | } | |
830 | this.emit("attrmodified-" + name, { | |
831 | detail: { | |
832 | prevValue: oldValue, | |
833 | newValue: value | |
834 | } | |
835 | }); | |
836 | } | |
837 | }, | |
838 | ||
839 | emit: function(/*String*/ type, /*Object?*/ eventObj, /*Array?*/ callbackArgs){ | |
840 | // summary: | |
841 | // Used by widgets to signal that a synthetic event occurred, ex: | |
842 | // | myWidget.emit("attrmodified-selectedChildWidget", {}). | |
843 | // | |
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). | |
848 | // tags: | |
849 | // protected | |
850 | ||
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; | |
859 | ||
860 | var ret, callback = this["on"+type]; | |
861 | if(callback){ | |
862 | ret = callback.apply(this, callbackArgs ? callbackArgs : [eventObj]); | |
863 | } | |
864 | ||
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); | |
868 | } | |
869 | ||
870 | return ret; | |
871 | }, | |
872 | ||
873 | on: function(/*String|Function*/ type, /*Function*/ func){ | |
874 | // summary: | |
875 | // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }). | |
876 | // type: | |
877 | // Name of event (ex: "click") or extension event like touch.press. | |
878 | // description: | |
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))`. | |
882 | ||
883 | // For backwards compatibility, if there's an onType() method in the widget then connect to that. | |
884 | // Remove in 2.0. | |
885 | var widgetMethod = this._onMap(type); | |
886 | if(widgetMethod){ | |
887 | return aspect.after(this, widgetMethod, func, true); | |
888 | } | |
889 | ||
890 | // Otherwise, just listen for the event on this.domNode. | |
891 | return this.own(on(this.domNode, type, func))[0]; | |
892 | }, | |
893 | ||
894 | _onMap: function(/*String|Function*/ type){ | |
895 | // summary: | |
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; | |
899 | if(!map){ | |
900 | map = (ctor._onMap = {}); | |
901 | for(var attr in ctor.prototype){ | |
902 | if(/^on/.test(attr)){ | |
903 | map[attr.replace(/^on/, "").toLowerCase()] = attr; | |
904 | } | |
905 | } | |
906 | } | |
907 | return map[typeof type == "string" && type.toLowerCase()]; // String | |
908 | }, | |
909 | ||
910 | toString: function(){ | |
911 | // summary: | |
912 | // Returns a string that represents the widget | |
913 | // description: | |
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 | |
916 | // serialization. | |
917 | return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String | |
918 | }, | |
919 | ||
920 | getChildren: function(){ | |
921 | // summary: | |
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[] | |
925 | }, | |
926 | ||
927 | getParent: function(){ | |
928 | // summary: | |
929 | // Returns the parent widget of this widget | |
930 | return registry.getEnclosingWidget(this.domNode.parentNode); | |
931 | }, | |
932 | ||
933 | connect: function( | |
934 | /*Object|null*/ obj, | |
935 | /*String|Function*/ event, | |
936 | /*String|Function*/ method){ | |
937 | // summary: | |
938 | // Deprecated, will be removed in 2.0, use this.own(on(...)) or this.own(aspect.after(...)) instead. | |
939 | // | |
940 | // Connects specified obj/event to specified method of this object | |
941 | // and registers for disconnect() on widget destroy. | |
942 | // | |
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 | |
946 | // destruction. | |
947 | // returns: | |
948 | // A handle that can be passed to `disconnect` in order to disconnect before | |
949 | // the widget is destroyed. | |
950 | // example: | |
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()); | |
956 | // | }); | |
957 | // tags: | |
958 | // protected | |
959 | ||
960 | return this.own(connect.connect(obj, event, this, method))[0]; // handle | |
961 | }, | |
962 | ||
963 | disconnect: function(handle){ | |
964 | // summary: | |
965 | // Deprecated, will be removed in 2.0, use handle.remove() instead. | |
966 | // | |
967 | // Disconnects handle created by `connect`. | |
968 | // tags: | |
969 | // protected | |
970 | ||
971 | handle.remove(); | |
972 | }, | |
973 | ||
974 | subscribe: function(t, method){ | |
975 | // summary: | |
976 | // Deprecated, will be removed in 2.0, use this.own(topic.subscribe()) instead. | |
977 | // | |
978 | // Subscribes to the specified topic and calls the specified method | |
979 | // of this object and registers for unsubscribe() on widget destroy. | |
980 | // | |
981 | // Provide widget-specific analog to dojo.subscribe, except with the | |
982 | // implicit use of this widget as the target object. | |
983 | // t: String | |
984 | // The topic | |
985 | // method: Function | |
986 | // The callback | |
987 | // example: | |
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); | |
993 | // | }); | |
994 | // tags: | |
995 | // protected | |
996 | return this.own(topic.subscribe(t, lang.hitch(this, method)))[0]; // handle | |
997 | }, | |
998 | ||
999 | unsubscribe: function(/*Object*/ handle){ | |
1000 | // summary: | |
1001 | // Deprecated, will be removed in 2.0, use handle.remove() instead. | |
1002 | // | |
1003 | // Unsubscribes handle created by this.subscribe. | |
1004 | // Also removes handle from this widget's list of subscriptions | |
1005 | // tags: | |
1006 | // protected | |
1007 | ||
1008 | handle.remove(); | |
1009 | }, | |
1010 | ||
1011 | isLeftToRight: function(){ | |
1012 | // summary: | |
1013 | // Return this widget's explicit or implicit orientation (true for LTR, false for RTL) | |
1014 | // tags: | |
1015 | // protected | |
1016 | return this.dir ? (this.dir == "ltr") : domGeometry.isBodyLtr(this.ownerDocument); //Boolean | |
1017 | }, | |
1018 | ||
1019 | isFocusable: function(){ | |
1020 | // summary: | |
1021 | // Return true if this widget can currently be focused | |
1022 | // and false if not | |
1023 | return this.focus && (domStyle.get(this.domNode, "display") != "none"); | |
1024 | }, | |
1025 | ||
1026 | placeAt: function(/* String|DomNode|_Widget */ reference, /* String|Int? */ position){ | |
1027 | // summary: | |
1028 | // Place this widget somewhere in the DOM based | |
1029 | // on standard domConstruct.place() conventions. | |
1030 | // description: | |
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. | |
1034 | // reference: | |
1035 | // Widget, DOMNode, or id of widget or DOMNode | |
1036 | // position: | |
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. | |
1040 | // | |
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 | |
1047 | // to a variable. | |
1048 | // example: | |
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'); })); | |
1053 | // example: | |
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"); | |
1056 | // example: | |
1057 | // | // place a new button as the first element of some div | |
1058 | // | var button = new Button({ label:"click" }).placeAt("wrapper","first"); | |
1059 | // example: | |
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) | |
1063 | ||
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); | |
1068 | }else{ | |
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); | |
1076 | ||
1077 | // Start this iff it has a parent widget that's already started. | |
1078 | if(!this._started && (this.getParent() || {})._started){ | |
1079 | this.startup(); | |
1080 | } | |
1081 | } | |
1082 | return this; | |
1083 | }, | |
1084 | ||
1085 | getTextDir: function(/*String*/ text,/*String*/ originalDir){ | |
1086 | // summary: | |
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. | |
1091 | // tags: | |
1092 | // protected. | |
1093 | return originalDir; | |
1094 | }, | |
1095 | ||
1096 | applyTextDir: function(/*===== element, text =====*/){ | |
1097 | // summary: | |
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. | |
1101 | // element: DOMNode | |
1102 | // text: String | |
1103 | // tags: | |
1104 | // protected. | |
1105 | }, | |
1106 | ||
1107 | defer: function(fcn, delay){ | |
1108 | // summary: | |
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) | |
1114 | // tags: | |
1115 | // protected. | |
1116 | var timer = setTimeout(lang.hitch(this, | |
1117 | function(){ | |
1118 | timer = null; | |
1119 | if(!this._destroyed){ | |
1120 | lang.hitch(this, fcn)(); | |
1121 | } | |
1122 | }), | |
1123 | delay || 0 | |
1124 | ); | |
1125 | return { | |
1126 | remove: function(){ | |
1127 | if(timer){ | |
1128 | clearTimeout(timer); | |
1129 | timer = null; | |
1130 | } | |
1131 | return null; // so this works well: handle = handle.remove(); | |
1132 | } | |
1133 | }; | |
1134 | } | |
1135 | }); | |
1136 | ||
1137 | }); |