source: Dev/trunk/src/client/dojox/form/manager/_Mixin.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 14.8 KB
Line 
1define([
2        "dojo/_base/window",
3        "dojo/_base/lang",
4        "dojo/_base/array",
5        "dojo/on",
6        "dojo/dom-attr",
7        "dojo/dom-class",
8        "dijit/_base/manager",
9        "dijit/_Widget",
10        "dijit/form/_FormWidget",
11        "dijit/form/Button",
12        "dijit/form/CheckBox",
13        "dojo/_base/declare"
14], function(win, lang, array, on, domAttr, domClass, manager, Widget, FormWidget, Button, CheckBox, declare){
15        // TODO: This class is loading a bunch of extra widgets just to perform isInstanceOf operations,
16        // which is wasteful
17
18        var fm = lang.getObject("dojox.form.manager", true),
19
20                aa = fm.actionAdapter = function(action){
21                        // summary:
22                        //              Adapter that automates application of actions to arrays.
23                        // action: Function
24                        //              Function that takes three parameters: a name, an object
25                        //              (usually node or widget), and a value. This action will
26                        //              be applied to all elements of array.
27                        return function(name, elems, value){
28                                if(lang.isArray(elems)){
29                                        array.forEach(elems, function(elem){
30                                                action.call(this, name, elem, value);
31                                        }, this);
32                                }else{
33                                        action.apply(this, arguments);
34                                }
35                        };
36                },
37
38                ia = fm.inspectorAdapter = function(inspector){
39                        // summary:
40                        //              Adapter that applies an inspector only to the first item of the array.
41                        // inspector: Function
42                        //              Function that takes three parameters: a name, an object
43                        //              (usually node or widget), and a value.
44                        return function(name, elem, value){
45                                return inspector.call(this, name, lang.isArray(elem) ? elem[0] : elem, value);
46                        };
47                },
48
49                skipNames = {domNode: 1, containerNode: 1, srcNodeRef: 1, bgIframe: 1},
50
51                keys = fm._keys = function(o){
52                        // similar to dojox.lang.functional.keys
53                        var list = [], key;
54                        for(key in o){
55                                if(o.hasOwnProperty(key)){
56                                        list.push(key);
57                                }
58                        }
59                        return list;
60                },
61
62                registerWidget = function(widget){
63                        var name = widget.get("name");
64                        if(name && widget.isInstanceOf(FormWidget)){
65                                if(name in this.formWidgets){
66                                        var a = this.formWidgets[name].widget;
67                                        if(lang.isArray(a)){
68                                                a.push(widget);
69                                        }else{
70                                                this.formWidgets[name].widget = [a, widget];
71                                        }
72                                }else{
73                                        this.formWidgets[name] = {widget: widget, connections: []};
74                                }
75                        }else{
76                                name = null;
77                        }
78                        return name;
79                },
80
81                getObserversFromWidget = function(name){
82                        var observers = {};
83                        aa(function(_, w){
84                                var o = w.get("data-dojo-observer") || w.get("observer");
85                                if(o && typeof o == "string"){
86                                        array.forEach(o.split(","), function(o){
87                                                o = lang.trim(o);
88                                                if(o && lang.isFunction(this[o])){
89                                                        observers[o] = 1;
90                                                }
91                                        }, this);
92                                }
93                        }).call(this, null, this.formWidgets[name].widget);
94                        return keys(observers);
95                },
96
97                connectWidget = function(name, observers){
98                        var t = this.formWidgets[name], w = t.widget, c = t.connections;
99                        if(c.length){
100                                array.forEach(c, function(item){ item.remove(); });
101                                c = t.connections = [];
102                        }
103                        if(lang.isArray(w)){
104                                // radio buttons
105                                array.forEach(w, function(w){
106                                        array.forEach(observers, function(o){
107                                                c.push(on(w, "change", lang.hitch(this, function(evt){
108                                                        // TODO: for some reason for radio button widgets
109                                                        // w.checked != w.focusNode.checked when value changes.
110                                                        // We test the underlying value to be 100% sure.
111                                                        if(this.watching && domAttr.get(w.focusNode, "checked")){
112                                                                this[o](w.get("value"), name, w, evt);
113                                                        }
114                                                })));
115                                        }, this);
116                                }, this);
117                        }else{
118                                // the rest
119                                // the next line is a crude workaround for Button that fires onClick instead of onChange
120                                var eventName = w.isInstanceOf(Button) ?
121                                                "click" : "change";
122                                array.forEach(observers, function(o){
123                                        c.push(on(w, eventName, lang.hitch(this, function(evt){
124                                                if(this.watching){
125                                                        this[o](w.get("value"), name, w, evt);
126                                                }
127                                        })));
128                                }, this);
129                        }
130                };
131
132        var _Mixin = declare("dojox.form.manager._Mixin", null, {
133                // summary:
134                //              Mixin to orchestrate dynamic forms.
135                // description:
136                //              This mixin provides a foundation for an enhanced form
137                //              functionality: unified access to individual form elements,
138                //              unified "onchange" event processing, general event
139                //              processing, I/O orchestration, and common form-related
140                //              functionality. See additional mixins in dojox.form.manager
141                //              namespace.
142
143                watching: true,
144
145                startup: function(){
146                        // summary:
147                        //              Called after all the widgets have been instantiated and their
148                        //              dom nodes have been inserted somewhere under win.doc.body.
149
150                        if(this._started){ return; }
151
152                        this.formWidgets = {};
153                        this.formNodes = {};
154                        this.registerWidgetDescendants(this);
155
156                        this.inherited(arguments);
157                },
158
159                destroy: function(){
160                        // summary:
161                        //              Called when the widget is being destroyed
162
163                        for(var name in this.formWidgets){
164                                array.forEach(this.formWidgets[name].connections, function(item){
165                                        item.remove();
166                                });
167                        }
168                        this.formWidgets = {};
169
170                        this.inherited(arguments);
171                },
172
173                // register/unregister widgets and nodes
174
175                registerWidget: function(widget){
176                        // summary:
177                        //              Register a widget with the form manager
178                        // widget: String|Node|dijit/form/_FormWidget
179                        //              A widget, or its widgetId, or its DOM node
180                        // returns: Object
181                        //              Returns self
182                        if(typeof widget == "string"){
183                                widget = manager.byId(widget);
184                        }else if(widget.tagName && widget.cloneNode){
185                                widget = manager.byNode(widget);
186                        }
187                        var name = registerWidget.call(this, widget);
188                        if(name){
189                                connectWidget.call(this, name, getObserversFromWidget.call(this, name));
190                        }
191                        return this;
192                },
193
194                unregisterWidget: function(name){
195                        // summary:
196                        //              Removes the widget by name from internal tables unregistering
197                        //              connected observers
198                        // name: String
199                        //              Name of the to unregister
200                        // returns: Object
201                        //              Returns self
202                        if(name in this.formWidgets){
203                                array.forEach(this.formWidgets[name].connections, function(item){
204                                        item.remove();
205                                });
206                                delete this.formWidgets[name];
207                        }
208                        return this;
209                },
210
211                registerWidgetDescendants: function(widget){
212                        // summary:
213                        //              Register widget's descendants with the form manager
214                        // widget: String|Node|dijit._Widget
215                        //              A widget, or its widgetId, or its DOM node
216                        // returns: Object
217                        //              Returns self
218
219                        // convert to widget, if required
220                        if(typeof widget == "string"){
221                                widget = manager.byId(widget);
222                        }else if(widget.tagName && widget.cloneNode){
223                                widget = manager.byNode(widget);
224                        }
225
226                        // build the map of widgets
227                        var widgets = array.map(widget.getDescendants(), registerWidget, this);
228
229                        // process observers for widgets
230                        array.forEach(widgets, function(name){
231                                if(name){
232                                        connectWidget.call(this, name, getObserversFromWidget.call(this, name));
233                                }
234                        }, this);
235
236                        // do the same with nodes, if available
237                        return this.registerNodeDescendants ?
238                                this.registerNodeDescendants(widget.domNode) : this;
239                },
240
241                unregisterWidgetDescendants: function(widget){
242                        // summary:
243                        //              Unregister widget's descendants with the form manager
244                        // widget: String|Node|dijit/_Widget
245                        //              A widget, or its widgetId, or its DOM node
246                        // returns: Object
247                        //              Returns self
248
249                        // convert to widget, if required
250                        if(typeof widget == "string"){
251                                widget = manager.byId(widget);
252                        }else if(widget.tagName && widget.cloneNode){
253                                widget = manager.byNode(widget);
254                        }
255
256                        // unregister widgets by names
257                        array.forEach(
258                                array.map(
259                                        widget.getDescendants(),
260                                        function(w){
261                                                return w instanceof FormWidget && w.get("name") || null;
262                                        }
263                                ),
264                                function(name){
265                                        if(name){
266                                                this.unregisterWidget(name);
267                                        }
268                                },
269                                this
270                        );
271
272                        // do the same with nodes, if available
273                        return this.unregisterNodeDescendants ?
274                                this.unregisterNodeDescendants(widget.domNode) : this;
275                },
276
277                // value accessors
278
279                formWidgetValue: function(elem, value){
280                        // summary:
281                        //              Set or get a form widget by name.
282                        // elem: String|Object|Array
283                        //              Form element's name, widget object, or array or radio widgets.
284                        // value: Object?
285                        //              Optional. The value to set.
286                        // returns: Object
287                        //              For a getter it returns the value, for a setter it returns
288                        //              self. If the elem is not valid, null will be returned.
289
290                        var isSetter = arguments.length == 2 && value !== undefined, result;
291
292                        if(typeof elem == "string"){
293                                elem = this.formWidgets[elem];
294                                if(elem){
295                                        elem = elem.widget;
296                                }
297                        }
298
299                        if(!elem){
300                                return null;    // Object
301                        }
302
303                        if(lang.isArray(elem)){
304                                // input/radio array of widgets
305                                if(isSetter){
306                                        array.forEach(elem, function(widget){
307                                                widget.set("checked", false, !this.watching);
308                                        }, this);
309                                        array.forEach(elem, function(widget){
310                                                widget.set("checked", widget.value === value, !this.watching);
311                                        }, this);
312                                        return this;    // self
313                                }
314                                // getter
315                                array.some(elem, function(widget){
316                                        // TODO: for some reason for radio button widgets
317                                        // w.checked != w.focusNode.checked when value changes.
318                                        // We test the underlying value to be 100% sure.
319                                        if(domAttr.get(widget.focusNode, "checked")){
320                                        //if(widget.get("checked")){
321                                                result = widget;
322                                                return true;
323                                        }
324                                        return false;
325                                });
326                                return result ? result.get("value") : "";       // String
327                        }
328
329                        // checkbox widget is a special case :-(
330                        if(elem.isInstanceOf && elem.isInstanceOf(CheckBox)){
331                                if(isSetter){
332                                        elem.set("value", Boolean(value), !this.watching);
333                                        return this;    // self
334                                }
335                                return Boolean(elem.get("value"));      // Object
336                        }
337
338                        // all other elements
339                        if(isSetter){
340                                elem.set("value", value, !this.watching);
341                                return this;    // self
342                        }
343                        return elem.get("value");       // Object
344                },
345
346                formPointValue: function(elem, value){
347                        // summary:
348                        //              Set or get a node context by name (using dojoAttachPoint).
349                        // elem: String|Object|Array
350                        //              A node.
351                        // value: Object?
352                        //              Optional. The value to set.
353                        // returns: Object
354                        //              For a getter it returns the value, for a setter it returns
355                        //              self. If the elem is not valid, null will be returned.
356
357                        if(elem && typeof elem == "string"){
358                                elem = this[elem];
359                        }
360
361                        if(!elem || !elem.tagName || !elem.cloneNode){
362                                return null;    // Object
363                        }
364
365                        if(!domClass.contains(elem, "dojoFormValue")){
366                                // accessing the value of the attached point not marked with CSS class 'dojoFormValue'
367                                return null;
368                        }
369
370                        if(arguments.length == 2 && value !== undefined){
371                                // setter
372                                elem.innerHTML = value;
373                                return this;    // self
374                        }
375                        // getter
376                        return elem.innerHTML;  // String
377                },
378
379                // inspectors
380
381                inspectFormWidgets: function(inspector, state, defaultValue){
382                        // summary:
383                        //              Run an inspector function on controlled widgets returning a result object.
384                        // inspector: Function
385                        //              A function to be called on a widget. Takes three arguments: a name, a widget object
386                        //              or an array of widget objects, and a supplied value. Runs in the context of
387                        //              the form manager. Returns a value that will be collected and returned as a state.
388                        // state: Object?
389                        //              Optional. If a name-value dictionary --- only listed names will be processed.
390                        //              If an array, all names in the array will be processed with defaultValue.
391                        //              If omitted or null, all widgets will be processed with defaultValue.
392                        // defaultValue: Object?
393                        //              Optional. The default state (true, if omitted).
394
395                        var name, result = {};
396
397                        if(state){
398                                if(lang.isArray(state)){
399                                        array.forEach(state, function(name){
400                                                if(name in this.formWidgets){
401                                                        result[name] = inspector.call(this, name, this.formWidgets[name].widget, defaultValue);
402                                                }
403                                        }, this);
404                                }else{
405                                        for(name in state){
406                                                if(name in this.formWidgets){
407                                                        result[name] = inspector.call(this, name, this.formWidgets[name].widget, state[name]);
408                                                }
409                                        }
410                                }
411                        }else{
412                                for(name in this.formWidgets){
413                                        result[name] = inspector.call(this, name, this.formWidgets[name].widget, defaultValue);
414                                }
415                        }
416
417                        return result;  // Object
418                },
419
420                inspectAttachedPoints: function(inspector, state, defaultValue){
421                        // summary:
422                        //              Run an inspector function on "dojoAttachPoint" nodes returning a result object.
423                        // inspector: Function
424                        //              A function to be called on a node. Takes three arguments: a name, a node or
425                        //              an array of nodes, and a supplied value. Runs in the context of the form manager.
426                        //              Returns a value that will be collected and returned as a state.
427                        // state: Object?
428                        //              Optional. If a name-value dictionary --- only listed names will be processed.
429                        //              If an array, all names in the array will be processed with defaultValue.
430                        //              If omitted or null, all attached point nodes will be processed with defaultValue.
431                        // defaultValue: Object?
432                        //              Optional. The default state (true, if omitted).
433
434                        var name, elem, result = {};
435
436                        if(state){
437                                if(lang.isArray(state)){
438                                        array.forEach(state, function(name){
439                                                elem = this[name];
440                                                if(elem && elem.tagName && elem.cloneNode){
441                                                        result[name] = inspector.call(this, name, elem, defaultValue);
442                                                }
443                                        }, this);
444                                }else{
445                                        for(name in state){
446                                                elem = this[name];
447                                                if(elem && elem.tagName && elem.cloneNode){
448                                                        result[name] = inspector.call(this, name, elem, state[name]);
449                                                }
450                                        }
451                                }
452                        }else{
453                                for(name in this){
454                                        if(!(name in skipNames)){
455                                                elem = this[name];
456                                                if(elem && elem.tagName && elem.cloneNode){
457                                                        result[name] = inspector.call(this, name, elem, defaultValue);
458                                                }
459                                        }
460                                }
461                        }
462
463                        return result;  // Object
464                },
465
466                inspect: function(inspector, state, defaultValue){
467                        // summary:
468                        //              Run an inspector function on controlled elements returning a result object.
469                        // inspector: Function
470                        //              A function to be called on a widget, form element, and an attached node.
471                        //              Takes three arguments: a name, a node (domNode in the case of widget) or
472                        //              an array of such objects, and a supplied value. Runs in the context of
473                        //              the form manager. Returns a value that will be collected and returned as a state.
474                        // state: Object?
475                        //              Optional. If a name-value dictionary --- only listed names will be processed.
476                        //              If an array, all names in the array will be processed with defaultValue.
477                        //              If omitted or null, all controlled elements will be processed with defaultValue.
478                        // defaultValue: Object?
479                        //              Optional. The default state (true, if omitted).
480
481                        var result = this.inspectFormWidgets(function(name, widget, value){
482                                if(lang.isArray(widget)){
483                                        return inspector.call(this, name, array.map(widget, function(w){ return w.domNode; }), value);
484                                }
485                                return inspector.call(this, name, widget.domNode, value);
486                        }, state, defaultValue);
487                        if(this.inspectFormNodes){
488                                lang.mixin(result, this.inspectFormNodes(inspector, state, defaultValue));
489                        }
490                        return lang.mixin(result, this.inspectAttachedPoints(inspector, state, defaultValue));  // Object
491                }
492        });
493
494// These arguments can be specified for widgets which are used in forms.
495// Since any widget can be specified as sub widgets, mix it into the base
496// widget class.  (This is a hack, but it's effective.)
497lang.extend(Widget, {
498        observer: ""
499});
500return _Mixin;
501});
Note: See TracBrowser for help on using the repository browser.