156 lines
No EOL
5.1 KiB
JavaScript
156 lines
No EOL
5.1 KiB
JavaScript
/*
|
|
Copyright (c) 2004-2006, The Dojo Foundation
|
|
All Rights Reserved.
|
|
|
|
Licensed under the Academic Free License version 2.1 or above OR the
|
|
modified BSD license. For more information on Dojo licensing, see:
|
|
|
|
http://dojotoolkit.org/community/licensing.shtml
|
|
*/
|
|
|
|
dojo.provide("dojo.lang.declare");
|
|
|
|
dojo.require("dojo.lang.common");
|
|
dojo.require("dojo.lang.extras");
|
|
|
|
/*
|
|
* Creates a constructor: inherit and extend
|
|
*
|
|
* - inherits from "superclass(es)"
|
|
*
|
|
* "superclass" argument may be a Function, or an array of
|
|
* Functions.
|
|
*
|
|
* If "superclass" is an array, the first element is used
|
|
* as the prototypical ancestor and any following Functions
|
|
* become mixin ancestors.
|
|
*
|
|
* All "superclass(es)" must be Functions (not mere Objects).
|
|
*
|
|
* Using mixin ancestors provides a type of multiple
|
|
* inheritance. Mixin ancestors prototypical
|
|
* properties are copied to the subclass, and any
|
|
* inializater/constructor is invoked.
|
|
*
|
|
* - "props" are copied to the constructor prototype
|
|
*
|
|
* - name of the class ("className" argument) is stored in
|
|
* "declaredClass" property
|
|
*
|
|
* - An initializer function can be specified in the "init"
|
|
* argument, or by including a function called "initializer"
|
|
* in "props".
|
|
*
|
|
* - Superclass methods (inherited methods) can be invoked using "inherited" method:
|
|
*
|
|
* this.inherited(<method name>[, <argument array>]);
|
|
*
|
|
* - inherited will continue up the prototype chain until it finds an implementation of method
|
|
* - nested calls to inherited are supported (i.e. inherited method "A" can succesfully call inherited("A"), and so on)
|
|
*
|
|
* Aliased as "dojo.declare"
|
|
*
|
|
* Usage:
|
|
*
|
|
* dojo.declare("my.classes.bar", my.classes.foo, {
|
|
* initializer: function() {
|
|
* this.myComplicatedObject = new ReallyComplicatedObject();
|
|
* },
|
|
* someValue: 2,
|
|
* aMethod: function() { doStuff(); }
|
|
* });
|
|
*
|
|
*/
|
|
dojo.lang.declare = function(className /*string*/, superclass /*function || array*/, init /*function*/, props /*object*/){
|
|
// FIXME: parameter juggling for backward compat ... deprecate and remove after 0.3.*
|
|
// new sig: (className (string)[, superclass (function || array)[, init (function)][, props (object)]])
|
|
// old sig: (className (string)[, superclass (function || array), props (object), init (function)])
|
|
if ((dojo.lang.isFunction(props))||((!props)&&(!dojo.lang.isFunction(init)))){
|
|
var temp = props;
|
|
props = init;
|
|
init = temp;
|
|
}
|
|
var mixins = [ ];
|
|
if (dojo.lang.isArray(superclass)) {
|
|
mixins = superclass;
|
|
superclass = mixins.shift();
|
|
}
|
|
if(!init){
|
|
init = dojo.evalObjPath(className, false);
|
|
if ((init)&&(!dojo.lang.isFunction(init))){ init = null };
|
|
}
|
|
var ctor = dojo.lang.declare._makeConstructor();
|
|
var scp = (superclass ? superclass.prototype : null);
|
|
if(scp){
|
|
scp.prototyping = true;
|
|
ctor.prototype = new superclass();
|
|
scp.prototyping = false;
|
|
}
|
|
ctor.superclass = scp;
|
|
ctor.mixins = mixins;
|
|
for(var i=0,l=mixins.length; i<l; i++){
|
|
dojo.lang.extend(ctor, mixins[i].prototype);
|
|
}
|
|
ctor.prototype.initializer = null;
|
|
ctor.prototype.declaredClass = className;
|
|
if(dojo.lang.isArray(props)){
|
|
dojo.lang.extend.apply(dojo.lang, [ctor].concat(props));
|
|
}else{
|
|
dojo.lang.extend(ctor, (props)||{});
|
|
}
|
|
dojo.lang.extend(ctor, dojo.lang.declare.base);
|
|
ctor.prototype.constructor = ctor;
|
|
ctor.prototype.initializer=(ctor.prototype.initializer)||(init)||(function(){});
|
|
dojo.lang.setObjPathValue(className, ctor, null, true);
|
|
}
|
|
|
|
dojo.lang.declare._makeConstructor = function() {
|
|
return function(){
|
|
// get the generational context (which object [or prototype] should be constructed)
|
|
var self = this._getPropContext();
|
|
var s = self.constructor.superclass;
|
|
if((s)&&(s.constructor)){
|
|
if(s.constructor==arguments.callee){
|
|
// if this constructor is invoked directly (my.ancestor.call(this))
|
|
this.inherited("constructor", arguments);
|
|
}else{
|
|
this._inherited(s, "constructor", arguments);
|
|
}
|
|
}
|
|
var m = (self.constructor.mixins)||([]);
|
|
for(var i=0,l=m.length; i<l; i++) {
|
|
(((m[i].prototype)&&(m[i].prototype.initializer))||(m[i])).apply(this, arguments);
|
|
}
|
|
if((!this.prototyping)&&(self.initializer)){
|
|
self.initializer.apply(this, arguments);
|
|
}
|
|
}
|
|
}
|
|
|
|
dojo.lang.declare.base = {
|
|
_getPropContext: function() { return (this.___proto||this); },
|
|
// caches ptype context and calls method on it
|
|
_inherited: function(ptype, method, args){
|
|
var stack = this.___proto;
|
|
this.___proto = ptype;
|
|
var result = ptype[method].apply(this,(args||[]));
|
|
this.___proto = stack;
|
|
return result;
|
|
},
|
|
// invokes ctor.prototype.method, with args, in our context
|
|
inheritedFrom: function(ctor, prop, args){
|
|
var p = ((ctor)&&(ctor.prototype)&&(ctor.prototype[prop]));
|
|
return (dojo.lang.isFunction(p) ? p.apply(this, (args||[])) : p);
|
|
},
|
|
// searches backward thru prototype chain to find nearest ancestral instance of prop
|
|
inherited: function(prop, args){
|
|
var p = this._getPropContext();
|
|
do{
|
|
if((!p.constructor)||(!p.constructor.superclass)){return;}
|
|
p = p.constructor.superclass;
|
|
}while(!(prop in p));
|
|
return (dojo.lang.isFunction(p[prop]) ? this._inherited(p, prop, args) : p[prop]);
|
|
}
|
|
}
|
|
|
|
dojo.declare = dojo.lang.declare; |