Stateful.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojo.Stateful"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojo.Stateful"] = true;
  8. dojo.provide("dojo.Stateful");
  9. dojo.declare("dojo.Stateful", null, {
  10. // summary:
  11. // Base class for objects that provide named properties with optional getter/setter
  12. // control and the ability to watch for property changes
  13. // example:
  14. // | var obj = new dojo.Stateful();
  15. // | obj.watch("foo", function(){
  16. // | console.log("foo changed to " + this.get("foo"));
  17. // | });
  18. // | obj.set("foo","bar");
  19. postscript: function(mixin){
  20. if(mixin){
  21. dojo.mixin(this, mixin);
  22. }
  23. },
  24. get: function(/*String*/name){
  25. // summary:
  26. // Get a property on a Stateful instance.
  27. // name:
  28. // The property to get.
  29. // description:
  30. // Get a named property on a Stateful object. The property may
  31. // potentially be retrieved via a getter method in subclasses. In the base class
  32. // this just retrieves the object's property.
  33. // For example:
  34. // | stateful = new dojo.Stateful({foo: 3});
  35. // | stateful.get("foo") // returns 3
  36. // | stateful.foo // returns 3
  37. return this[name];
  38. },
  39. set: function(/*String*/name, /*Object*/value){
  40. // summary:
  41. // Set a property on a Stateful instance
  42. // name:
  43. // The property to set.
  44. // value:
  45. // The value to set in the property.
  46. // description:
  47. // Sets named properties on a stateful object and notifies any watchers of
  48. // the property. A programmatic setter may be defined in subclasses.
  49. // For example:
  50. // | stateful = new dojo.Stateful();
  51. // | stateful.watch(function(name, oldValue, value){
  52. // | // this will be called on the set below
  53. // | }
  54. // | stateful.set(foo, 5);
  55. //
  56. // set() may also be called with a hash of name/value pairs, ex:
  57. // | myObj.set({
  58. // | foo: "Howdy",
  59. // | bar: 3
  60. // | })
  61. // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
  62. if(typeof name === "object"){
  63. for(var x in name){
  64. this.set(x, name[x]);
  65. }
  66. return this;
  67. }
  68. var oldValue = this[name];
  69. this[name] = value;
  70. if(this._watchCallbacks){
  71. this._watchCallbacks(name, oldValue, value);
  72. }
  73. return this;
  74. },
  75. watch: function(/*String?*/name, /*Function*/callback){
  76. // summary:
  77. // Watches a property for changes
  78. // name:
  79. // Indicates the property to watch. This is optional (the callback may be the
  80. // only parameter), and if omitted, all the properties will be watched
  81. // returns:
  82. // An object handle for the watch. The unwatch method of this object
  83. // can be used to discontinue watching this property:
  84. // | var watchHandle = obj.watch("foo", callback);
  85. // | watchHandle.unwatch(); // callback won't be called now
  86. // callback:
  87. // The function to execute when the property changes. This will be called after
  88. // the property has been changed. The callback will be called with the |this|
  89. // set to the instance, the first argument as the name of the property, the
  90. // second argument as the old value and the third argument as the new value.
  91. var callbacks = this._watchCallbacks;
  92. if(!callbacks){
  93. var self = this;
  94. callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){
  95. var notify = function(propertyCallbacks){
  96. if(propertyCallbacks){
  97. propertyCallbacks = propertyCallbacks.slice();
  98. for(var i = 0, l = propertyCallbacks.length; i < l; i++){
  99. try{
  100. propertyCallbacks[i].call(self, name, oldValue, value);
  101. }catch(e){
  102. console.error(e);
  103. }
  104. }
  105. }
  106. };
  107. notify(callbacks['_' + name]);
  108. if(!ignoreCatchall){
  109. notify(callbacks["*"]); // the catch-all
  110. }
  111. }; // we use a function instead of an object so it will be ignored by JSON conversion
  112. }
  113. if(!callback && typeof name === "function"){
  114. callback = name;
  115. name = "*";
  116. }else{
  117. // prepend with dash to prevent name conflicts with function (like "name" property)
  118. name = '_' + name;
  119. }
  120. var propertyCallbacks = callbacks[name];
  121. if(typeof propertyCallbacks !== "object"){
  122. propertyCallbacks = callbacks[name] = [];
  123. }
  124. propertyCallbacks.push(callback);
  125. return {
  126. unwatch: function(){
  127. propertyCallbacks.splice(dojo.indexOf(propertyCallbacks, callback), 1);
  128. }
  129. };
  130. }
  131. });
  132. }