Skip to content

Instantly share code, notes, and snippets.

@7fe
Created July 29, 2011 16:45
Show Gist options
  • Save 7fe/1114201 to your computer and use it in GitHub Desktop.
Save 7fe/1114201 to your computer and use it in GitHub Desktop.
delimit.htm
<!doctype html>
<script>(function(undefined){
var recentDelimiter,
nativeIndexOf = Array.prototype.indexOf,
hasOwn = Object.prototype.hasOwnProperty,
toString = Object.prototype.toString,
isPrototypeOf = Object.prototype.isPrototypeOf,
prototypeProperty = 'isPrototypeOf',
types = "Number String Function Array Date RegExp Object".split(" "),
class2type = [];
for(var i=0,l=types.length,name;i<l;i++){
name = types[i];
class2type[ "[object " + name + "]" ] = name;
}
var indexOf = function(array, item) {
if (array == undefined) return -1;
var i, l;
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
return -1;
}
var prototypeDelimiter = function(arg){
if((arg===undefined || arg===0 ) && recentDelimiter){
// Allow for shorthand [$](0) chaining
if(arg===0)arg=init(this,recentDelimiter).chain();
else arg=init(this,recentDelimiter);
recentDelimiter = false;
return arg;
}else{
return isPrototypeOf.call(this,arg);
}
}
try{
/*
* Check for Object.defineProperty support
* before adding property to Object.prototype
* IE8 only supports Object.defineProperty on
* DOM Objects
*/
var defineProperty = {
value: prototypeDelimiter,
writable: true,
enumerable: false,
configurable: true
};
if(Object.defineProperty){
var fn = function(){};
Object.defineProperty(fn.prototype,"__delimit__",defineProperty);
for(var i in (new fn)){
if(i==='__delimit__'){
throw '__delimt__ found on fn';
}
}
isPrototypeOf = Object.prototype.__delimiter__ || isPrototypeOf;
Object.defineProperty(Object.prototype,"__delimit__",defineProperty);
prototypeProperty = '__delimit__';
}else{
throw 'Object.defineProperty doesn\' exist';
}
}catch(error){
/*
* Internet Explorer requires workaround to get
* onto Object.prototype chain.
* This page: http://jsbin.com/ehaziv/3 tests
* for `isPrototypeOf` overiding support.
* isPrototypeOf's fuctionality is 100% preserved
* isPrototypeOf is also the least used Object.prototype
* it isn't even ever used in jQuery, Prototype...
*/
Object.prototype.isPrototypeOf = prototypeDelimiter;
}
/*
* Make an Object into a 'delimitertoString'
*/
function delimit(newDelimiter,parentDelimiter){
/*
* Objects and functions are stored as a tree
* on the delimitertoString to allow proper garbage collection
* everything should inherit from fns[0]
*
* ojbs ['Object',obj1,obj2..]
* /
* obj.toString
* \
* fns [ObjectFn,fn1,fn2...]
*/
var delimitertoString = newDelimiter.toString = function(){
recentDelimiter = this;
return prototypeProperty;
};
// ObjectFn
var fn = addFn();
var parentDelimitertoString;
if(parentDelimiter){
parentDelimitertoString = parentDelimiter.toString;
fn.prototype = new parentDelimitertoString.fns[0];
}
delimitertoString.objs =
( parentDelimitertoString && parentDelimitertoString.objs.slice(0) )
|| ['Object'];
delimitertoString.fns = [fn];
var i = 1,obj;
while(obj=delimitertoString.objs[i++]){
var fn = addFn(delimitertoString);
if(parentDelimitertoString){
var parentFn = parentDelimitertoString.fns[i-1].prototype;
for(var i in parentFn){
fn.prototype[i]=parentFn[i];
}
}
}
return newDelimiter;
}
/*
* Add new function to delimitertoString
*/
function addFn(delimitertoString,constructor){
// The current obj is stored on `__` so it will
// appear at the top of the list in console
var fn = function(a){this.__=a;};
/*
* delimitertoString and Obj are Optional
*/
if(constructor){
if(constructor.name){
var i = indexOf(types,constructor.name);
if(-1!=i){
constructor = types[i];
}
}
delimitertoString.objs.push(constructor);
}
if(delimitertoString){
delimitertoString.fns.push(fn);
fn.prototype = new delimitertoString.fns[0];
}
fn.prototype.chain = function(){this.__chain__=true;return this;}
fn.prototype.end = function(){return this.__;}
return fn;
}
/*
* Find the correct function in delimitertoString.fns
* based on an a contsructor or obj
* constructor may also be "Array,String,RegExp,Object,Date,Function"
* findFn(delimitertoString,null,obj) is to allow for cross frame lookup
* of native host objects
*/
function findFn(delimitertoString,constructor,obj){
var i;
// When called from init
if(obj!=undefined){
if(indexOf(types,obj.constructor.name)!=-1){
constructor = class2type[toString.call(obj)]
}else{
constructor = obj.constructor;
}
i = indexOf(delimitertoString.objs,constructor);
}else{
i = indexOf(delimitertoString.objs,constructor.name);
if(i==-1){
indexOf(delimitertoString.objs,constructor);
}
}
return delimitertoString.fns[i];
}
/*
* Each Object,Array,String,etc
* has one fn that stores the
* prototype for the Object
*/
function ret(obj,fn){
return fn.__chain__ ? init(obj,fn.__delimitertoString__).chain() : obj;
}
function addPrototype(delimiter,constructor,name,method){
/*
* To support live inheritance, all prototypes
* added to Object are copied over to each prototype
* and marked with object flag. The flag tells the script
* it is safe to overide later(to simulate inheritance)
* any other protypes added to an Object must also copy
* over all `Object.prototype`s on creation
* This means adding 'Object' prototypes are slighly more
* expensive, but should be used sparingly to begin with
*/
var fn = findFn(delimiter.toString,constructor) || addFn(delimiter.toString,constructor);
fn.prototype[name] = function(){
var apply = method.apply(this.__,arguments);
return apply===undefined?apply:ret(apply,this);
}
}
/*
* runs when delimitertoString is called
* on object example: object[$]()
*/
function init(obj,delimiter){
// fn contains `Object`s proxied prototype
var fn = findFn(delimiter.toString,undefined,obj) || delimiter.toString.fns[0];
var ret = new fn(obj);
// Store reference to delimitertoString in case of chaining
ret.__delimitertoString__ = delimiter.toString;
if(class2type[toString.call(obj)=='Function'] || indexOf(types,obj.name)){
//console.log(delimiter);
ret.fn = function(o){
for(var i in o){
addPrototype(delimiter,obj,i,o[i]);
}
}
}
return ret;
}
window.delimit = delimit;
// Allow for jQuery, Mootools, Zepto, Prototype... to continue to use '$' variable
delimit(window.$ || (window.$ = {}));
})();
Array[$]().fn({
indexOf:function(array){
console.log('indexOf');
}
});
var fn = function(){};
var f = new fn;
fn[$]().fn({
test:function(){
return 'test'
}
});
Object[$]().fn({
log:function(){
console.log(this);
}
});
f
[$]().test()
[$]().log();
var o = {};
delimit(o,$);
Array[o]().fn({
indexOf:function(array){
console.log('indexOf override!');
},
yo:function(){alert('yo')}
});
var array = [1,2,3];
array[o]().indexOf(2);
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment