/public/javascripts/dojo/release/dojo/dojo/_base/declare.js

http://enginey.googlecode.com/ · JavaScript · 190 lines · 94 code · 8 blank · 88 comment · 39 complexity · 37180a1d47bf56f3a7e5095f985acad9 MD5 · raw file

  1. /*
  2. Copyright (c) 2004-2008, 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._base.declare"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojo._base.declare"] = true;
  8. dojo.provide("dojo._base.declare");
  9. dojo.require("dojo._base.lang");
  10. // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
  11. dojo.declare = function(/*String*/ className, /*Function|Function[]*/ superclass, /*Object*/ props){
  12. // summary:
  13. // Create a feature-rich constructor from compact notation
  14. // className:
  15. // The name of the constructor (loosely, a "class")
  16. // stored in the "declaredClass" property in the created prototype
  17. // superclass:
  18. // May be null, a Function, or an Array of Functions. If an array,
  19. // the first element is used as the prototypical ancestor and
  20. // any following Functions become mixin ancestors.
  21. // props:
  22. // An object whose properties are copied to the
  23. // created prototype.
  24. // Add an instance-initialization function by making it a property
  25. // named "constructor".
  26. // description:
  27. // Create a constructor using a compact notation for inheritance and
  28. // prototype extension.
  29. //
  30. // All superclasses (including mixins) must be Functions (not simple Objects).
  31. //
  32. // Mixin ancestors provide a type of multiple inheritance. Prototypes of mixin
  33. // ancestors are copied to the new class: changes to mixin prototypes will
  34. // not affect classes to which they have been mixed in.
  35. //
  36. // "className" is cached in "declaredClass" property of the new class.
  37. //
  38. // example:
  39. // | dojo.declare("my.classes.bar", my.classes.foo, {
  40. // | // properties to be added to the class prototype
  41. // | someValue: 2,
  42. // | // initialization function
  43. // | constructor: function(){
  44. // | this.myComplicatedObject = new ReallyComplicatedObject();
  45. // | },
  46. // | // other functions
  47. // | someMethod: function(){
  48. // | doStuff();
  49. // | }
  50. // | );
  51. // process superclass argument
  52. var dd = arguments.callee, mixins;
  53. if(dojo.isArray(superclass)){
  54. mixins = superclass;
  55. superclass = mixins.shift();
  56. }
  57. // construct intermediate classes for mixins
  58. if(mixins){
  59. dojo.forEach(mixins, function(m){
  60. if(!m){ throw(className + ": mixin #" + i + " is null"); } // It's likely a required module is not loaded
  61. superclass = dd._delegate(superclass, m);
  62. });
  63. }
  64. // create constructor
  65. var ctor = dd._delegate(superclass);
  66. // extend with "props"
  67. props = props || {};
  68. ctor.extend(props);
  69. // more prototype decoration
  70. dojo.extend(ctor, {declaredClass: className, _constructor: props.constructor/*, preamble: null*/});
  71. // special help for IE
  72. ctor.prototype.constructor = ctor;
  73. // create named reference
  74. return dojo.setObject(className, ctor); // Function
  75. };
  76. dojo.mixin(dojo.declare, {
  77. _delegate: function(base, mixin){
  78. var bp = (base||0).prototype, mp = (mixin||0).prototype, dd=dojo.declare;
  79. // fresh constructor, fresh prototype
  80. var ctor = dd._makeCtor();
  81. // cache ancestry
  82. dojo.mixin(ctor, {superclass: bp, mixin: mp, extend: dd._extend});
  83. // chain prototypes
  84. if(base){ctor.prototype = dojo._delegate(bp);}
  85. // add mixin and core
  86. dojo.extend(ctor, dd._core, mp||0, {_constructor: null, preamble: null});
  87. // special help for IE
  88. ctor.prototype.constructor = ctor;
  89. // name this class for debugging
  90. ctor.prototype.declaredClass = (bp||0).declaredClass + '_' + (mp||0).declaredClass;
  91. return ctor;
  92. },
  93. _extend: function(props){
  94. var i, fn;
  95. for(i in props){ if(dojo.isFunction(fn=props[i]) && !0[i]){fn.nom=i;fn.ctor=this;} }
  96. dojo.extend(this, props);
  97. },
  98. _makeCtor: function(){
  99. // we have to make a function, but don't want to close over anything
  100. return function(){ this._construct(arguments); };
  101. },
  102. _core: {
  103. _construct: function(args){
  104. var c=args.callee, s=c.superclass, ct=s&&s.constructor, m=c.mixin, mct=m&&m.constructor, a=args, ii, fn;
  105. // side-effect of = used on purpose here, lint may complain, don't try this at home
  106. if(a[0]){
  107. // FIXME: preambles for each mixin should be allowed
  108. // FIXME:
  109. // should we allow the preamble here NOT to modify the
  110. // default args, but instead to act on each mixin
  111. // independently of the class instance being constructed
  112. // (for impedence matching)?
  113. // allow any first argument w/ a "preamble" property to act as a
  114. // class preamble (not exclusive of the prototype preamble)
  115. if(/*dojo.isFunction*/((fn = a[0].preamble))){
  116. a = fn.apply(this, a) || a;
  117. }
  118. }
  119. // prototype preamble
  120. if((fn = c.prototype.preamble)){a = fn.apply(this, a) || a;}
  121. // FIXME:
  122. // need to provide an optional prototype-settable
  123. // "_explicitSuper" property which disables this
  124. // initialize superclass
  125. if(ct&&ct.apply){ct.apply(this, a);}
  126. // initialize mixin
  127. if(mct&&mct.apply){mct.apply(this, a);}
  128. // initialize self
  129. if((ii=c.prototype._constructor)){ii.apply(this, args);}
  130. // post construction
  131. if(this.constructor.prototype==c.prototype && (ct=this.postscript)){ ct.apply(this, args); }
  132. },
  133. _findMixin: function(mixin){
  134. var c = this.constructor, p, m;
  135. while(c){
  136. p = c.superclass;
  137. m = c.mixin;
  138. if(m==mixin || (m instanceof mixin.constructor)){return p;}
  139. if(m && m._findMixin && (m=m._findMixin(mixin))){return m;}
  140. c = p && p.constructor;
  141. }
  142. },
  143. _findMethod: function(name, method, ptype, has){
  144. // consciously trading readability for bytes and speed in this low-level method
  145. var p=ptype, c, m, f;
  146. do{
  147. c = p.constructor;
  148. m = c.mixin;
  149. // find method by name in our mixin ancestor
  150. if(m && (m=this._findMethod(name, method, m, has))){return m;}
  151. // if we found a named method that either exactly-is or exactly-is-not 'method'
  152. if((f=p[name])&&(has==(f==method))){return p;}
  153. // ascend chain
  154. p = c.superclass;
  155. }while(p);
  156. // if we couldn't find an ancestor in our primary chain, try a mixin chain
  157. return !has && (p=this._findMixin(ptype)) && this._findMethod(name, method, p, has);
  158. },
  159. inherited: function(name, args, newArgs){
  160. // optionalize name argument
  161. var a = arguments;
  162. if(!dojo.isString(a[0])){newArgs=args; args=name; name=args.callee.nom;}
  163. a = newArgs||args;
  164. var c = args.callee, p = this.constructor.prototype, fn, mp;
  165. // if not an instance override
  166. if(this[name] != c || p[name] == c){
  167. // start from memoized prototype, or
  168. // find a prototype that has property 'name' == 'c'
  169. mp = (c.ctor||0).superclass || this._findMethod(name, c, p, true);
  170. if(!mp){throw(this.declaredClass + ': inherited method "' + name + '" mismatch');}
  171. // find a prototype that has property 'name' != 'c'
  172. p = this._findMethod(name, c, mp, false);
  173. }
  174. // we expect 'name' to be in prototype 'p'
  175. fn = p && p[name];
  176. if(!fn){throw(mp.declaredClass + ': inherited method "' + name + '" not found');}
  177. // if the function exists, invoke it in our scope
  178. return fn.apply(this, a);
  179. }
  180. }
  181. });
  182. }