dojox
Version:
Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.
245 lines (237 loc) • 9.45 kB
JavaScript
dojo.provide("dojox.lang.tests.declare-old");
// the old file copied and modified here for testing
// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
dojox.lang.tests.declareOld = function(/*String*/ className, /*Function|Function[]*/ superclass, /*Object*/ props){
// summary:
// Create a feature-rich constructor from compact notation
//
// description:
// Create a feature-rich constructor from compact notation
//
// className:
// The name of the constructor (loosely, a "class")
// stored in the "declaredClass" property in the created prototype
// superclass:
// May be null, a Function, or an Array of Functions. If an array,
// the first element is used as the prototypical ancestor and
// any following Functions become mixin ancestors.
// props:
// An object whose properties are copied to the
// created prototype.
// Add an instance-initialization function by making it a property
// named "constructor".
// description:
// Create a constructor using a compact notation for inheritance and
// prototype extension.
//
// All superclasses (including mixins) must be Functions (not simple Objects).
//
// Mixin ancestors provide a type of multiple inheritance. Prototypes of mixin
// ancestors are copied to the new class: changes to mixin prototypes will
// not affect classes to which they have been mixed in.
//
// "className" is cached in "declaredClass" property of the new class.
//
// example:
// Declare a class with no ancestors.
// | dojo.declare("my.ClassyThing", null, {
// | aProperty:"string",
// | constructor: function(args){
// | dojo.mixin(this, args);
// | }
// | });
//
// example:
// Declare a class inheriting from my.classed.Foo
// | dojo.declare("my.classes.Bar", my.classes.Foo, {
// | // properties to be added to the class prototype
// | someValue: 2,
// | // initialization function
// | constructor: function(){
// | this.myComplicatedObject = new ReallyComplicatedObject();
// | },
// | // other functions
// | someMethod: function(){
// | doStuff();
// | }
// | );
//
// example:
// Declare a class inherting from two mixins, handling multiple constructor args
// | dojo.declare("my.ComplexMix", [my.BaseClass, my.MixedClass],{
// | constructor: function(a, b){
// | // someone called `new my.ComplexMix("something", "maybesomething");`
// | }
// | });
// process superclass argument
var dd = arguments.callee, mixins;
if(dojo.isArray(superclass)){
mixins = superclass;
superclass = mixins.shift();
}
// construct intermediate classes for mixins
if(mixins){
dojo.forEach(mixins, function(m, i){
if(!m){ throw(className + ": mixin #" + i + " is null"); } // It's likely a required module is not loaded
superclass = dd._delegate(superclass, m);
});
}
// create constructor
var ctor = dd._delegate(superclass);
// extend with "props"
props = props || {};
ctor.extend(props);
// more prototype decoration
dojo.extend(ctor, { declaredClass: className, _constructor: props.constructor/*, preamble: null*/ });
// special help for IE
ctor.prototype.constructor = ctor;
// create named reference
return dojo.setObject(className, ctor); // Function
};
dojo.mixin(dojox.lang.tests.declareOld, {
_delegate: function(base, mixin){
var bp = (base || 0).prototype, mp = (mixin || 0).prototype, dd = dojox.lang.tests.declareOld;
// fresh constructor, fresh prototype
var ctor = dd._makeCtor();
// cache ancestry
dojo.mixin(ctor, { superclass: bp, mixin: mp, extend: dd._extend });
// chain prototypes
if(base){ ctor.prototype = dojo._delegate(bp); }
// add mixin and core
dojo.extend(ctor, dd._core, mp || 0, { _constructor: null, preamble: null });
// special help for IE
ctor.prototype.constructor = ctor;
// name this class for debugging
ctor.prototype.declaredClass = (bp || 0).declaredClass + '_' + (mp || 0).declaredClass;
return ctor;
},
_extend: function(props){
var i, fn;
for(i in props){ if(dojo.isFunction(fn=props[i]) && !0[i]){fn.nom=i;fn.ctor=this;} }
dojo.extend(this, props);
},
_makeCtor: function(){
// we have to make a function, but don't want to close over anything
return function(){ this._construct(arguments); };
},
_core: {
_construct: function(args){
var c = args.callee, s = c.superclass, ct = s && s.constructor,
m = c.mixin, mct = m && m.constructor, a = args, ii, fn;
// side-effect of = used on purpose here, lint may complain, don't try this at home
if(a[0]){
// FIXME: preambles for each mixin should be allowed
// FIXME:
// should we allow the preamble here NOT to modify the
// default args, but instead to act on each mixin
// independently of the class instance being constructed
// (for impedence matching)?
// allow any first argument w/ a "preamble" property to act as a
// class preamble (not exclusive of the prototype preamble)
if(/*dojo.isFunction*/((fn = a[0].preamble))){
a = fn.apply(this, a) || a;
}
}
// prototype preamble
if((fn = c.prototype.preamble)){ a = fn.apply(this, a) || a; }
// FIXME:
// need to provide an optional prototype-settable
// "_explicitSuper" property which disables this
// initialize superclass
if(ct && ct.apply){ ct.apply(this, a); }
// initialize mixin
if(mct && mct.apply){ mct.apply(this, a); }
// initialize self
if((ii = c.prototype._constructor)){ ii.apply(this, args); }
// post construction
if(this.constructor.prototype == c.prototype && (ct = this.postscript)){ ct.apply(this, args); }
},
_findMixin: function(mixin){
var c = this.constructor, p, m;
while(c){
p = c.superclass;
m = c.mixin;
if(m == mixin || (m instanceof mixin.constructor)){ return p; }
if(m && m._findMixin && (m = m._findMixin(mixin))){ return m; }
c = p && p.constructor;
}
},
_findMethod: function(name, method, ptype, has){
// consciously trading readability for bytes and speed in this low-level method
var p=ptype, c, m, f;
do{
c = p.constructor;
m = c.mixin;
// find method by name in our mixin ancestor
if(m && (m = this._findMethod(name, method, m, has))){ return m; }
// if we found a named method that either exactly-is or exactly-is-not 'method'
if((f = p[name]) && (has == (f == method))){ return p; }
// ascend chain
p = c.superclass;
}while(p);
// if we couldn't find an ancestor in our primary chain, try a mixin chain
return !has && (p = this._findMixin(ptype)) && this._findMethod(name, method, p, has);
},
inherited: function(name, args, newArgs){
// summary:
// Call an inherited member function of this declared class.
//
// description:
// Call an inherited member function of this declared class, allowing advanced
// manipulation of passed arguments to inherited functions.
// Explicitly cannot handle the case of intending to pass no `newArgs`, though
// hoping the use in conjunction with `dojo.hitch`. Calling an inherited
// function directly via hitch() is not supported.
//
// name: String?
// The name of the method to call. If omitted, the special `arguments` passed is
// used to determine the inherited function. All subsequent positional arguments
// are shifted left if `name` has been omitted. (eg: args becomes name)
//
// args: Object
// An `arguments` object to pass along to the inherited function. Can be in the
// `name` position if `name` has been omitted. This is a literal JavaScript `arguments`
// object, and must be passed.
//
// newArgs: Array?
// An Array of argument values to pass to the inherited function. If omitted,
// the original arguments are passed (determined from the `args` variable)
//
// example:
// Simply call an inherited function with the same signature.
// | this.inherited(arguments);
// example:
// Call an inherited method, replacing the arguments passed with "replacement" and "args"
// | this.inherited(arguments, [replacement, args]);
// example:
// Call an inherited method, passing an explicit name.
// | this.inherited("method", arguments);
// example:
// Call an inherited method by name, replacing the arguments:
// | this.inherited("method", arguments, [replacement, args]);
var a = arguments;
// some magic crap that alters `arguments` to shift in the case of missing `name`
if(typeof a[0] != "string"){ // inline'd type check
newArgs = args;
args = name;
name = args.callee.nom;
}
a = newArgs || args; // WARNING: hitch()ed functions may pass a newArgs you aren't expecting.
var c = args.callee, p = this.constructor.prototype, fn, mp;
// if not an instance override
if(this[name] != c || p[name] == c){
// start from memoized prototype, or
// find a prototype that has property 'name' == 'c'
mp = (c.ctor || 0).superclass || this._findMethod(name, c, p, true);
if(!mp){ throw(this.declaredClass + ': inherited method "' + name + '" mismatch'); }
// find a prototype that has property 'name' != 'c'
p = this._findMethod(name, c, mp, false);
}
// we expect 'name' to be in prototype 'p'
fn = p && p[name];
if(!fn){ throw( mp.declaredClass + ': inherited method "' + name + '" not found'); }
// if the function exists, invoke it in our scope
return fn.apply(this, a);
}
}
});