source: Dev/trunk/src/client/dojo/Stateful.js @ 527

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

Added Dojo 1.9.3 release.

File size: 7.0 KB
Line 
1define(["./_base/declare", "./_base/lang", "./_base/array", "./when"], function(declare, lang, array, when){
2        // module:
3        //              dojo/Stateful
4
5return declare("dojo.Stateful", null, {
6        // summary:
7        //              Base class for objects that provide named properties with optional getter/setter
8        //              control and the ability to watch for property changes
9        //
10        //              The class also provides the functionality to auto-magically manage getters
11        //              and setters for object attributes/properties.
12        //             
13        //              Getters and Setters should follow the format of _xxxGetter or _xxxSetter where
14        //              the xxx is a name of the attribute to handle.  So an attribute of "foo"
15        //              would have a custom getter of _fooGetter and a custom setter of _fooSetter.
16        //
17        // example:
18        //      |       require(["dojo/Stateful", function(Stateful) {
19        //      |               var obj = new Stateful();
20        //      |               obj.watch("foo", function(){
21        //      |                       console.log("foo changed to " + this.get("foo"));
22        //      |               });
23        //      |               obj.set("foo","bar");
24        //      |       });
25
26        // _attrPairNames: Hash
27        //              Used across all instances a hash to cache attribute names and their getter
28        //              and setter names.
29        _attrPairNames: {},
30
31        _getAttrNames: function(name){
32                // summary:
33                //              Helper function for get() and set().
34                //              Caches attribute name values so we don't do the string ops every time.
35                // tags:
36                //              private
37
38                var apn = this._attrPairNames;
39                if(apn[name]){ return apn[name]; }
40                return (apn[name] = {
41                        s: "_" + name + "Setter",
42                        g: "_" + name + "Getter"
43                });
44        },
45
46        postscript: function(/*Object?*/ params){
47                // Automatic setting of params during construction
48                if (params){ this.set(params); }
49        },
50
51        _get: function(name, names){
52                // summary:
53                //              Private function that does a get based off a hash of names
54                // names:
55                //              Hash of names of custom attributes
56                return typeof this[names.g] === "function" ? this[names.g]() : this[name];
57        },
58        get: function(/*String*/name){
59                // summary:
60                //              Get a property on a Stateful instance.
61                // name:
62                //              The property to get.
63                // returns:
64                //              The property value on this Stateful instance.
65                // description:
66                //              Get a named property on a Stateful object. The property may
67                //              potentially be retrieved via a getter method in subclasses. In the base class
68                //              this just retrieves the object's property.
69                // example:
70                //      |       require(["dojo/Stateful", function(Stateful) {
71                //      |               var stateful = new Stateful({foo: 3});
72                //      |               stateful.get("foo") // returns 3
73                //      |               stateful.foo // returns 3
74                //      |       });
75
76                return this._get(name, this._getAttrNames(name)); //Any
77        },
78        set: function(/*String*/name, /*Object*/value){
79                // summary:
80                //              Set a property on a Stateful instance
81                // name:
82                //              The property to set.
83                // value:
84                //              The value to set in the property.
85                // returns:
86                //              The function returns this dojo.Stateful instance.
87                // description:
88                //              Sets named properties on a stateful object and notifies any watchers of
89                //              the property. A programmatic setter may be defined in subclasses.
90                // example:
91                //      |       require(["dojo/Stateful", function(Stateful) {
92                //      |               var stateful = new Stateful();
93                //      |               stateful.watch(function(name, oldValue, value){
94                //      |                       // this will be called on the set below
95                //      |               }
96                //      |               stateful.set(foo, 5);
97                //      set() may also be called with a hash of name/value pairs, ex:
98                //      |               stateful.set({
99                //      |                       foo: "Howdy",
100                //      |                       bar: 3
101                //      |               });
102                //      |       });
103                //      This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
104
105                // If an object is used, iterate through object
106                if(typeof name === "object"){
107                        for(var x in name){
108                                if(name.hasOwnProperty(x) && x !="_watchCallbacks"){
109                                        this.set(x, name[x]);
110                                }
111                        }
112                        return this;
113                }
114
115                var names = this._getAttrNames(name),
116                        oldValue = this._get(name, names),
117                        setter = this[names.s],
118                        result;
119                if(typeof setter === "function"){
120                        // use the explicit setter
121                        result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
122                }else{
123                        // no setter so set attribute directly
124                        this[name] = value;
125                }
126                if(this._watchCallbacks){
127                        var self = this;
128                        // If setter returned a promise, wait for it to complete, otherwise call watches immediatly
129                        when(result, function(){
130                                self._watchCallbacks(name, oldValue, value);
131                        });
132                }
133                return this; // dojo/Stateful
134        },
135        _changeAttrValue: function(name, value){
136                // summary:
137                //              Internal helper for directly changing an attribute value.
138                //
139                // name: String
140                //              The property to set.
141                // value: Mixed
142                //              The value to set in the property.
143                //
144                // description:
145                //              Directly change the value of an attribute on an object, bypassing any
146                //              accessor setter.  Also handles the calling of watch and emitting events.
147                //              It is designed to be used by descendent class when there are two values
148                //              of attributes that are linked, but calling .set() is not appropriate.
149
150                var oldValue = this.get(name);
151                this[name] = value;
152                if(this._watchCallbacks){
153                        this._watchCallbacks(name, oldValue, value);
154                }
155                return this; // dojo/Stateful
156        },
157        watch: function(/*String?*/name, /*Function*/callback){
158                // summary:
159                //              Watches a property for changes
160                // name:
161                //              Indicates the property to watch. This is optional (the callback may be the
162                //              only parameter), and if omitted, all the properties will be watched
163                // returns:
164                //              An object handle for the watch. The unwatch method of this object
165                //              can be used to discontinue watching this property:
166                //              |       var watchHandle = obj.watch("foo", callback);
167                //              |       watchHandle.unwatch(); // callback won't be called now
168                // callback:
169                //              The function to execute when the property changes. This will be called after
170                //              the property has been changed. The callback will be called with the |this|
171                //              set to the instance, the first argument as the name of the property, the
172                //              second argument as the old value and the third argument as the new value.
173
174                var callbacks = this._watchCallbacks;
175                if(!callbacks){
176                        var self = this;
177                        callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){
178                                var notify = function(propertyCallbacks){
179                                        if(propertyCallbacks){
180                                                propertyCallbacks = propertyCallbacks.slice();
181                                                for(var i = 0, l = propertyCallbacks.length; i < l; i++){
182                                                        propertyCallbacks[i].call(self, name, oldValue, value);
183                                                }
184                                        }
185                                };
186                                notify(callbacks['_' + name]);
187                                if(!ignoreCatchall){
188                                        notify(callbacks["*"]); // the catch-all
189                                }
190                        }; // we use a function instead of an object so it will be ignored by JSON conversion
191                }
192                if(!callback && typeof name === "function"){
193                        callback = name;
194                        name = "*";
195                }else{
196                        // prepend with dash to prevent name conflicts with function (like "name" property)
197                        name = '_' + name;
198                }
199                var propertyCallbacks = callbacks[name];
200                if(typeof propertyCallbacks !== "object"){
201                        propertyCallbacks = callbacks[name] = [];
202                }
203                propertyCallbacks.push(callback);
204
205                // TODO: Remove unwatch in 2.0
206                var handle = {};
207                handle.unwatch = handle.remove = function(){
208                        var index = array.indexOf(propertyCallbacks, callback);
209                        if(index > -1){
210                                propertyCallbacks.splice(index, 1);
211                        }
212                };
213                return handle; //Object
214        }
215
216});
217
218});
Note: See TracBrowser for help on using the repository browser.