source: Dev/trunk/src/client/dojox/lang/tests/declare-old.js @ 532

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

Added Dojo 1.9.3 release.

File size: 9.2 KB
Line 
1dojo.provide("dojox.lang.tests.declare-old");
2
3// the old file copied and modified here for testing
4
5// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
6
7dojox.lang.tests.declareOld = function(/*String*/ className, /*Function|Function[]*/ superclass, /*Object*/ props){
8        // summary:
9        //              Create a feature-rich constructor from compact notation
10        //
11        // description:
12        //              Create a feature-rich constructor from compact notation
13        //
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        //              Declare a class with no ancestors.
40        //      |       dojo.declare("my.ClassyThing", null, {
41        //      |               aProperty:"string",
42        //      |               constructor: function(args){
43        //      |                       dojo.mixin(this, args);
44        //      |               }
45        //      |       });
46        //
47        // example:
48        //              Declare a class inheriting from my.classed.Foo
49        //      |       dojo.declare("my.classes.Bar", my.classes.Foo, {
50        //      |               // properties to be added to the class prototype
51        //      |               someValue: 2,
52        //      |               // initialization function
53        //      |               constructor: function(){
54        //      |                       this.myComplicatedObject = new ReallyComplicatedObject();
55        //      |               },
56        //      |               // other functions
57        //      |               someMethod: function(){
58        //      |                       doStuff();
59        //      |               }
60        //      |       );
61        //
62        // example:
63        //              Declare a class inherting from two mixins, handling multiple constructor args
64        //      |       dojo.declare("my.ComplexMix", [my.BaseClass, my.MixedClass],{
65        //      |               constructor: function(a, b){
66        //      |                       // someone called `new my.ComplexMix("something", "maybesomething");`
67        //      |               }
68        //      |       });
69
70        // process superclass argument
71        var dd = arguments.callee, mixins;
72        if(dojo.isArray(superclass)){
73                mixins = superclass;
74                superclass = mixins.shift();
75        }
76        // construct intermediate classes for mixins
77        if(mixins){
78                dojo.forEach(mixins, function(m, i){
79                        if(!m){ throw(className + ": mixin #" + i + " is null"); } // It's likely a required module is not loaded
80                        superclass = dd._delegate(superclass, m);
81                });
82        }
83        // create constructor
84        var ctor = dd._delegate(superclass);
85        // extend with "props"
86        props = props || {};
87        ctor.extend(props);
88        // more prototype decoration
89        dojo.extend(ctor, { declaredClass: className, _constructor: props.constructor/*, preamble: null*/ });
90        // special help for IE
91        ctor.prototype.constructor = ctor;
92        // create named reference
93        return dojo.setObject(className, ctor); // Function
94};
95
96dojo.mixin(dojox.lang.tests.declareOld, {
97        _delegate: function(base, mixin){
98                var bp = (base || 0).prototype, mp = (mixin || 0).prototype, dd = dojox.lang.tests.declareOld;
99                // fresh constructor, fresh prototype
100                var ctor = dd._makeCtor();
101                // cache ancestry
102                dojo.mixin(ctor, { superclass: bp, mixin: mp, extend: dd._extend });
103                // chain prototypes
104                if(base){ ctor.prototype = dojo._delegate(bp); }
105                // add mixin and core
106                dojo.extend(ctor, dd._core, mp || 0, { _constructor: null, preamble: null });
107                // special help for IE
108                ctor.prototype.constructor = ctor;
109                // name this class for debugging
110                ctor.prototype.declaredClass = (bp || 0).declaredClass + '_' + (mp || 0).declaredClass;
111                return ctor;
112        },
113        _extend: function(props){
114                var i, fn;
115                for(i in props){ if(dojo.isFunction(fn=props[i]) && !0[i]){fn.nom=i;fn.ctor=this;} }
116                dojo.extend(this, props);
117        },
118        _makeCtor: function(){
119                // we have to make a function, but don't want to close over anything
120                return function(){ this._construct(arguments); };
121        },
122        _core: {
123                _construct: function(args){
124                        var c = args.callee, s = c.superclass, ct = s && s.constructor,
125                                m = c.mixin, mct = m && m.constructor, a = args, ii, fn;
126                        // side-effect of = used on purpose here, lint may complain, don't try this at home
127                        if(a[0]){
128                                // FIXME: preambles for each mixin should be allowed
129                                // FIXME:
130                                //              should we allow the preamble here NOT to modify the
131                                //              default args, but instead to act on each mixin
132                                //              independently of the class instance being constructed
133                                //              (for impedence matching)?
134
135                                // allow any first argument w/ a "preamble" property to act as a
136                                // class preamble (not exclusive of the prototype preamble)
137                                if(/*dojo.isFunction*/((fn = a[0].preamble))){
138                                        a = fn.apply(this, a) || a;
139                                }
140                        }
141                        // prototype preamble
142                        if((fn = c.prototype.preamble)){ a = fn.apply(this, a) || a; }
143                        // FIXME:
144                        //              need to provide an optional prototype-settable
145                        //              "_explicitSuper" property which disables this
146                        // initialize superclass
147                        if(ct && ct.apply){ ct.apply(this, a); }
148                        // initialize mixin
149                        if(mct && mct.apply){ mct.apply(this, a); }
150                        // initialize self
151                        if((ii = c.prototype._constructor)){ ii.apply(this, args); }
152                        // post construction
153                        if(this.constructor.prototype == c.prototype && (ct = this.postscript)){ ct.apply(this, args); }
154                },
155                _findMixin: function(mixin){
156                        var c = this.constructor, p, m;
157                        while(c){
158                                p = c.superclass;
159                                m = c.mixin;
160                                if(m == mixin || (m instanceof mixin.constructor)){ return p; }
161                                if(m && m._findMixin && (m = m._findMixin(mixin))){ return m; }
162                                c = p && p.constructor;
163                        }
164                },
165                _findMethod: function(name, method, ptype, has){
166                        // consciously trading readability for bytes and speed in this low-level method
167                        var p=ptype, c, m, f;
168                        do{
169                                c = p.constructor;
170                                m = c.mixin;
171                                // find method by name in our mixin ancestor
172                                if(m && (m = this._findMethod(name, method, m, has))){ return m; }
173                                // if we found a named method that either exactly-is or exactly-is-not 'method'
174                                if((f = p[name]) && (has == (f == method))){ return p; }
175                                // ascend chain
176                                p = c.superclass;
177                        }while(p);
178                        // if we couldn't find an ancestor in our primary chain, try a mixin chain
179                        return !has && (p = this._findMixin(ptype)) && this._findMethod(name, method, p, has);
180                },
181                inherited: function(name, args, newArgs){
182                        // summary:
183                        //              Call an inherited member function of this declared class.
184                        //
185                        // description:
186                        //              Call an inherited member function of this declared class, allowing advanced
187                        //              manipulation of passed arguments to inherited functions.
188                        //              Explicitly cannot handle the case of intending to pass no `newArgs`, though
189                        //              hoping the use in conjuction with `dojo.hitch`. Calling an inherited
190                        //              function directly via hitch() is not supported.
191                        //
192                        // name: String?
193                        //              The name of the method to call. If omitted, the special `arguments` passed is
194                        //              used to determine the inherited function. All subsequent positional arguments
195                        //              are shifted left if `name` has been omitted. (eg: args becomes name)
196                        //
197                        // args: Object
198                        //              An `arguments` object to pass along to the inherited function. Can be in the
199                        //              `name` position if `name` has been omitted. This is a literal JavaScript `arguments`
200                        //              object, and must be passed.
201                        //
202                        // newArgs: Array?
203                        //              An Array of argument values to pass to the inherited function. If omitted,
204                        //              the original arguments are passed (determined from the `args` variable)
205                        //
206                        // example:
207                        //              Simply call an inherited function with the same signature.
208                        //      |       this.inherited(arguments);
209                        // example:
210                        //              Call an inherited method, replacing the arguments passed with "replacement" and "args"
211                        //      |       this.inherited(arguments, [replacement, args]);
212                        // example:
213                        //              Call an inherited method, passing an explicit name.
214                        //      |       this.inherited("method", arguments);
215                        // example:
216                        //              Call an inherited method by name, replacing the arguments:
217                        //      |       this.inherited("method", arguments, [replacement, args]);
218
219                        var a = arguments;
220                        // some magic crap that alters `arguments` to shift in the case of missing `name`
221                        if(typeof a[0] != "string"){ // inline'd type check
222                                newArgs = args;
223                                args = name;
224                                name = args.callee.nom;
225                        }
226                        a = newArgs || args; // WARNING: hitch()ed functions may pass a newArgs you aren't expecting.
227                        var c = args.callee, p = this.constructor.prototype, fn, mp;
228                        // if not an instance override
229                        if(this[name] != c || p[name] == c){
230                                // start from memoized prototype, or
231                                // find a prototype that has property 'name' == 'c'
232                                mp = (c.ctor || 0).superclass || this._findMethod(name, c, p, true);
233                                if(!mp){ throw(this.declaredClass + ': inherited method "' + name + '" mismatch'); }
234                                // find a prototype that has property 'name' != 'c'
235                                p = this._findMethod(name, c, mp, false);
236                        }
237                        // we expect 'name' to be in prototype 'p'
238                        fn = p && p[name];
239                        if(!fn){ throw( mp.declaredClass + ': inherited method "' + name + '" not found'); }
240                        // if the function exists, invoke it in our scope
241                        return fn.apply(this, a);
242                }
243        }
244});
Note: See TracBrowser for help on using the repository browser.