Skip to content

Instantly share code, notes, and snippets.

@appden
Created May 4, 2009 02:47
Show Gist options
  • Save appden/106277 to your computer and use it in GitHub Desktop.
Save appden/106277 to your computer and use it in GitHub Desktop.
// FUNCTION BINDING
Function.prototype.bind = function(bind, args){
var self = this.unbind();
if (args != null) args = $splat(args);
var fn = function(){
return self.apply(bind, args || arguments);
};
fn._bound_ = self;
return fn;
};
Function.prototype.unbind = function(){
return (this._bound_) ? this._bound_.unbind() : this;
};
Function.prototype.unbound = function(){
this._unbound_ = true;
return this;
};
// ACCESSOR -- NOW WITH ABILITY TO PASS IN PRIVATE OBJECT
this.Accessor = new Native('Accessor', function(name, accessor){
var Name = (name || '').capitalize();
accessor = accessor || {};
this['define' + Name] = function(key, value){
accessor[key] = value;
return this;
}.setMany();
this['lookup' + Name] = function(key, value){
return accessor.hasOwnProperty(key) ? accessor[key] : null;
}.getMany();
});
// MODIFIED CLASS
(function(){
this.Class = new Native('Class', function(params){
if (instanceOf(params, Function)) params = {initialize: params};
var newClass = function(){
resetAll(this);
if (newClass._prototyping_) return this;
// POST INIT MUTATORS
for (var modifier in initializers){
if (this[modifier]) this[modifier] = initializers[modifier].call(this, this[modifier]);
}
// WRAP METHODS AND AUTO-BIND
for (var method in this){
if (this[method]._wrap_) this[method] = wrap.call(this, newClass, method, this[method]);
}
this._current_ = nil;
var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
this._current_ = this.caller = null;
return value;
}.extend(this);
// APPLY CLASS MUTATORS
for (var mutator in mutators){
if (!params[mutator]) continue;
params[mutator] = mutators[mutator].call(newClass, params[mutator], params);
if (params[mutator] == null) delete params[mutator];
}
newClass.implement(params);
newClass.constructor = Class;
newClass.prototype.constructor = newClass;
return newClass;
});
var resetOne = function(object, key){
delete object[key];
switch (typeOf(object[key])){
case 'object':
var F = function(){};
F.prototype = object[key];
var instance = new F;
object[key] = resetAll(instance);
break;
case 'array': object[key] = Object.clone(object[key]); break;
}
return object;
};
var resetAll = function(object){
for (var p in object) resetOne(object, p);
return object;
};
var getPrototype = function(klass){
klass._prototyping_ = true;
var proto = new klass;
delete klass._prototyping_;
return proto;
};
var wrap = function(klass, key, method){
var self = this;
if (method._origin_) method = method._origin_;
return function(){
if (method._protected_ && self._current_ == null) throw new Error('The method "' + key + '" cannot be called.');
var caller = self.caller, current = self._current_;
self.caller = current; self._current_ = arguments.callee;
// IF METHOD MARKED UNBOUND, DO NOT BIND
var result = method.apply(method._unbound_ ? this : self, arguments);
self._current_ = current; self.caller = caller;
return result;
}.extend({_owner_: klass, _origin_: method, _name_: key});
};
// NOW TAKES A CLASS AS AN ARGUMENT
Class.implement('implement', function(key, value){
if (typeof key != 'string'){
if (instanceOf(key, Function)) key = getPrototype(key);
for (var prop in key) this.implement(prop, key[prop]);
return this;
}
var proto = this.prototype;
switch (typeOf(value)){
case 'function':
if (value._hidden_) return this;
value._wrap_ = true; // MARK FOR WRAPPING ON INIT
proto[key] = value;
// proto[key] = wrap(this, key, value);
break;
case 'object':
var previous = proto[key];
if (typeOf(previous) == 'object') Object.merge(previous, value);
else proto[key] = Object.clone(value);
break;
case 'array':
proto[key] = Object.clone(value);
break;
default: proto[key] = value;
}
return this;
});
// PRIVATE TO THIS CLOSURE, ACCESSIBLE TO OUTSIDE WORLD THRU ACCESSORS
var mutators = {
Extends: function(parent){
this.parent = parent;
this.prototype = getPrototype(parent);
if (this.prototype.parent == null) this.implement('parent', function(){
var name = this.caller._name_, previous = this.caller._owner_.parent.prototype[name];
if (!previous) throw new Error('The method "' + name + '" has no parent.');
return previous.apply(this, arguments);
}.protect());
},
// TEST FOR PASSING IN OTHER CLASSES NOW INSIDE IMPLEMENT METHOD
Implements: function(items){
Array.from(items).forEach(function(item){
this.implement(item);
}, this);
}
};
Class.extend(new Accessor('Mutator', mutators));
// POST INIT MUTATORS
var initializers = {};
Class.extend(new Accessor('Initializer', initializers));
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment