define("dojo/Stateful", ["./_base/kernel", "./_base/declare", "./_base/lang", "./_base/array"], function(dojo, declare, lang, array) { // module: // dojo/Stateful // summary: // TODOC return dojo.declare("dojo.Stateful", null, { // summary: // Base class for objects that provide named properties with optional getter/setter // control and the ability to watch for property changes // example: // | var obj = new dojo.Stateful(); // | obj.watch("foo", function(){ // | console.log("foo changed to " + this.get("foo")); // | }); // | obj.set("foo","bar"); postscript: function(mixin){ if(mixin){ lang.mixin(this, mixin); } }, get: function(/*String*/name){ // summary: // Get a property on a Stateful instance. // name: // The property to get. // returns: // The property value on this Stateful instance. // description: // Get a named property on a Stateful object. The property may // potentially be retrieved via a getter method in subclasses. In the base class // this just retrieves the object's property. // For example: // | stateful = new dojo.Stateful({foo: 3}); // | stateful.get("foo") // returns 3 // | stateful.foo // returns 3 return this[name]; //Any }, set: function(/*String*/name, /*Object*/value){ // summary: // Set a property on a Stateful instance // name: // The property to set. // value: // The value to set in the property. // returns: // The function returns this dojo.Stateful instance. // description: // Sets named properties on a stateful object and notifies any watchers of // the property. A programmatic setter may be defined in subclasses. // For example: // | stateful = new dojo.Stateful(); // | stateful.watch(function(name, oldValue, value){ // | // this will be called on the set below // | } // | stateful.set(foo, 5); // // set() may also be called with a hash of name/value pairs, ex: // | myObj.set({ // | foo: "Howdy", // | bar: 3 // | }) // This is equivalent to calling set(foo, "Howdy") and set(bar, 3) if(typeof name === "object"){ for(var x in name){ this.set(x, name[x]); } return this; } var oldValue = this[name]; this[name] = value; if(this._watchCallbacks){ this._watchCallbacks(name, oldValue, value); } return this; //dojo.Stateful }, watch: function(/*String?*/name, /*Function*/callback){ // summary: // Watches a property for changes // name: // Indicates the property to watch. This is optional (the callback may be the // only parameter), and if omitted, all the properties will be watched // returns: // An object handle for the watch. The unwatch method of this object // can be used to discontinue watching this property: // | var watchHandle = obj.watch("foo", callback); // | watchHandle.unwatch(); // callback won't be called now // callback: // The function to execute when the property changes. This will be called after // the property has been changed. The callback will be called with the |this| // set to the instance, the first argument as the name of the property, the // second argument as the old value and the third argument as the new value. var callbacks = this._watchCallbacks; if(!callbacks){ var self = this; callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){ var notify = function(propertyCallbacks){ if(propertyCallbacks){ propertyCallbacks = propertyCallbacks.slice(); for(var i = 0, l = propertyCallbacks.length; i < l; i++){ try{ propertyCallbacks[i].call(self, name, oldValue, value); }catch(e){ console.error(e); } } } }; notify(callbacks['_' + name]); if(!ignoreCatchall){ notify(callbacks["*"]); // the catch-all } }; // we use a function instead of an object so it will be ignored by JSON conversion } if(!callback && typeof name === "function"){ callback = name; name = "*"; }else{ // prepend with dash to prevent name conflicts with function (like "name" property) name = '_' + name; } var propertyCallbacks = callbacks[name]; if(typeof propertyCallbacks !== "object"){ propertyCallbacks = callbacks[name] = []; } propertyCallbacks.push(callback); return { unwatch: function(){ propertyCallbacks.splice(array.indexOf(propertyCallbacks, callback), 1); } }; //Object } }); });