Created
July 30, 2011 21:07
-
-
Save gtzilla/1115997 to your computer and use it in GitHub Desktop.
RAFT
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
/* | |
* | |
* | |
* file: raft.js | |
* R.A.F.T. | |
* | |
* Request | |
* Animation | |
* Frame | |
* Timer/Template | |
* | |
* author: gregory tomlinson | |
* desc: handy scaffolding for doing continous animations | |
* | |
* the requestion animation frame works very well as a lazy timer | |
* it's cpu cycles are low and it informs the browser the | |
* intention is to change the DOM | |
* | |
* Some Rafting terminology borrowed from | |
* <http://en.wikipedia.org/wiki/Rafting> | |
* | |
* usage: | |
* var animantion=raft(); | |
* // where raft_inst is current instance of raft(); | |
* animation.add( function(data, ts, raft_inst) { | |
* console.log(data.foo, ts); | |
* }, { foo: "bar" }, this); | |
* | |
* | |
* animation.add(function(data,ts,raft_inst) { | |
* console.log(data.foo, ts); | |
* }, { foo: "car" }, this); | |
* | |
* | |
* // start it up | |
* animation.paddle(); | |
* | |
* | |
* // full stop, ensign | |
* animation.stop(); | |
* | |
* | |
* Addtional Declartion Styles / Type / Methods: | |
* | |
* | |
* raft(func, {}, this).paddle(); | |
* // add a single method with data {} and scope 'this' | |
* | |
* raft(func).paddle(); | |
* // data is empty, scope defaults to window | |
* | |
* raft(func, null, this).paddle(); | |
* // data set to empty object, scope set to 'this' | |
* | |
* raft({ | |
* method:func, | |
* data:{}, | |
* scope:this | |
* }).paddle(); | |
* | |
* | |
* Provide a list of methods, format | |
* [{ method:func, data:{}, scope:this }] | |
* | |
* raft([{ | |
* method:function(data,ts) { | |
* }, | |
* data:{ | |
* foo:"bar" | |
* }, | |
* scope:this | |
* },{ | |
* method:function(data,ts) { | |
* }, | |
* data:{ | |
* count:0 | |
* }, | |
* scope:this | |
* }]).paddle(); | |
* | |
* | |
* Pass @param data & @param scope to all methods via raft(); | |
* raft([ | |
* function(data,ts) { data.count+=1 }, | |
* function(data,ts, r_inst ) { data.count+=1; if((ts-data.start_ts)/1000 > 200) { r_inst.stop(); } } | |
* ], { count:0, start_ts:(new Date()).getTime() }, myObj).paddle(); | |
* | |
* | |
* var rr=raft(func,{},this); | |
* var r2 = raft( rr ); | |
* // r2.length === 1 | |
* r2.paddle(); | |
* | |
* Though more handy would be | |
* var rs = raft().splice(0, 0, r1, r2, r3 ); | |
* rs.paddle(); | |
* // where r(1-3) are instances of raft(), each with | |
* // there own list of 'chambers' | |
* | |
* // or inflate can you used like raft( ... ) | |
* raft().inflate([func, func], data, scope) | |
* | |
* | |
* | |
* */ | |
(function(window){ | |
/* | |
* Public method | |
* window.raft(); | |
* | |
* sets up a new raft.prototype.inflate(); | |
* | |
* Build a raft and go for a ride: | |
* raft( ... ).paddle(); | |
* | |
* */ | |
function raft( funcLst, data, scope ) { | |
return new raft.fn.inflate( funcLst, data, scope ); | |
} | |
var push=Array.prototype.push; | |
raft.fn=raft.prototype={ | |
constructor:raft, | |
iterateMethod:"__loopChambers", | |
version:"0.2", | |
allowRun:true, | |
active:false, | |
paddle: function() { | |
// take this raft for a run | |
// paddle, paddle paddle! | |
if(this.active) { | |
return this; | |
} | |
this.allowRun=true; | |
this.active=true; | |
this.surf(); | |
return this; | |
}, | |
/* | |
* Add | |
* @param method func to execute. Receives 3 arguments | |
* 1. data, passed in as param 2. An empty obj | |
* if not specific. | |
* 2. ts - timestamp from animation frame | |
* 3. instance of current Raft | |
* | |
* @param data data, typically obj to be passed to | |
* method in param position 1 | |
* | |
* @param scope An object to execute scope via .call() | |
* | |
* */ | |
add:function(method, data, scope ) { | |
if(!method){ return this; } | |
push.call(this, this.__chamber( method, data, scope )); | |
return this; | |
}, | |
__chamber:function(method,data,scope){ | |
return { | |
"method":method, | |
"data":data, | |
"scope":scope || window | |
} | |
}, | |
remove: function( method ) { | |
var chambers=this, item; | |
for(var i=0; i<chambers.length; i++) { | |
item=chambers[i]; | |
if(item.method === method) { | |
//TODO semi untested | |
chambers.splice(i,1); | |
//this.splice(i,1); | |
} | |
} | |
return this; | |
}, | |
removeAll: function() { | |
Array.prototype.splice.call(this, 0, this.length); | |
}, | |
stop: function() { | |
this.allowRun=false; | |
return this; | |
}, | |
/* | |
* | |
* run the requestionAnimationFrame and callback to | |
* __loopChambers | |
* | |
* */ | |
surf:function() { | |
var self=this, func_name=self.iterateMethod; | |
if(!this.allowRun) { | |
this.active=false; | |
return this; | |
} | |
this.animation_frame.call(window,function(ts){ | |
var result = self[func_name](ts); | |
if(!result) { | |
self.active=false; | |
return; | |
} | |
self.surf(); // continue the loop | |
}); | |
return this; | |
}, | |
/* | |
* | |
* This method is an exception, doesn't return 'this' | |
* | |
* play chambers (this[this.iterateMethod]()) can control | |
* animation on/off | |
* | |
* If all methods return not true, loop will continue. | |
* | |
* If any method returns true, the loop stops. | |
* | |
* In this way, return true is like <raft instance>.stop() | |
* | |
* | |
* */ | |
__loopChambers:function(ts) { | |
var chambers=this, item, | |
results=[], response; | |
chambersloop: for(var i=0, l=chambers.length; i<l; i++) { | |
item=chambers[i]; | |
response=item.method.call( item.scope, item.data||{}, ts, this ); | |
if(response) { | |
// funcs return null (not true) | |
// by default, leverage this | |
// funcs by default will contine the teh loop | |
results.push(i); | |
} | |
} | |
if(results.length > 0) { | |
return false; | |
} | |
// returning true contines the loop | |
// this is different than the methods called | |
// which are attempting to become true | |
// and end the loop | |
return true; | |
}, | |
/* | |
* | |
* Inflate the raft (initalize) | |
* | |
* @param funcLst obj,arr or func | |
* obj={ method:func, data:{}, scope:this||someObj } | |
* arr=[{ method:func, data:{}, scope:this||someObj }] | |
* @param data (optional) | |
* @param scope (optional) | |
* | |
* Inflate is slightly unique. Not only can @param | |
* funcLst, be an obj, array or func. But when | |
* param funcLst is an obj or arr, optional params | |
* are default if not specified. | |
* | |
* raft([{ | |
* method:function(data,ts){}, | |
* data:{ local:true } | |
* },{ | |
* method:function(data,ts){} | |
* }], { global:true }, this||someObj ).paddle(); | |
* | |
* */ | |
inflate: function( funcLst, data, scope ) { | |
var item; | |
this.allowRun=true; | |
this.active=this.active||false; | |
if(funcLst && typeof funcLst === "function") { | |
this.add( funcLst, data, scope ); | |
} else if(funcLst && funcLst.length > 0 ) { | |
for(var i=0, l=funcLst.length; i<l; i++) { | |
item=funcLst[i]; | |
if(typeof item === "function") { | |
this.add( item, data || {}, scope ); | |
} else { | |
this.add( item.method, item.data || data || {}, item.scope || scope ); | |
} | |
} | |
} else if(funcLst && funcLst.method && | |
typeof funcLst.method === "function") { | |
// { method:func, data:{}, scope:this||someObj } | |
this.add(funcLst.method, funcLst.data || data || {}, funcLst.scope || scope ); | |
} | |
return this; | |
}, | |
/* | |
* Manage 'chambers' in raft | |
* | |
* | |
* */ | |
// array-like | |
length:0, | |
/* | |
* | |
* treat lime Array.prototype.slice | |
* | |
* @return raft() instance of raft with sliced chambers attached | |
* | |
* */ | |
slice:function() { | |
// doesn't conform to raft norm. returns raft instance, not this | |
return raft( Array.prototype.slice.apply(this,arguments) ); | |
}, | |
/* | |
* | |
* Works like Array.prototype.splice | |
* | |
* @param start | |
* @param howMany | |
* @param ... added elements | |
* 3rd param and beyond can resemble any format | |
* that inflate can handle | |
* including | |
* .splice(0,1,raft(), raft()) | |
* .splice(0,0,[func, func] | |
* // note scope and data are not assignable in this format | |
* | |
* .splice(0,1, func) | |
* .splice(0, 1, [{method:func, data:{}, scope:window},{ ... }] | |
* | |
* */ | |
splice:function() { | |
// doesnt conform to raft norm, returns raft instances cut | |
var args=Array.prototype.slice.call(arguments,0); | |
var items=[], item; | |
if(args.length > 2) { | |
push.apply(items, args.slice(0,2)); | |
for(var i=2; i<args.length; i++) { | |
if(typeof args[i] === "function") { | |
items.push( this.__chamber( args[i], {} ) ); | |
} else if( args[i] && args[i].length>0 ) { | |
for(var j=0; j<args[i].length; j++) { | |
item=args[i][j]; | |
if(item.method && typeof item.method === "function") { | |
items.push( this.__chamber( item.method, item.data || {}, item.scope ) ); | |
} else if( item && typeof item === "function" ) { | |
items.push( this.__chamber(item, {}) ); | |
} | |
//NOTE deeply nested would fail on this check | |
} | |
} | |
} | |
} else if(args.length===2) { | |
items=args; | |
} | |
return raft( Array.prototype.splice.apply(this, items ) ); | |
} | |
} | |
// make it so new raft.prototype.init can be called and | |
// get all the prototypes of raft | |
raft.fn.inflate.prototype=raft.fn; | |
// setup a generic wrapper to browsers request animation frame | |
raft.fn.animation_frame=(function(){ | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function(callback){ | |
setTimeout(function() { | |
callback(new Date().getTime()); | |
}, 1000 / 60); | |
}; | |
})(); | |
window.raft=raft; | |
})(window); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment