Created
July 29, 2011 16:45
-
-
Save 7fe/1114201 to your computer and use it in GitHub Desktop.
delimit.htm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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