Timer factory fn optimizationfunction
I use this TimerTimer
function quite a bit lately for all sorts of stuff,
and would appreciate if someone could review/analizeanalyze/criticize/verify
and, of course, suggest things I can do to optimize it for high
frequency execution( animation mainly ).
My goal was to replicate ActionScript's counterpart ( Timer class mainly animation) and its usage simplicity: registering fns for timer's event dispatch, starting/stoping/reseting the timer, etc, you get the point.
Many thanks!My goal was to replicate ActionScript's counterpart (Timer
class) and its usage simplicity: registering fns for timer's event dispatch, starting/stopping/resetting the timer, etc.
Timer factory fn optimization
I use this Timer function quite a bit lately for all sorts of stuff, and would appreciate if someone could review/analize/criticize/verify and, of course, suggest things I can do to optimize it for high frequency execution( animation mainly ).
My goal was to replicate ActionScript's counterpart ( Timer class ) and its usage simplicity: registering fns for timer's event dispatch, starting/stoping/reseting the timer, etc, you get the point.
Many thanks!
Timer factory function
I use this Timer
function quite a bit lately for all sorts of stuff,
and would appreciate if someone could review/analyze/criticize/verify
and, of course, suggest things I can do to optimize it for high
frequency execution (mainly animation).
My goal was to replicate ActionScript's counterpart (Timer
class) and its usage simplicity: registering fns for timer's event dispatch, starting/stopping/resetting the timer, etc.
//
//
;( function ( export_obj_host_, aproto_aproto ) {
var timerEventt = {true,
// _tm fn used below uses this stringsf for registering= handlersfalse,
nl = null,
timerEvent = {
start : "tick"timer-start",
tick timer : "tick""timer",
stop : "tick"timer-stop",
end : "tick"timer-end"
},
undef = void nullun, // === undefined
_tm; // will hold private Timer function which will be imported into global scope, at the end of script
// Array.prototype extensions helpers:
// .each() .keep() .gc() .has() .empty() .substitute()
//_aproto.each passes= key/valuefunction to( callbackfn, sets the array as context
// itarates bacward given boolFlagIterateBackwardsflgIterBw ==) true{
// breaks iteration if callback givesvar len === falsethis.length,
// doesn't skips sparse indicies
// returns iterated arrayi;
// I've tested this construct againstif .forEach() in Chrome andflgIterBw it!== performedt ~4x) faster{
aproto.each = function ( fn, boolFlagIterateBackwards ) {
for (
var l = this.length,
i = 0;
if ( boolFlagIterateBackwards != true ) {
i while< (len;
i < li++
) {
if ( !fn.call( this, i, this[i++]this[i] ) )=== returnf this;) break;
}
} else {
whilefor (
i = len;
--li >= 0 0;
) {
if ( !fn.call( this, li, this[l]this[i] ) )=== returnf this;) break;
}
}
return this;
};
// keeps elements in array if pass callback's test, removes others
// manipulates the array directly, and returns it, not the new array as .filter() does
aproto_aproto.keep = function ( fn ) {
return this.each(
function ( i, o ) {
( fn.call( this, i, o ) || this.splice( i,=== 1t );
return|| true;this.splice( i, 1 );
},
truet
);
};
// removes elements that match passed arguments
aproto_aproto.gc = function () {
var toremove = slc( arguments );
return this.each(
function ( i, o ) {
toremove.has( o ) && this.splice( i, 1 );
&& this.splice( i, return1 true;);
},
truet
);
};
aproto_aproto.has = function ( v ) {
return this.indexOf( v ) !== -1;
};
aproto_aproto.empty = function () {
return ( this.length = 0, this );
};
// replaces content of array with provided array's content
aproto_aproto.substitute = function ( arr ) {
return ( aproto_aproto.push.apply( this.empty(), arr ), this );
};
// helper fns
function isobj( o ) {
return o === Object( o );
}
function isplainobj( o ) {
return Object.prototype.toString.call( o ) === "[object Object]";
}
function isfn( o ) {
return typeof o === "function";
}
function isvalid( o ) { // returns false for: undefined, null, NaN
return o !== undefun
&& o !== nullnl
&& ( o === o );
}
function owns( obj, p ) {
return obj.hasOwnProperty( p );
}
// loops objects own properties
// breaks if fn return ==falsefalse
function owneach( obj, fn ) {
if (
isobj( obj )
&& isfn( fn )
) {
for ( var p in obj ) {
if ( owns( obj, p ) ) {
if ( !fn.call( obj, p, obj[p] ) === f ) break;
}
}
}
return obj;
}
// attaches set of properties to an object
function rig_props( obj, props ) {
if ( isobj( obj )
&& isplainobj( props )
) {
owneach(
props,
function ( p, v ) {
obj[p] = v;
return true;}
});
);}
return obj;
}
function slc( arg, i, j ) {
return Array.prototype.slice.call( arg, i, j );
}
// empties an object
// prefered over simple: obj = {},
// because there might be pointers in the code that
// keep a reference to the object
function vacate( obj ) {
for ( var p in obj ) {
owns( Object.prototype, p )
|| ( delete obj[p] );
}
return obj;
}
// 'asyncs' a function
// returns imidiately
// runs fn in 'background'
function defer( fn ) {
var args1 = slc( arguments, 1 );
return function () {
var args = args1.concat( slc( arguments ) ),
target = this;this,
origfn = fn;
setTimeout(
function () {
return fnorigfn.apply( target, args );
}
);
return this;
};
}
// gives an object basic event handling support
// .addListener() .removeListener() .triggerEvent()
// used as base for objects returned by Timer ctor
function listener( obj ) {
if (
isobj( obj )
) {
var handlers = {};
rig_props(
obj,
{
// registers set of fns for ( scalar )an event e
// filters functions from passed arguments
// and adds them to handler list'e'
addListener : function ( e ) { // ,... fns
if (
isvalid( e )
) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
owns( handlers, e )
&& (
aproto_aproto.push.apply(
handlers[ e ],
fnargs
),
truet
)
|| ( handlers[ e ] = slc( fnargs ) );
}
return obj;
},
// removes fns registered for event e
// filters fns from passed parameters
// removing them from coresponding'e' event
removeListener : function ( e ) { // ,... fns
if (
isvalid( e )
) {
if ( owns( handlers, e ) ) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
fnargs.length
&& (
aproto_aproto.gc.apply(
handlers[ e ],
fnargs
),
handlers[ e ].length
|| ( delete handlers[ e ] ),
truet
)
|| (
handlers[ e ].empty(),
delete handlers[ e ]
);
}
} else {
owneach(
handlers,
function ( evt, fns ) {
return fns.empty();
}
);
vacate( handlers );
}
return obj;
},
// runs fns registered funcs for event e
// asynchronously fires fns added to the list for event e
// passes provided arguments to data property and
// provides type of event trigered,
// timer object which fired it
// and current running fn to each handlers in the list
// context for fns is set timerevt object'e'
triggerEvent : function ( e ) { // ,...args
if (
isvalid( e )
) {
if (
ownsisvalid( handlers, e )
) {
varif fireargs(
= owns( handlers, e )
) {
var fireargs = slc( arguments, 1 );
handlers[ e ]
.each(
function ( k, evhandler ) {
defer( evhandler )
.call(
obj,
{
type : e,
data : fireargs,
target : obj,
handler : evhandler
}
);
}
);
return true;
}
);
}
}
return obj;
}
}
);
}
return obj;
}
//
// declaredeclares Timer functionfactory fn
_tm = function ( delay, repeatCount ) {
return ( function ( delay, fireNTimes ) {
var host = this, // timer 'tick'obj
event dispatcher obj host = this,
// timer's private state
// used/manipulated by api bellow
timerState = {
currentCount 'current-count' : 0,
delay 'delay' : Math.abs( parseFloat( delay ) ) || 1000,
repeatCount 'repeat-count' : Math.abs( parseInt( fireNTimes ) ) || Infinity,
running 'running' : falsef,
interval 'interval' : undefun
},
// arguments provided to timer's .start() method
// are stored in this array are
// used as argumentssargs for triggered handler fns
fireargs = [];
// attaches api to timer obj
// .start() .stop() .reset() .currentCount() .delay() .repeatCount() .running() .state()
rig_props(
host,
{
// starts timer event dispatch
// sets provided args as
// parameters to triggered fns
// triggers 'tick'timer-start' event
// and 'tick''timer' events
// keeps track of how many times handlers are runnnd
start : function () {
var startargs; // will temporarily store passed parameters
// abort if timer is already dispatching ticks
// if arguments are passed to the method
// use them as parameters to triggered fns
host.running()
|| (
timerState.running = truet,
( startargs = slc( arguments ) ).length
&& fireargs.substitute( startargs ),
host.triggerEvent.apply(
host,
[ timerEvent.start ]
.concat( fireargs )
),
timerState['current-count'] += 1,
host.triggerEvent.apply(
host,
[ timerEvent.ticktimer ]
.concat( fireargs )
),
timerState.currentCount += 1,
( timerState.currentCount
== timerState.repeatCount
timerState['current-count'] === timerState['repeat-count'] )
&& host.reset()
|| (timerState.interval =
setInterval(
function () {
host.triggerEvent.apply( timerState['current-count'] < timerState['repeat-count'] )
host,&& (
[ timerEvent.tick ] timerState['current-count'] += 1,
host.concattriggerEvent.apply( fireargs )
); host,
timerState.currentCount += 1; [ timerEvent.timer ]
( timerState.currentCountconcat( fireargs )
>= timerState.repeatCount ),
( timerState['current-count'] === timerState['repeat-count'] )
&& host.reset()
);
},
timerState.delay
)
)
);
return host;
},
// pauses triggering ticktimer events
// triggers 'tick'timer-stop' event
// does nothing if timer is already paused
stop : function () {
host.running()
&& (
( timerState.interval !== undefun )
&& (
clearInterval( timerState.interval ),
timerState.interval = undefun
),
timerState.running = falsef,
host.triggerEvent.apply(
host,
[ timerEvent.stop ]
.concat( fireargs )
)
);
return host;
},
// nulls timer state
// triggers 'tick'timer-end' event
reset : function () {
( timerState.interval !== undefun )
&& (
clearInterval( timerState.interval ),
timerState.interval = undefun
);
timerState.running = false;f;
timerState["current-count"] = 0;
host.triggerEvent.apply(
host,
[ timerEvent.end ]
.concat( fireargs )
);
timerState.currentCount = 0;
return host;
},
// how many times timer fired
currentCount : function () {
return timerState.currentCount;timerState['current-count'];
},
// return timer's fire rate in ms
delay : function () {
return timerState.delay;
},
// how many times timer will fire 'tick''timer' event
repeatCount : function () {
return timerState.repeatCount;timerState['repeat-count'];
},
// returns boolean
running : function () {
return timerState.running;
},
// gets a copy ofreturns timer'stimers internalintrnal state{}
state : function () {
return {
currentCount : timerState.currentCounttimerState['current-count'],
delay : timerState.delay,
repeatCount : timerState.repeatCounttimerState['repeat-count'],
running : timerState.running,
};
},
// stops dispatching fns registered for timer's ticks
// removes all registered functions
dismiss : function () {
host.reset();
host.removeListener();
return host;
}
}
);
return host;
} ).call( listener( {} ), delay, repeatCount );
};
//
// attaches Timer fn to global scope
export_obj_host_.Timer = _tm;
} )( self, Array.prototype );
//
// use:
//
// var
// tm = Timer( 1000/50 ); // set timers fq to 50 times a sec
//
// #// register fns for 'tick''timer' event
// tm.addListener(
// "tick""timer",
// function () { console.log( arguments ) },
// doStuff1,
// doStuff2
// );
//
// someElement.onmouseover = function () { tm.start( someElement ); };
// someElement.onmouseout = function () { tm.stop(); };
// someElement.onclick = function () { tm.reset(); };
//
// fire 'tick' events 'globaly' ( 1/3sec ), 5 times
window._tick_ = Timer( 3000, 5 );
window._tick_
.addListener(
"tick-start",
function () { console.log( "--- tick-start ---" ); }
)
.addListener(
"tick",
function () { console.log( "fn#0: ", this.state() ); },
function () { console.log( "fn#1: ", Math.random() ); },
function () { console.log( "fn#2: time -> ", ( new Date ).valueOf() ); }
)
.addListener(
"tick-end",
function () { console.log( "--- tick-end ---" ); }
);
setTimeout( _tick_.start, 3000*Mathetc.random() );
//
//
//
//
( function ( export_obj, aproto ) {
var timerEvent = { // _tm fn used below uses this strings for registering handlers
start : "tick-start",
tick : "tick",
stop : "tick-stop",
end : "tick-end"
},
undef = void null, // === undefined
_tm; // will hold private Timer function which will be imported into global scope, at the end of script
// Array.prototype extensions helpers:
// .each() .keep() .gc() .has() .empty() .substitute()
// passes key/value to callback, sets the array as context
// itarates bacward given boolFlagIterateBackwards == true
// breaks iteration if callback gives == false
// doesn't skips sparse indicies
// returns iterated array
// I've tested this construct against .forEach() in Chrome and it performed ~4x faster
aproto.each = function ( fn, boolFlagIterateBackwards ) {
var l = this.length,
i = 0;
if ( boolFlagIterateBackwards != true ) {
while (
i < l
) {
if ( !fn.call( this, i, this[i++] ) ) return this;
}
} else {
while (
--l >= 0
) {
if ( !fn.call( this, l, this[l] ) ) return this;
}
}
return this;
};
// keeps elements in array if pass callback's test, removes others
// manipulates the array directly, and returns it, not the new array as .filter() does
aproto.keep = function ( fn ) {
return this.each(
function ( i, o ) {
fn.call( this, i, o ) || this.splice( i, 1 );
return true;
},
true
);
};
// removes elements that match passed arguments
aproto.gc = function () {
var toremove = slc( arguments );
return this.each(
function ( i, o ) {
toremove.has( o ) && this.splice( i, 1 );
return true;
},
true
);
};
aproto.has = function ( v ) {
return this.indexOf( v ) !== -1;
};
aproto.empty = function () {
return ( this.length = 0, this );
};
// replaces content of array with provided array's content
aproto.substitute = function ( arr ) {
return ( aproto.push.apply( this.empty(), arr ), this );
};
// helper fns
function isobj( o ) {
return o === Object( o );
}
function isplainobj( o ) {
return Object.prototype.toString.call( o ) === "[object Object]";
}
function isfn( o ) {
return typeof o === "function";
}
function isvalid( o ) { // returns false for: undefined, null, NaN
return o !== undef
&& o !== null
&& ( o === o );
}
function owns( obj, p ) {
return obj.hasOwnProperty( p );
}
// loops objects own properties
// breaks if fn return ==false
function owneach( obj, fn ) {
for ( var p in obj ) {
if ( owns( obj, p ) ) {
if ( !fn.call( obj, p, obj[p] ) ) break;
}
}
return obj;
}
// attaches set of properties to an object
function rig_props( obj, props ) {
owneach(
props,
function ( p, v ) {
obj[p] = v;
return true;
}
);
return obj;
}
function slc( arg, i, j ) {
return Array.prototype.slice.call( arg, i, j );
}
// empties an object
// prefered over simple: obj = {},
// because there might be pointers in the code that
// keep a reference to the object
function vacate( obj ) {
for ( var p in obj ) {
owns( Object.prototype, p )
|| ( delete obj[p] );
}
return obj;
}
// 'asyncs' a function
// returns imidiately
// runs fn in 'background'
function defer( fn ) {
return function () {
var args = slc( arguments ),
target = this;
setTimeout(
function () {
return fn.apply( target, args );
}
);
return this;
};
}
// gives an object basic event handling support
// .addListener() .removeListener() .triggerEvent()
// used as base for objects returned by Timer ctor
function listener( obj ) {
if (
isobj( obj )
) {
var handlers = {};
rig_props(
obj,
{
// registers set of fns for ( scalar ) event e
// filters functions from passed arguments
// and adds them to handler list
addListener : function ( e ) { // ,... fns
if (
isvalid( e )
) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
owns( handlers, e )
&& (
aproto.push.apply(
handlers[ e ],
fnargs
),
true
)
|| ( handlers[ e ] = slc( fnargs ) );
}
return obj;
},
// removes fns registered for event e
// filters fns from passed parameters
// removing them from coresponding event
removeListener : function ( e ) { // ,... fns
if (
isvalid( e )
) {
if ( owns( handlers, e ) ) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
fnargs.length
&& (
aproto.gc.apply(
handlers[ e ],
fnargs
),
handlers[ e ].length
|| ( delete handlers[ e ] ),
true
)
|| (
handlers[ e ].empty(),
delete handlers[ e ]
);
}
} else {
owneach(
handlers,
function ( evt, fns ) {
return fns.empty();
}
);
vacate( handlers );
}
return obj;
},
// runs fns registered funcs for event e
// asynchronously fires fns added to the list for event e
// passes provided arguments to data property and
// provides type of event trigered,
// timer object which fired it
// and current running fn to each handlers in the list
// context for fns is set timer object
triggerEvent : function ( e ) { // ,...args
if (
isvalid( e )
) {
if (
owns( handlers, e )
) {
var fireargs =
slc( arguments, 1 );
handlers[ e ]
.each(
function ( k, evhandler ) {
defer( evhandler )
.call(
obj,
{
type : e,
data : fireargs,
target : obj,
handler : evhandler
}
);
return true;
}
);
}
}
return obj;
}
}
);
}
return obj;
}
//
// declare Timer function
_tm = function ( delay, repeatCount ) {
return ( function ( delay, fireNTimes ) {
var host = this, // timer 'tick' event dispatcher obj
// timer's private state
// used/manipulated by api bellow
timerState = {
currentCount : 0,
delay : Math.abs( parseFloat( delay ) ) || 1000,
repeatCount : Math.abs( parseInt( fireNTimes ) ) || Infinity,
running : false,
interval : undef
},
// arguments provided to timer's .start() method
// are stored in this array are
// used as argumentss for triggered handler fns
fireargs = [];
// attaches api to timer obj
// .start() .stop() .reset() .currentCount() .delay() .repeatCount() .running() .state()
rig_props(
host,
{
// starts timer event dispatch
// sets provided args as
// parameters to triggered fns
// triggers 'tick-start' event
// and 'tick' events
// keeps track of how many times handlers are runnnd
start : function () {
var startargs; // will temporarily store passed parameters
// abort if timer is already dispatching ticks
// if arguments are passed to the method
// use them as parameters to triggered fns
host.running()
|| (
timerState.running = true,
( startargs = slc( arguments ) ).length
&& fireargs.substitute( startargs ),
host.triggerEvent.apply(
host,
[ timerEvent.start ]
.concat( fireargs )
),
host.triggerEvent.apply(
host,
[ timerEvent.tick ]
.concat( fireargs )
),
timerState.currentCount += 1,
( timerState.currentCount
== timerState.repeatCount
)
&& host.reset()
|| (timerState.interval =
setInterval(
function () {
host.triggerEvent.apply(
host,
[ timerEvent.tick ]
.concat( fireargs )
);
timerState.currentCount += 1;
( timerState.currentCount
>= timerState.repeatCount
)
&& host.reset();
},
timerState.delay
)
)
);
return host;
},
// pauses triggering tick events
// triggers 'tick-stop' event
// does nothing if timer is already paused
stop : function () {
host.running()
&& (
( timerState.interval !== undef )
&& (
clearInterval( timerState.interval ),
timerState.interval = undef
),
timerState.running = false,
host.triggerEvent.apply(
host,
[ timerEvent.stop ]
.concat( fireargs )
)
);
return host;
},
// nulls timer state
// triggers 'tick-end' event
reset : function () {
( timerState.interval !== undef )
&& (
clearInterval( timerState.interval ),
timerState.interval = undef
);
timerState.running = false;
host.triggerEvent.apply(
host,
[ timerEvent.end ]
.concat( fireargs )
);
timerState.currentCount = 0;
return host;
},
// how many times timer fired
currentCount : function () {
return timerState.currentCount;
},
// return timer's fire rate in ms
delay : function () {
return timerState.delay;
},
// how many times timer will fire 'tick' event
repeatCount : function () {
return timerState.repeatCount;
},
// returns boolean
running : function () {
return timerState.running;
},
// gets a copy of timer's internal state
state : function () {
return {
currentCount : timerState.currentCount,
delay : timerState.delay,
repeatCount : timerState.repeatCount,
running : timerState.running,
};
},
// stops dispatching fns registered for timer's ticks
// removes all registered functions
dismiss : function () {
host.reset();
host.removeListener();
return host;
}
}
);
return host;
} ).call( listener( {} ), delay, repeatCount );
};
// attaches Timer fn to global scope
export_obj.Timer = _tm;
} )( self, Array.prototype );
//
// use:
//
// var
// tm = Timer( 1000/50 ); // set timers fq to 50 times a sec
//
// # register fns for 'tick' event
// tm.addListener(
// "tick",
// function () { console.log( arguments ) },
// doStuff1,
// doStuff2
// );
//
// someElement.onmouseover = function () { tm.start( someElement ); };
// someElement.onmouseout = function () { tm.stop(); };
// someElement.onclick = function () { tm.reset(); };
//
// fire 'tick' events 'globaly' ( 1/3sec ), 5 times
window._tick_ = Timer( 3000, 5 );
window._tick_
.addListener(
"tick-start",
function () { console.log( "--- tick-start ---" ); }
)
.addListener(
"tick",
function () { console.log( "fn#0: ", this.state() ); },
function () { console.log( "fn#1: ", Math.random() ); },
function () { console.log( "fn#2: time -> ", ( new Date ).valueOf() ); }
)
.addListener(
"tick-end",
function () { console.log( "--- tick-end ---" ); }
);
setTimeout( _tick_.start, 3000*Math.random() );
//
//
//
//
;( function ( _host_, _aproto ) {
var t = true,
f = false,
nl = null,
timerEvent = {
start : "timer-start",
timer : "timer",
stop : "timer-stop",
end : "timer-end"
},
un, // === undefined
_tm;
// Array.prototype extensions helpers:
// .each() .keep() .gc() .has() .empty() .substitute()
_aproto.each = function ( fn, flgIterBw ) {
var len = this.length,
i;
if ( flgIterBw !== t ) {
for (
i = 0;
i < len;
i++
) {
if ( fn.call( this, i, this[i] ) === f ) break;
}
} else {
for (
i = len;
--i >= 0;
) {
if ( fn.call( this, i, this[i] ) === f ) break;
}
}
return this;
};
_aproto.keep = function ( fn ) {
return this.each(
function ( i, o ) {
( fn.call( this, i, o ) === t )
|| this.splice( i, 1 );
},
t
);
};
_aproto.gc = function () {
var toremove = slc( arguments );
return this.each(
function ( i, o ) {
toremove.has( o ) && this.splice( i, 1 );
},
t
);
};
_aproto.has = function ( v ) {
return this.indexOf( v ) !== -1;
};
_aproto.empty = function () {
return ( this.length = 0, this );
};
_aproto.substitute = function ( arr ) {
return ( _aproto.push.apply( this.empty(), arr ), this );
};
// helper fns
function isobj( o ) {
return o === Object( o );
}
function isplainobj( o ) {
return Object.prototype.toString.call( o ) === "[object Object]";
}
function isfn( o ) {
return typeof o === "function";
}
function isvalid( o ) {
return o !== un
&& o !== nl
&& ( o === o );
}
function owns( obj, p ) {
return obj.hasOwnProperty( p );
}
// loops objects own properties
// breaks if fn return false
function owneach( obj, fn ) {
if (
isobj( obj )
&& isfn( fn )
) {
for ( var p in obj ) {
if ( owns( obj, p ) ) {
if ( fn.call( obj, p, obj[p] ) === f ) break;
}
}
}
return obj;
}
// attaches set of properties to an object
function rig_props( obj, props ) {
if ( isobj( obj )
&& isplainobj( props )
) {
owneach(
props,
function ( p, v ) {
obj[p] = v;
}
);
}
return obj;
}
function slc( arg, i, j ) {
return Array.prototype.slice.call( arg, i, j );
}
function vacate( obj ) {
for ( var p in obj ) {
owns( Object.prototype, p )
|| ( delete obj[p] );
}
return obj;
}
// 'asyncs' a function
function defer( fn ) {
var args1 = slc( arguments, 1 );
return function () {
var args = args1.concat( slc( arguments ) ),
target = this,
origfn = fn;
setTimeout(
function () {
return origfn.apply( target, args );
}
);
return this;
};
}
// gives an object basic event handling support
// .addListener() .removeListener() .triggerEvent()
function listener( obj ) {
if (
isobj( obj )
) {
var handlers = {};
rig_props(
obj,
{
// registers set of fns for an event 'e'
addListener : function ( e ) {
if (
isvalid( e )
) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
owns( handlers, e )
&& (
_aproto.push.apply(
handlers[ e ],
fnargs
),
t
)
|| ( handlers[ e ] = slc( fnargs ) );
}
return obj;
},
// removes fns registered for 'e' event
removeListener : function ( e ) {
if (
isvalid( e )
) {
if ( owns( handlers, e ) ) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
fnargs.length
&& (
_aproto.gc.apply(
handlers[ e ],
fnargs
),
handlers[ e ].length
|| ( delete handlers[ e ] ),
t
)
|| (
handlers[ e ].empty(),
delete handlers[ e ]
);
}
} else {
owneach(
handlers,
function ( evt, fns ) {
fns.empty();
}
);
vacate( handlers );
}
return obj;
},
// runs fns registered for evt 'e'
triggerEvent : function ( e ) {
if (
isvalid( e )
) {
if (
owns( handlers, e )
) {
var fireargs = slc( arguments, 1 );
handlers[ e ]
.each(
function ( k, evhandler ) {
defer( evhandler )
.call(
obj,
{
type : e,
data : fireargs,
target : obj,
handler : evhandler
}
);
}
);
}
}
return obj;
}
}
);
}
return obj;
}
//
// declares Timer factory fn
_tm = function ( delay, repeatCount ) {
return ( function ( delay, fireNTimes ) {
var // timer obj
host = this,
// timer's private state
// used/manipulated by api bellow
timerState = {
'current-count' : 0,
'delay' : Math.abs( parseFloat( delay ) ) || 1000,
'repeat-count' : Math.abs( parseInt( fireNTimes ) ) || Infinity,
'running' : f,
'interval' : un
},
// arguments provided to timer's .start() method
// used as args for triggered fns
fireargs = [];
// attaches api to timer obj
// .start() .stop() .reset() .currentCount() .delay() .repeatCount() .running() .state()
rig_props(
host,
{
// starts timer event dispatch
// sets provided args as
// parameters to triggered fns
// triggers 'timer-start' event
// and 'timer' events
start : function () {
var startargs;
host.running()
|| (
timerState.running = t,
( startargs = slc( arguments ) ).length
&& fireargs.substitute( startargs ),
host.triggerEvent.apply(
host,
[ timerEvent.start ]
.concat( fireargs )
),
timerState['current-count'] += 1,
host.triggerEvent.apply(
host,
[ timerEvent.timer ]
.concat( fireargs )
),
( timerState['current-count'] === timerState['repeat-count'] )
&& host.reset()
|| (timerState.interval =
setInterval(
function () {
( timerState['current-count'] < timerState['repeat-count'] )
&& (
timerState['current-count'] += 1,
host.triggerEvent.apply( host,
[ timerEvent.timer ]
.concat( fireargs )
),
( timerState['current-count'] === timerState['repeat-count'] )
&& host.reset()
);
},
timerState.delay
)
)
);
return host;
},
// pauses triggering timer events
// triggers 'timer-stop' event
stop : function () {
host.running()
&& (
( timerState.interval !== un )
&& (
clearInterval( timerState.interval ),
timerState.interval = un
),
timerState.running = f,
host.triggerEvent.apply(
host,
[ timerEvent.stop ]
.concat( fireargs )
)
);
return host;
},
// nulls timer state
// triggers 'timer-end' event
reset : function () {
( timerState.interval !== un )
&& (
clearInterval( timerState.interval ),
timerState.interval = un
);
timerState.running = f;
timerState["current-count"] = 0;
host.triggerEvent.apply(
host,
[ timerEvent.end ]
.concat( fireargs )
);
return host;
},
// how many times timer fired
currentCount : function () {
return timerState['current-count'];
},
// return timer's fire rate in ms
delay : function () {
return timerState.delay;
},
// how many times timer will fire 'timer' event
repeatCount : function () {
return timerState['repeat-count'];
},
// returns boolean
running : function () {
return timerState.running;
},
// returns timers intrnal state{}
state : function () {
return {
currentCount : timerState['current-count'],
delay : timerState.delay,
repeatCount : timerState['repeat-count'],
running : timerState.running
};
}
}
);
return host;
} ).call( listener( {} ), delay, repeatCount );
};
//
// attaches Timer fn to global scope
_host_.Timer = _tm;
} )( self, Array.prototype );
//
// use:
//
// var
// tm = Timer( 1000/50 ); // set timers fq to 50 times a sec
//
// // register fns for 'timer' event
// tm.addListener(
// "timer",
// function () { console.log( arguments ) },
// doStuff1,
// doStuff2
// );
//
// someElement.onmouseover = function () { tm.start( someElement ); };
// someElement.onmouseout = function () { tm.stop(); };
// someElement.onclick = function () { tm.reset(); };
//
// etc.
//
//
//
( function ( export_obj, aproto ) {
var timerEvent = { // _tm fn used below uses this strings for registering handlers
start : "tick-start",
tick : "tick",
stop : "tick-stop",
end : "tick-end"
},
undef = void null, // === undefined
_tm; // will hold private Timer function which will be imported into global scope, at the end of script
// Array.prototype extensions helpers:
// .each() .keep() .gc() .has() .empty() .substitute()
// passes key/value to callback, sets the array as context
// itarates bacward given boolFlagIterateBackwards == true
// breaks iteration if callback gives == false
// doesn't skips sparse indicies
// returns iterated array
// I've tested this construct against .forEach() in Chrome and it performed ~4x faster
aproto.each = function ( fn, boolFlagIterateBackwards ) {
var l = this.length,
i = 0;
if ( boolFlagIterateBackwards != true ) {
while (
i < l
) {
if ( !fn.call( this, i, this[i++] ) ) return this;
}
} else {
while (
--l >= 0
) {
if ( !fn.call( this, l, this[l] ) ) return this;
}
}
return this;
};
// keeps elements in array if pass callback's test, removes others
// manipulates the array directly, and returns it, not the new array as .filter() does
aproto.keep = function ( fn ) {
return this.each(
function ( i, o ) {
fn.call( this, i, o ) || this.splice( i, 1 );
return true;
},
true
);
};
// removes elements that match passed arguments
aproto.gc = function () {
var toremove = slc( arguments );
return this.each(
function ( i, o ) {
toremove.has( o ) && this.splice( i, 1 );
return true;
},
true
);
};
aproto.has = function ( v ) {
return this.indexOf( v ) !== -1;
};
aproto.empty = function () {
return ( this.length = 0, this );
};
// replaces content of array with provided array's content
aproto.substitute = function ( arr ) {
return ( aproto.push.apply( this.empty(), arr ), this );
};
// helper fns
function isobj( o ) {
return o === Object( o );
}
function isplainobj( o ) {
return Object.prototype.toString.call( o ) === "[object Object]";
}
function isfn( o ) {
return typeof o === "function";
}
function isvalid( o ) { // returns false for: undefined, null, NaN
return o !== undef
&& o !== null
&& ( o === o );
}
function owns( obj, p ) {
return obj.hasOwnProperty( p );
}
// loops objects own properties
// breaks if fn return ==false
function owneach( obj, fn ) {
for ( var p in obj ) {
if ( owns( obj, p ) ) {
if ( !fn.call( obj, p, obj[p] ) ) break;
}
}
return obj;
}
// attaches set of properties to an object
function rig_props( obj, props ) {
owneach(
props,
function ( p, v ) {
obj[p] = v;
return true;
}
);
return obj;
}
function slc( arg, i, j ) {
return Array.prototype.slice.call( arg, i, j );
}
// empties an object
// prefered over simple: obj = {},
// because there might be pointers in the code that
// keep a reference to the object
function vacate( obj ) {
for ( var p in obj ) {
owns( Object.prototype, p )
|| ( delete obj[p] );
}
return obj;
}
// 'asyncs' a function
// returns imidiately
// runs fn in 'background'
function defer( fn ) {
return function () {
var args = slc( arguments ),
target = this;
setTimeout(
function () {
return fn.apply( target, args );
}
);
return this;
};
}
// gives an object basic event handling support
// .addListener() .removeListener() .triggerEvent()
// used as base for objects returned by Timer ctor
function listener( obj ) {
if (
isobj( obj )
) {
var handlers = {};
rig_props(
obj,
{
// registers set of fns for ( scalar ) event e
// filters functions from passed arguments
// and adds them to handler list
addListener : function ( e ) { // ,... fns
if (
isvalid( e )
) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
owns( handlers, e )
&& (
aproto.push.apply(
handlers[ e ],
fnargs
),
true
)
|| ( handlers[ e ] = slc( fnargs ) );
}
return obj;
},
// removes fns registered for event e
// filters fns from passed parameters
// removing them from coresponding event
removeListener : function ( e ) { // ,... fns
if (
isvalid( e )
) {
if ( owns( handlers, e ) ) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
fnargs.length
&& (
aproto.gc.apply(
handlers[ e ],
fnargs
),
handlers[ e ].length
|| ( delete handlers[ e ] ),
true
)
|| (
handlers[ e ].empty(),
delete handlers[ e ]
);
}
} else {
owneach(
handlers,
function ( evt, fns ) {
return fns.empty();
}
);
vacate( handlers );
}
return obj;
},
// runs fns registered funcs for event e
// asynchronously fires fns added to the list for event e
// passes provided arguments to data property and
// provides type of event trigered,
// timer object which fired it
// and current running fn to each handlers in the list
// context for fns is set timer object
triggerEvent : function ( e ) { // ,...args
if (
isvalid( e )
) {
if (
owns( handlers, e )
) {
var fireargs =
slc( arguments, 1 );
handlers[ e ]
.each(
function ( k, evhandler ) {
defer( evhandler )
.call(
obj,
{
type : e,
data : fireargs,
target : obj,
handler : evhandler
}
);
return true;
}
);
}
}
return obj;
}
}
);
}
return obj;
}
//
// declare Timer function
_tm = function ( delay, repeatCount ) {
return ( function ( delay, fireNTimes ) {
var host = this, // timer 'tick' event dispatcher obj
// timer's private state
// used/manipulated by api bellow
timerState = {
currentCount : 0,
delay : Math.abs( parseFloat( delay ) ) || 1000,
repeatCount : Math.abs( parseInt( fireNTimes ) ) || Infinity,
running : false,
interval : undef
},
// arguments provided to timer's .start() method
// are stored in this array are
// used as argumentss for triggered handler fns
fireargs = [];
// attaches api to timer obj
// .start() .stop() .reset() .currentCount() .delay() .repeatCount() .running() .state()
rig_props(
host,
{
// starts timer event dispatch
// sets provided args as
// parameters to triggered fns
// triggers 'tick-start' event
// and 'tick' events
// keeps track of how many times handlers are runnnd
start : function () {
var startargs; // will temporarily store passed parameters
// abort if timer is already dispatching ticks
// if arguments are passed to the method
// use them as parameters to triggered fns
host.running()
|| (
timerState.running = true,
( startargs = slc( arguments ) ).length
&& fireargs.substitute( startargs ),
host.triggerEvent.apply(
host,
[ timerEvent.start ]
.concat( fireargs )
),
timerState.currentCount += 1,
host.triggerEvent.apply(
host,
[ timerEvent.tick ]
.concat( fireargs )
),
timerState.currentCount += 1,
( timerState.currentCount
===== timerState.repeatCount
)
&& host.reset()
|| (timerState.interval =
setInterval(
function () {
(
timerState.currentCount
< timerState.repeatCount
)
&& (
timerState.currentCount += 1,
host.triggerEvent.apply(
host,
[ timerEvent.tick ]
.concat( fireargs )
),;
( timerState.currentCount += 1;
===( timerState.repeatCountcurrentCount
>= )timerState.repeatCount
&& host.reset()
&& host.reset();
},
timerState.delay
)
)
);
return host;
},
// pauses triggering tick events
// triggers 'tick-stop' event
// does nothing if timer is already paused
stop : function () {
host.running()
&& (
( timerState.interval !== undef )
&& (
clearInterval( timerState.interval ),
timerState.interval = undef
),
timerState.running = false,
host.triggerEvent.apply(
host,
[ timerEvent.stop ]
.concat( fireargs )
)
);
return host;
},
// nulls timer state
// triggers 'tick-end' event
reset : function () {
( timerState.interval !== undef )
&& (
clearInterval( timerState.interval ),
timerState.interval = undef
);
timerState.running = false;
timerState.currentCount = 0;
host.triggerEvent.apply(
host,
[ timerEvent.end ]
.concat( fireargs )
);
timerState.currentCount = 0;
return host;
},
// how many times timer fired
currentCount : function () {
return timerState.currentCount;
},
// return timer's fire rate in ms
delay : function () {
return timerState.delay;
},
// how many times timer will fire 'tick' event
repeatCount : function () {
return timerState.repeatCount;
},
// returns boolean
running : function () {
return timerState.running;
},
// gets a copy of timer's internal state
state : function () {
return {
currentCount : timerState.currentCount,
delay : timerState.delay,
repeatCount : timerState.repeatCount,
running : timerState.running,
};
},
// stops dispatching fns registered for timer's ticks
// removes all registered functions
dismiss : function () {
host.reset();
host.removeListener();
return host;
}
}
);
return host;
} ).call( listener( {} ), delay, repeatCount );
};
// attaches Timer fn to global scope
export_obj.Timer = _tm;
} )( self, Array.prototype );
//
// use:
//
// var
// tm = Timer( 1000/50 ); // set timers fq to 50 times a sec
//
// # register fns for 'tick' event
// tm.addListener(
// "tick",
// function () { console.log( arguments ) },
// doStuff1,
// doStuff2
// );
//
// someElement.onmouseover = function () { tm.start( someElement ); };
// someElement.onmouseout = function () { tm.stop(); };
// someElement.onclick = function () { tm.reset(); };
//
// fire 'tick' events 'globaly' ( 1/sec3sec ), 5 times
window._tick_ = Timer( 10003000, 5 );
_tick_
window.start();_tick_
setTimeout.addListener(
"tick-start",
function () { console.log( "--- tick-start ---" ); _tick_}
)
.addListener(
"tick",
function () { console.log( "fn#0: ", this.state() ); },
function () { console.log("tick#1 "fn#1: "+Math", Math.random()); },
function () { console.log( }
"fn#2: time -> ", ( new Date ).valueOf() ); }
)
.addListener(
}"tick-end",
function () 3000*Math{ console.randomlog( "--- tick-end ---" ); }
);
setTimeout( _tick_.start, 3000*Math.random() );
//
//
//
//
( function ( export_obj, aproto ) {
var timerEvent = { // _tm fn used below uses this strings for registering handlers
start : "tick-start",
tick : "tick",
stop : "tick-stop",
end : "tick-end"
},
undef = void null, // === undefined
_tm; // will hold private Timer function which will be imported into global scope, at the end of script
// Array.prototype extensions helpers:
// .each() .keep() .gc() .has() .empty() .substitute()
// passes key/value to callback, sets the array as context
// itarates bacward given boolFlagIterateBackwards == true
// breaks iteration if callback gives == false
// doesn't skips sparse indicies
// returns iterated array
// I've tested this construct against .forEach() in Chrome and it performed ~4x faster
aproto.each = function ( fn, boolFlagIterateBackwards ) {
var l = this.length,
i = 0;
if ( boolFlagIterateBackwards != true ) {
while (
i < l
) {
if ( !fn.call( this, i, this[i++] ) ) return this;
}
} else {
while (
--l >= 0
) {
if ( !fn.call( this, l, this[l] ) ) return this;
}
}
return this;
};
// keeps elements in array if pass callback's test, removes others
// manipulates the array directly, and returns it, not the new array as .filter() does
aproto.keep = function ( fn ) {
return this.each(
function ( i, o ) {
fn.call( this, i, o ) || this.splice( i, 1 );
return true;
},
true
);
};
// removes elements that match passed arguments
aproto.gc = function () {
var toremove = slc( arguments );
return this.each(
function ( i, o ) {
toremove.has( o ) && this.splice( i, 1 );
return true;
},
true
);
};
aproto.has = function ( v ) {
return this.indexOf( v ) !== -1;
};
aproto.empty = function () {
return ( this.length = 0, this );
};
// replaces content of array with provided array's content
aproto.substitute = function ( arr ) {
return ( aproto.push.apply( this.empty(), arr ), this );
};
// helper fns
function isobj( o ) {
return o === Object( o );
}
function isplainobj( o ) {
return Object.prototype.toString.call( o ) === "[object Object]";
}
function isfn( o ) {
return typeof o === "function";
}
function isvalid( o ) { // returns false for: undefined, null, NaN
return o !== undef
&& o !== null
&& ( o === o );
}
function owns( obj, p ) {
return obj.hasOwnProperty( p );
}
// loops objects own properties
// breaks if fn return ==false
function owneach( obj, fn ) {
for ( var p in obj ) {
if ( owns( obj, p ) ) {
if ( !fn.call( obj, p, obj[p] ) ) break;
}
}
return obj;
}
// attaches set of properties to an object
function rig_props( obj, props ) {
owneach(
props,
function ( p, v ) {
obj[p] = v;
return true;
}
);
return obj;
}
function slc( arg, i, j ) {
return Array.prototype.slice.call( arg, i, j );
}
// empties an object
// prefered over simple: obj = {},
// because there might be pointers in the code that
// keep a reference to the object
function vacate( obj ) {
for ( var p in obj ) {
owns( Object.prototype, p )
|| ( delete obj[p] );
}
return obj;
}
// 'asyncs' a function
// returns imidiately
// runs fn in 'background'
function defer( fn ) {
return function () {
var args = slc( arguments ),
target = this;
setTimeout(
function () {
return fn.apply( target, args );
}
);
return this;
};
}
// gives an object basic event handling support
// .addListener() .removeListener() .triggerEvent()
// used as base for objects returned by Timer ctor
function listener( obj ) {
if (
isobj( obj )
) {
var handlers = {};
rig_props(
obj,
{
// registers set of fns for ( scalar ) event e
// filters functions from passed arguments
// and adds them to handler list
addListener : function ( e ) { // ,... fns
if (
isvalid( e )
) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
owns( handlers, e )
&& (
aproto.push.apply(
handlers[ e ],
fnargs
),
true
)
|| ( handlers[ e ] = slc( fnargs ) );
}
return obj;
},
// removes fns registered for event e
// filters fns from passed parameters
// removing them from coresponding event
removeListener : function ( e ) { // ,... fns
if (
isvalid( e )
) {
if ( owns( handlers, e ) ) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
fnargs.length
&& (
aproto.gc.apply(
handlers[ e ],
fnargs
),
handlers[ e ].length
|| ( delete handlers[ e ] ),
true
)
|| (
handlers[ e ].empty(),
delete handlers[ e ]
);
}
} else {
owneach(
handlers,
function ( evt, fns ) {
return fns.empty();
}
);
vacate( handlers );
}
return obj;
},
// runs fns registered funcs for event e
// asynchronously fires fns added to the list for event e
// passes provided arguments to data property and
// provides type of event trigered,
// timer object which fired it
// and current running fn to each handlers in the list
// context for fns is set timer object
triggerEvent : function ( e ) { // ,...args
if (
isvalid( e )
) {
if (
owns( handlers, e )
) {
var fireargs =
slc( arguments, 1 );
handlers[ e ]
.each(
function ( k, evhandler ) {
defer( evhandler )
.call(
obj,
{
type : e,
data : fireargs,
target : obj,
handler : evhandler
}
);
return true;
}
);
}
}
return obj;
}
}
);
}
return obj;
}
//
// declare Timer function
_tm = function ( delay, repeatCount ) {
return ( function ( delay, fireNTimes ) {
var host = this, // timer 'tick' event dispatcher obj
// timer's private state
// used/manipulated by api bellow
timerState = {
currentCount : 0,
delay : Math.abs( parseFloat( delay ) ) || 1000,
repeatCount : Math.abs( parseInt( fireNTimes ) ) || Infinity,
running : false,
interval : undef
},
// arguments provided to timer's .start() method
// are stored in this array are
// used as argumentss for triggered handler fns
fireargs = [];
// attaches api to timer obj
// .start() .stop() .reset() .currentCount() .delay() .repeatCount() .running() .state()
rig_props(
host,
{
// starts timer event dispatch
// sets provided args as
// parameters to triggered fns
// triggers 'tick-start' event
// and 'tick' events
// keeps track of how many times handlers are runnnd
start : function () {
var startargs; // will temporarily store passed parameters
// abort if timer is already dispatching ticks
// if arguments are passed to the method
// use them as parameters to triggered fns
host.running()
|| (
timerState.running = true,
( startargs = slc( arguments ) ).length
&& fireargs.substitute( startargs ),
host.triggerEvent.apply(
host,
[ timerEvent.start ]
.concat( fireargs )
),
timerState.currentCount += 1,
host.triggerEvent.apply(
host,
[ timerEvent.tick ]
.concat( fireargs )
),
( timerState.currentCount
=== timerState.repeatCount
)
&& host.reset()
|| (timerState.interval =
setInterval(
function () {
(
timerState.currentCount
< timerState.repeatCount
)
&& (
timerState.currentCount += 1,
host.triggerEvent.apply(
host,
[ timerEvent.tick ]
.concat( fireargs )
),
( timerState.currentCount === timerState.repeatCount
)
&& host.reset()
);
},
timerState.delay
)
)
);
return host;
},
// pauses triggering tick events
// triggers 'tick-stop' event
// does nothing if timer is already paused
stop : function () {
host.running()
&& (
( timerState.interval !== undef )
&& (
clearInterval( timerState.interval ),
timerState.interval = undef
),
timerState.running = false,
host.triggerEvent.apply(
host,
[ timerEvent.stop ]
.concat( fireargs )
)
);
return host;
},
// nulls timer state
// triggers 'tick-end' event
reset : function () {
( timerState.interval !== undef )
&& (
clearInterval( timerState.interval ),
timerState.interval = undef
);
timerState.running = false;
timerState.currentCount = 0;
host.triggerEvent.apply(
host,
[ timerEvent.end ]
.concat( fireargs )
);
return host;
},
// how many times timer fired
currentCount : function () {
return timerState.currentCount;
},
// return timer's fire rate in ms
delay : function () {
return timerState.delay;
},
// how many times timer will fire 'tick' event
repeatCount : function () {
return timerState.repeatCount;
},
// returns boolean
running : function () {
return timerState.running;
},
// gets a copy of timer's internal state
state : function () {
return {
currentCount : timerState.currentCount,
delay : timerState.delay,
repeatCount : timerState.repeatCount,
running : timerState.running,
};
},
// stops dispatching fns registered for timer's ticks
// removes all registered functions
dismiss : function () {
host.reset();
host.removeListener();
return host;
}
}
);
return host;
} ).call( listener( {} ), delay, repeatCount );
};
// attaches Timer fn to global scope
export_obj.Timer = _tm;
} )( self, Array.prototype );
//
// use:
//
// var
// tm = Timer( 1000/50 ); // set timers fq to 50 times a sec
//
// # register fns for 'tick' event
// tm.addListener(
// "tick",
// function () { console.log( arguments ) },
// doStuff1,
// doStuff2
// );
//
// someElement.onmouseover = function () { tm.start( someElement ); };
// someElement.onmouseout = function () { tm.stop(); };
// someElement.onclick = function () { tm.reset(); };
//
// fire 'tick' events 'globaly' ( 1/sec )
window._tick_ = Timer( 1000 );
_tick_.start();
setTimeout(
function () { _tick_.addListener(
"tick",
function () { console.log("tick#1: "+Math.random()); }
); },
3000*Math.random()
);
//
//
//
//
( function ( export_obj, aproto ) {
var timerEvent = { // _tm fn used below uses this strings for registering handlers
start : "tick-start",
tick : "tick",
stop : "tick-stop",
end : "tick-end"
},
undef = void null, // === undefined
_tm; // will hold private Timer function which will be imported into global scope, at the end of script
// Array.prototype extensions helpers:
// .each() .keep() .gc() .has() .empty() .substitute()
// passes key/value to callback, sets the array as context
// itarates bacward given boolFlagIterateBackwards == true
// breaks iteration if callback gives == false
// doesn't skips sparse indicies
// returns iterated array
// I've tested this construct against .forEach() in Chrome and it performed ~4x faster
aproto.each = function ( fn, boolFlagIterateBackwards ) {
var l = this.length,
i = 0;
if ( boolFlagIterateBackwards != true ) {
while (
i < l
) {
if ( !fn.call( this, i, this[i++] ) ) return this;
}
} else {
while (
--l >= 0
) {
if ( !fn.call( this, l, this[l] ) ) return this;
}
}
return this;
};
// keeps elements in array if pass callback's test, removes others
// manipulates the array directly, and returns it, not the new array as .filter() does
aproto.keep = function ( fn ) {
return this.each(
function ( i, o ) {
fn.call( this, i, o ) || this.splice( i, 1 );
return true;
},
true
);
};
// removes elements that match passed arguments
aproto.gc = function () {
var toremove = slc( arguments );
return this.each(
function ( i, o ) {
toremove.has( o ) && this.splice( i, 1 );
return true;
},
true
);
};
aproto.has = function ( v ) {
return this.indexOf( v ) !== -1;
};
aproto.empty = function () {
return ( this.length = 0, this );
};
// replaces content of array with provided array's content
aproto.substitute = function ( arr ) {
return ( aproto.push.apply( this.empty(), arr ), this );
};
// helper fns
function isobj( o ) {
return o === Object( o );
}
function isplainobj( o ) {
return Object.prototype.toString.call( o ) === "[object Object]";
}
function isfn( o ) {
return typeof o === "function";
}
function isvalid( o ) { // returns false for: undefined, null, NaN
return o !== undef
&& o !== null
&& ( o === o );
}
function owns( obj, p ) {
return obj.hasOwnProperty( p );
}
// loops objects own properties
// breaks if fn return ==false
function owneach( obj, fn ) {
for ( var p in obj ) {
if ( owns( obj, p ) ) {
if ( !fn.call( obj, p, obj[p] ) ) break;
}
}
return obj;
}
// attaches set of properties to an object
function rig_props( obj, props ) {
owneach(
props,
function ( p, v ) {
obj[p] = v;
return true;
}
);
return obj;
}
function slc( arg, i, j ) {
return Array.prototype.slice.call( arg, i, j );
}
// empties an object
// prefered over simple: obj = {},
// because there might be pointers in the code that
// keep a reference to the object
function vacate( obj ) {
for ( var p in obj ) {
owns( Object.prototype, p )
|| ( delete obj[p] );
}
return obj;
}
// 'asyncs' a function
// returns imidiately
// runs fn in 'background'
function defer( fn ) {
return function () {
var args = slc( arguments ),
target = this;
setTimeout(
function () {
return fn.apply( target, args );
}
);
return this;
};
}
// gives an object basic event handling support
// .addListener() .removeListener() .triggerEvent()
// used as base for objects returned by Timer ctor
function listener( obj ) {
if (
isobj( obj )
) {
var handlers = {};
rig_props(
obj,
{
// registers set of fns for ( scalar ) event e
// filters functions from passed arguments
// and adds them to handler list
addListener : function ( e ) { // ,... fns
if (
isvalid( e )
) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
owns( handlers, e )
&& (
aproto.push.apply(
handlers[ e ],
fnargs
),
true
)
|| ( handlers[ e ] = slc( fnargs ) );
}
return obj;
},
// removes fns registered for event e
// filters fns from passed parameters
// removing them from coresponding event
removeListener : function ( e ) { // ,... fns
if (
isvalid( e )
) {
if ( owns( handlers, e ) ) {
var fnargs =
slc( arguments, 1 )
.keep(
function ( i, o ) {
return isfn( o );
}
);
fnargs.length
&& (
aproto.gc.apply(
handlers[ e ],
fnargs
),
handlers[ e ].length
|| ( delete handlers[ e ] ),
true
)
|| (
handlers[ e ].empty(),
delete handlers[ e ]
);
}
} else {
owneach(
handlers,
function ( evt, fns ) {
return fns.empty();
}
);
vacate( handlers );
}
return obj;
},
// runs fns registered funcs for event e
// asynchronously fires fns added to the list for event e
// passes provided arguments to data property and
// provides type of event trigered,
// timer object which fired it
// and current running fn to each handlers in the list
// context for fns is set timer object
triggerEvent : function ( e ) { // ,...args
if (
isvalid( e )
) {
if (
owns( handlers, e )
) {
var fireargs =
slc( arguments, 1 );
handlers[ e ]
.each(
function ( k, evhandler ) {
defer( evhandler )
.call(
obj,
{
type : e,
data : fireargs,
target : obj,
handler : evhandler
}
);
return true;
}
);
}
}
return obj;
}
}
);
}
return obj;
}
//
// declare Timer function
_tm = function ( delay, repeatCount ) {
return ( function ( delay, fireNTimes ) {
var host = this, // timer 'tick' event dispatcher obj
// timer's private state
// used/manipulated by api bellow
timerState = {
currentCount : 0,
delay : Math.abs( parseFloat( delay ) ) || 1000,
repeatCount : Math.abs( parseInt( fireNTimes ) ) || Infinity,
running : false,
interval : undef
},
// arguments provided to timer's .start() method
// are stored in this array are
// used as argumentss for triggered handler fns
fireargs = [];
// attaches api to timer obj
// .start() .stop() .reset() .currentCount() .delay() .repeatCount() .running() .state()
rig_props(
host,
{
// starts timer event dispatch
// sets provided args as
// parameters to triggered fns
// triggers 'tick-start' event
// and 'tick' events
// keeps track of how many times handlers are runnnd
start : function () {
var startargs; // will temporarily store passed parameters
// abort if timer is already dispatching ticks
// if arguments are passed to the method
// use them as parameters to triggered fns
host.running()
|| (
timerState.running = true,
( startargs = slc( arguments ) ).length
&& fireargs.substitute( startargs ),
host.triggerEvent.apply(
host,
[ timerEvent.start ]
.concat( fireargs )
),
host.triggerEvent.apply(
host,
[ timerEvent.tick ]
.concat( fireargs )
),
timerState.currentCount += 1,
( timerState.currentCount
== timerState.repeatCount
)
&& host.reset()
|| (timerState.interval =
setInterval(
function () {
host.triggerEvent.apply(
host,
[ timerEvent.tick ]
.concat( fireargs )
);
timerState.currentCount += 1;
( timerState.currentCount
>= timerState.repeatCount
)
&& host.reset();
},
timerState.delay
)
)
);
return host;
},
// pauses triggering tick events
// triggers 'tick-stop' event
// does nothing if timer is already paused
stop : function () {
host.running()
&& (
( timerState.interval !== undef )
&& (
clearInterval( timerState.interval ),
timerState.interval = undef
),
timerState.running = false,
host.triggerEvent.apply(
host,
[ timerEvent.stop ]
.concat( fireargs )
)
);
return host;
},
// nulls timer state
// triggers 'tick-end' event
reset : function () {
( timerState.interval !== undef )
&& (
clearInterval( timerState.interval ),
timerState.interval = undef
);
timerState.running = false;
host.triggerEvent.apply(
host,
[ timerEvent.end ]
.concat( fireargs )
);
timerState.currentCount = 0;
return host;
},
// how many times timer fired
currentCount : function () {
return timerState.currentCount;
},
// return timer's fire rate in ms
delay : function () {
return timerState.delay;
},
// how many times timer will fire 'tick' event
repeatCount : function () {
return timerState.repeatCount;
},
// returns boolean
running : function () {
return timerState.running;
},
// gets a copy of timer's internal state
state : function () {
return {
currentCount : timerState.currentCount,
delay : timerState.delay,
repeatCount : timerState.repeatCount,
running : timerState.running,
};
},
// stops dispatching fns registered for timer's ticks
// removes all registered functions
dismiss : function () {
host.reset();
host.removeListener();
return host;
}
}
);
return host;
} ).call( listener( {} ), delay, repeatCount );
};
// attaches Timer fn to global scope
export_obj.Timer = _tm;
} )( self, Array.prototype );
//
// use:
//
// var
// tm = Timer( 1000/50 ); // set timers fq to 50 times a sec
//
// # register fns for 'tick' event
// tm.addListener(
// "tick",
// function () { console.log( arguments ) },
// doStuff1,
// doStuff2
// );
//
// someElement.onmouseover = function () { tm.start( someElement ); };
// someElement.onmouseout = function () { tm.stop(); };
// someElement.onclick = function () { tm.reset(); };
//
// fire 'tick' events 'globaly' ( 1/3sec ), 5 times
window._tick_ = Timer( 3000, 5 );
window._tick_
.addListener(
"tick-start",
function () { console.log( "--- tick-start ---" ); }
)
.addListener(
"tick",
function () { console.log( "fn#0: ", this.state() ); },
function () { console.log( "fn#1: ", Math.random()); },
function () { console.log( "fn#2: time -> ", ( new Date ).valueOf() ); }
)
.addListener(
"tick-end",
function () { console.log( "--- tick-end ---" ); }
);
setTimeout( _tick_.start, 3000*Math.random() );
//
//
- 376
- 1
- 2
- 16