source: Dev/branches/rest-dojo-ui/client/dojox/lang/tests/declare-old.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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.