Skip to main content
Code Review

Return to Question

deleted 38 characters in body; edited title
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238

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.

Rollback to Revision 2
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238
//
//
;( 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.
//
Notice removed Authoritative reference needed by Community Bot
Bounty Ended with konijn's answer chosen by Community Bot
added 126 characters in body
Source Link
Nikola Vukovic
  • 376
  • 1
  • 2
  • 16
//
//
( 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() );
//
//
added 34 characters in body
Source Link
Nikola Vukovic
  • 376
  • 1
  • 2
  • 16
Loading
Tweeted twitter.com/#!/StackCodeReview/status/384064256754589696
edited body
Source Link
Nikola Vukovic
  • 376
  • 1
  • 2
  • 16
Loading
added 491 characters in body
Source Link
Nikola Vukovic
  • 376
  • 1
  • 2
  • 16
Loading
Revisited version, add some more comments, fixed lousy variable names.
Source Link
Nikola Vukovic
  • 376
  • 1
  • 2
  • 16
Loading
Notice added Authoritative reference needed by Nikola Vukovic
Bounty Started worth 50 reputation by Nikola Vukovic
added 436 characters in body
Source Link
Nikola Vukovic
  • 376
  • 1
  • 2
  • 16
Loading
deleted 14 characters in body
Source Link
Nikola Vukovic
  • 376
  • 1
  • 2
  • 16
Loading
Source Link
Nikola Vukovic
  • 376
  • 1
  • 2
  • 16
Loading
default

AltStyle によって変換されたページ (->オリジナル) /