Skip to content

Commit

Permalink
significant refactor and use dojo class extension. implement some of …
Browse files Browse the repository at this point in the history
…the jquery examples
  • Loading branch information
Tony Issakov committed Jan 29, 2010
1 parent 3172781 commit c232035
Show file tree
Hide file tree
Showing 31 changed files with 989 additions and 208 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
.DS_Store
189 changes: 189 additions & 0 deletions lib/declare.js
@@ -0,0 +1,189 @@
$e.delegate = $e._delegate = (function(){
// boodman/crockford delegation w/ cornford optimization
function TMP(){}
return function(obj, props){
TMP.prototype = obj;
var tmp = new TMP();
TMP.prototype = null;
if(props){
$e.mixin(tmp, props);
}
return tmp; // Object
}
})();

$e.declare = function(/*String*/ className, /*Function|Function[]*/ superclass, /*Object*/ props){
var dd = arguments.callee, mixins;
if(jQuery.isArray(superclass)){
mixins = superclass;
superclass = mixins.shift();
}
// construct intermediate classes for mixins
if(mixins){
jQuery.each(mixins, function(i, m){
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
$e.extend(ctor, { declaredClass: className, _constructor: props.constructor/*, preamble: null*/ });
// special help for IE
ctor.prototype.constructor = ctor;
// create named reference
return $e.setObject(className, ctor);
};

$e.mixin($e.declare, {
_delegate: function(base, mixin){
var bp = (base || 0).prototype, mp = (mixin || 0).prototype, dd = $e.declare;
// fresh constructor, fresh prototype
var ctor = dd._makeCtor();
// cache ancestry
$e.mixin(ctor, { superclass: bp, mixin: mp, extend: dd._extend });
// chain prototypes
if(base){ ctor.prototype = $e._delegate(bp); }
// add mixin and core
$e.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(jQuery.isFunction(fn=props[i]) && !0[i]){fn.nom=i;fn.ctor=this;} }
$e.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 conjuction 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);
}
}
});

0 comments on commit c232035

Please sign in to comment.