]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dijit/_Widget.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dijit / _Widget.js
1 if(!dojo._hasResource["dijit._Widget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dijit._Widget"] = true;
3 dojo.provide("dijit._Widget");
4
5 dojo.require( "dijit._base" );
6
7 dojo.declare("dijit._Widget", null, {
8         //      summary:
9         //              The foundation of dijit widgets.        
10         //
11         //      id: String
12         //              a unique, opaque ID string that can be assigned by users or by the
13         //              system. If the developer passes an ID which is known not to be
14         //              unique, the specified ID is ignored and the system-generated ID is
15         //              used instead.
16         id: "",
17
18         //      lang: String
19         //              Rarely used.  Overrides the default Dojo locale used to render this widget,
20         //              as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
21         //              Value must be among the list of locales specified during by the Dojo bootstrap,
22         //              formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
23         lang: "",
24
25         //      dir: String
26         //              Unsupported by Dijit, but here for completeness.  Dijit only supports setting text direction on the
27         //              entire document.
28         //              Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
29         //              attribute. Either left-to-right "ltr" or right-to-left "rtl".
30         dir: "",
31
32         // class: String
33         //              HTML class attribute
34         "class": "",
35
36         // style: String
37         //              HTML style attribute
38         style: "",
39
40         // title: String
41         //              HTML title attribute
42         title: "",
43
44         // srcNodeRef: DomNode
45         //              pointer to original dom node
46         srcNodeRef: null,
47
48         // domNode: DomNode
49         //              this is our visible representation of the widget! Other DOM
50         //              Nodes may by assigned to other properties, usually through the
51         //              template system's dojoAttachPonit syntax, but the domNode
52         //              property is the canonical "top level" node in widget UI.
53         domNode: null,
54
55         // attributeMap: Object
56         //              A map of attributes and attachpoints -- typically standard HTML attributes -- to set
57         //              on the widget's dom, at the "domNode" attach point, by default.
58         //              Other node references can be specified as properties of 'this'
59         attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""},  // TODO: add on* handlers?
60
61         //////////// INITIALIZATION METHODS ///////////////////////////////////////
62 //TODOC: params and srcNodeRef need docs.  Is srcNodeRef optional?
63 //TODOC: summary needed for postscript
64         postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
65                 this.create(params, srcNodeRef);
66         },
67
68         create: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
69                 //      summary:
70                 //              Kick off the life-cycle of a widget
71                 //      description:
72                 //              To understand the process by which widgets are instantiated, it
73                 //              is critical to understand what other methods create calls and
74                 //              which of them you'll want to override. Of course, adventurous
75                 //              developers could override create entirely, but this should
76                 //              only be done as a last resort.
77                 //
78                 //              Below is a list of the methods that are called, in the order
79                 //              they are fired, along with notes about what they do and if/when
80                 //              you should over-ride them in your widget:
81                 //
82                 // * postMixInProperties:
83                 //      |       * a stub function that you can over-ride to modify
84                 //              variables that may have been naively assigned by
85                 //              mixInProperties
86                 // * widget is added to manager object here
87                 // * buildRendering:
88                 //      |       * Subclasses use this method to handle all UI initialization
89                 //              Sets this.domNode.  Templated widgets do this automatically
90                 //              and otherwise it just uses the source dom node.
91                 // * postCreate:
92                 //      |       * a stub function that you can over-ride to modify take
93                 //              actions once the widget has been placed in the UI
94
95                 // store pointer to original dom tree
96                 this.srcNodeRef = dojo.byId(srcNodeRef);
97
98                 // For garbage collection.  An array of handles returned by Widget.connect()
99                 // Each handle returned from Widget.connect() is an array of handles from dojo.connect()
100                 this._connects=[];
101
102                 // _attaches: String[]
103                 //              names of all our dojoAttachPoint variables
104                 this._attaches=[];
105
106                 //mixin our passed parameters
107                 if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
108                 if(params){
109                         this.params = params;
110                         dojo.mixin(this,params);
111                 }
112                 this.postMixInProperties();
113
114                 // generate an id for the widget if one wasn't specified
115                 // (be sure to do this before buildRendering() because that function might
116                 // expect the id to be there.
117                 if(!this.id){
118                         this.id=dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
119                 }
120                 dijit.registry.add(this);
121
122                 this.buildRendering();
123
124                 // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
125                 // The placement of these attributes is according to the property mapping in attributeMap.
126                 // Note special handling for 'style' and 'class' attributes which are lists and can
127                 // have elements from both old and new structures, and some attributes like "type"
128                 // cannot be processed this way as they are not mutable.
129                 if(this.domNode){
130                         for(var attr in this.attributeMap){
131                                 var value = this[attr];
132                                 if(typeof value != "object" && ((value !== "" && value !== false) || (params && params[attr]))){
133                                         this.setAttribute(attr, value);
134                                 }
135                         }
136                 }
137
138                 if(this.domNode){
139                         this.domNode.setAttribute("widgetId", this.id);
140                 }
141                 this.postCreate();
142
143                 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
144                 if(this.srcNodeRef && !this.srcNodeRef.parentNode){
145                         delete this.srcNodeRef;
146                 }       
147         },
148
149         postMixInProperties: function(){
150                 // summary
151                 //      Called after the parameters to the widget have been read-in,
152                 //      but before the widget template is instantiated.
153                 //      Especially useful to set properties that are referenced in the widget template.
154         },
155
156         buildRendering: function(){
157                 // summary:
158                 //              Construct the UI for this widget, setting this.domNode.
159                 //              Most widgets will mixin TemplatedWidget, which overrides this method.
160                 this.domNode = this.srcNodeRef || dojo.doc.createElement('div');
161         },
162
163         postCreate: function(){
164                 // summary:
165                 //              Called after a widget's dom has been setup
166         },
167
168         startup: function(){
169                 // summary:
170                 //              Called after a widget's children, and other widgets on the page, have been created.
171                 //              Provides an opportunity to manipulate any children before they are displayed.
172                 //              This is useful for composite widgets that need to control or layout sub-widgets.
173                 //              Many layout widgets can use this as a wiring phase.
174                 this._started = true;
175         },
176
177         //////////// DESTROY FUNCTIONS ////////////////////////////////
178
179         destroyRecursive: function(/*Boolean*/ finalize){
180                 // summary:
181                 //              Destroy this widget and it's descendants. This is the generic
182                 //              "destructor" function that all widget users should call to
183                 //              cleanly discard with a widget. Once a widget is destroyed, it's
184                 //              removed from the manager object.
185                 // finalize: Boolean
186                 //              is this function being called part of global environment
187                 //              tear-down?
188
189                 this.destroyDescendants();
190                 this.destroy();
191         },
192
193         destroy: function(/*Boolean*/ finalize){
194                 // summary:
195                 //              Destroy this widget, but not its descendants
196                 // finalize: Boolean
197                 //              is this function being called part of global environment
198                 //              tear-down?
199
200                 this.uninitialize();
201                 dojo.forEach(this._connects, function(array){
202                         dojo.forEach(array, dojo.disconnect);
203                 });
204
205                 // destroy widgets created as part of template, etc.
206                 dojo.forEach(this._supportingWidgets || [], function(w){ w.destroy(); });
207                 
208                 this.destroyRendering(finalize);
209                 dijit.registry.remove(this.id);
210         },
211
212         destroyRendering: function(/*Boolean*/ finalize){
213                 // summary:
214                 //              Destroys the DOM nodes associated with this widget
215                 // finalize: Boolean
216                 //              is this function being called part of global environment
217                 //              tear-down?
218
219                 if(this.bgIframe){
220                         this.bgIframe.destroy();
221                         delete this.bgIframe;
222                 }
223
224                 if(this.domNode){
225                         dojo._destroyElement(this.domNode);
226                         delete this.domNode;
227                 }
228
229                 if(this.srcNodeRef){
230                         dojo._destroyElement(this.srcNodeRef);
231                         delete this.srcNodeRef;
232                 }
233         },
234
235         destroyDescendants: function(){
236                 // summary:
237                 //              Recursively destroy the children of this widget and their
238                 //              descendants.
239
240                 // TODO: should I destroy in the reverse order, to go bottom up?
241                 dojo.forEach(this.getDescendants(), function(widget){ widget.destroy(); });
242         },
243
244         uninitialize: function(){
245                 // summary:
246                 //              stub function. Override to implement custom widget tear-down
247                 //              behavior.
248                 return false;
249         },
250
251         ////////////////// MISCELLANEOUS METHODS ///////////////////
252
253         onFocus: function(){
254                 // summary:
255                 //              stub function. Override or connect to this method to receive
256                 //              notifications for when the widget moves into focus.
257         },
258
259         onBlur: function(){
260                 // summary:
261                 //              stub function. Override or connect to this method to receive
262                 //              notifications for when the widget moves out of focus.
263         },
264
265         _onFocus: function(e){
266                 this.onFocus();
267         },
268
269         _onBlur: function(){
270                 this.onBlur();
271         },
272
273         setAttribute: function(/*String*/ attr, /*anything*/ value){
274                 // summary
275                 //              Set native HTML attributes reflected in the widget,
276                 //              such as readOnly, disabled, and maxLength in TextBox widgets.
277                 // description
278                 //              In general, a widget's "value" is controlled via setValue()/getValue(), 
279                 //              rather than this method.  The exception is for widgets where the
280                 //              end user can't adjust the value, such as Button and CheckBox;
281                 //              in the unusual case that you want to change the value attribute of
282                 //              those widgets, use setAttribute().
283                 var mapNode = this[this.attributeMap[attr]||'domNode'];
284                 this[attr] = value;
285                 switch(attr){
286                         case "class":
287                                 dojo.addClass(mapNode, value);
288                                 break;
289                         case "style":
290                                 if(mapNode.style.cssText){
291                                         mapNode.style.cssText += "; " + value;// FIXME: Opera
292                                 }else{
293                                         mapNode.style.cssText = value;
294                                 }
295                                 break;
296                         default:
297                                 if(/^on[A-Z]/.test(attr)){ // eg. onSubmit needs to be onsubmit
298                                         attr = attr.toLowerCase();
299                                 }
300                                 if(typeof value == "function"){ // functions execute in the context of the widget
301                                         value = dojo.hitch(this, value);
302                                 }
303                                 dojo.attr(mapNode, attr, value);
304                 }
305         },
306
307         toString: function(){
308                 // summary:
309                 //              returns a string that represents the widget. When a widget is
310                 //              cast to a string, this method will be used to generate the
311                 //              output. Currently, it does not implement any sort of reversable
312                 //              serialization.
313                 return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
314         },
315
316         getDescendants: function(){
317                 // summary:
318                 //      Returns all the widgets that contained by this, i.e., all widgets underneath this.containerNode.
319                 if(this.containerNode){
320                         var list= dojo.query('[widgetId]', this.containerNode);
321                         return list.map(dijit.byNode);          // Array
322                 }else{
323                         return [];
324                 }
325         },
326
327 //TODOC
328         nodesWithKeyClick: ["input", "button"],
329
330         connect: function(
331                         /*Object|null*/ obj,
332                         /*String*/ event,
333                         /*String|Function*/ method){
334                 //      summary:
335                 //              Connects specified obj/event to specified method of this object
336                 //              and registers for disconnect() on widget destroy.
337                 //              Special event: "ondijitclick" triggers on a click or enter-down or space-up
338                 //              Similar to dojo.connect() but takes three arguments rather than four.
339                 var handles =[];
340                 if(event == "ondijitclick"){
341                         // add key based click activation for unsupported nodes.
342                         if(!this.nodesWithKeyClick[obj.nodeName]){
343                                 handles.push(dojo.connect(obj, "onkeydown", this,
344                                         function(e){
345                                                 if(e.keyCode == dojo.keys.ENTER){
346                                                         return (dojo.isString(method))?
347                                                                 this[method](e) : method.call(this, e);
348                                                 }else if(e.keyCode == dojo.keys.SPACE){
349                                                         // stop space down as it causes IE to scroll
350                                                         // the browser window
351                                                         dojo.stopEvent(e);
352                                                 }
353                                         }));
354                                 handles.push(dojo.connect(obj, "onkeyup", this,
355                                         function(e){
356                                                 if(e.keyCode == dojo.keys.SPACE){
357                                                         return dojo.isString(method) ?
358                                                                 this[method](e) : method.call(this, e);
359                                                 }
360                                         }));
361                         }
362                         event = "onclick";
363                 }
364                 handles.push(dojo.connect(obj, event, this, method));
365
366                 // return handles for FormElement and ComboBox
367                 this._connects.push(handles);
368                 return handles;
369         },
370
371         disconnect: function(/*Object*/ handles){
372                 // summary:
373                 //              Disconnects handle created by this.connect.
374                 //              Also removes handle from this widget's list of connects
375                 for(var i=0; i<this._connects.length; i++){
376                         if(this._connects[i]==handles){
377                                 dojo.forEach(handles, dojo.disconnect);
378                                 this._connects.splice(i, 1);
379                                 return;
380                         }
381                 }
382         },
383
384         isLeftToRight: function(){
385                 // summary:
386                 //              Checks the DOM to for the text direction for bi-directional support
387                 // description:
388                 //              This method cannot be used during widget construction because the widget
389                 //              must first be connected to the DOM tree.  Parent nodes are searched for the
390                 //              'dir' attribute until one is found, otherwise left to right mode is assumed.
391                 //              See HTML spec, DIR attribute for more information.
392
393                 if(!("_ltr" in this)){
394                         this._ltr = dojo.getComputedStyle(this.domNode).direction != "rtl";
395                 }
396                 return this._ltr; //Boolean
397         },
398
399         isFocusable: function(){
400                 // summary:
401                 //              Return true if this widget can currently be focused
402                 //              and false if not
403                 return this.focus && (dojo.style(this.domNode, "display") != "none");
404         }
405 });
406
407 }