if(!Function.prototype.bind){Function.prototype.bind=function(obj){var slice=[].slice,args=slice.call(arguments,1),self=this,nop=function(){},bound=function(){return self.apply(this instanceof nop?this:(obj||{}),args.concat(slice.call(arguments)));};nop.prototype=self.prototype;bound.prototype=new nop();return bound;};} if(!Object.create){Object.create=function(base){function F(){};F.prototype=base;return new F();}} if(!Object.construct){Object.construct=function(base){var instance=Object.create(base);if(instance.initialize) instance.initialize.apply(instance,[].slice.call(arguments,1));return instance;}} if(!Object.extend){Object.extend=function(destination,source){for(var property in source){if(source.hasOwnProperty(property)) destination[property]=source[property];} return destination;};} Element=function(){var instance={_extended:true,showIf:function(on){if(on)this.show();else this.hide();},show:function(){this.style.display='';},hide:function(){this.style.display='none';},update:function(content){this.innerHTML=content;},hasClassName:function(name){return(new RegExp("(^|\s*)"+name+"(\s*|$)")).test(this.className)},addClassName:function(name){this.toggleClassName(name,true);},removeClassName:function(name){this.toggleClassName(name,false);},toggleClassName:function(name,on){var classes=this.className.split(' ');var n=classes.indexOf(name);on=(typeof on=='undefined')?(n<0):on;if(on&&(n<0)) classes.push(name);else if(!on&&(n>=0)) classes.splice(n,1);this.className=classes.join(' ');}};var get=function(ele){if(typeof ele=='string') ele=document.getElementById(ele);if(!ele._extended) Object.extend(ele,instance);return ele;};return get;}();$=Element;StateMachine={create:function(cfg){var target=cfg.target||{};var events=cfg.events;var n,event,name,can={};for(n=0;n=0;};target.cannot=function(event){return!this.can(event);};if(cfg.initial){var initial=(typeof cfg.initial=='string')?{state:cfg.initial}:cfg.initial;name=initial.event||'startup';can[name]=['none'];event=this.buildEvent(name,'none',initial.state,target);if(initial.defer) target[name]=event;else event.call(target);} return target;},buildEvent:function(name,from,to,target){return function(){if(this.cannot(name)) throw "event "+name+" innapropriate in current state "+this.current;var beforeEvent=this['onbefore'+name];if(beforeEvent&&(false===beforeEvent.apply(this,arguments))) return;if(this.current!=to){var exitState=this['onleave'+this.current];if(exitState) exitState.apply(this,arguments);this.current=to;var enterState=this['onenter'+to]||this['on'+to];if(enterState) enterState.apply(this,arguments);} var afterEvent=this['onafter'+name]||this['on'+name];if(afterEvent) afterEvent.apply(this,arguments);}}};Game={compatible:function(){return Object.create&&Object.extend&&Function.bind&&document.addEventListener&&Game.ua.hasCanvas},start:function(id,game,cfg){if(Game.compatible()) return Game.current=Object.construct(Game.Runner,id,game,cfg).game;},ua:function(){var ua=navigator.userAgent.toLowerCase();var key=((ua.indexOf("opera")>-1)?"opera":null);key=key||((ua.indexOf("firefox")>-1)?"firefox":null);key=key||((ua.indexOf("chrome")>-1)?"chrome":null);key=key||((ua.indexOf("safari")>-1)?"safari":null);key=key||((ua.indexOf("msie")>-1)?"ie":null);try{var re=(key=="ie")?"msie (\\d)":key+"\\/(\\d\\.\\d)" var matches=ua.match(new RegExp(re,"i"));var version=matches?parseFloat(matches[1]):null;}catch(e){} return{full:ua,name:key+(version?" "+version.toString():""),version:version,isFirefox:(key=="firefox"),isChrome:(key=="chrome"),isSafari:(key=="safari"),isOpera:(key=="opera"),isIE:(key=="ie"),hasCanvas:(document.createElement('canvas').getContext),hasAudio:(typeof(Audio)!='undefined'),hasTouch:('ontouchstart'in window)}}(),addEvent:function(obj,type,fn){$(obj).addEventListener(type,fn,false);},removeEvent:function(obj,type,fn){$(obj).removeEventListener(type,fn,false);},windowWidth:function(){return window.innerWidth||document.documentElement.offsetWidth;},windowHeight:function(){return window.innerHeight||document.documentElement.offsetHeight;},ready:function(fn){if(Game.compatible()) Game.addEvent(document,'DOMContentLoaded',fn);},renderToCanvas:function(width,height,render,canvas){canvas=canvas||document.createElement('canvas');canvas.width=width;canvas.height=height;render(canvas.getContext('2d'));return canvas;},loadScript:function(src,cb){var head=document.getElementsByTagName('head')[0];var s=document.createElement('script');head.appendChild(s);if(Game.ua.isIE){s.onreadystatechange=function(e){if(e.currentTarget.readyState=='loaded') cb(e.currentTarget);}} else{s.onload=function(e){cb(e.currentTarget);}} s.type='text/javascript';s.src=src;},loadImages:function(sources,callback){var images={};var count=sources?sources.length:0;if(count==0){callback(images);} else{for(var n=0;nbox2.right)||(box1.top>box2.bottom)||(box1.bottom0?1:-1);var dy2=dy+(accel*dt)*(dy>0?1:-1);return{nx:(x2-x),ny:(y2-y),x:x2,y:y2,dx:dx2,dy:dy2};},intercept:function(x1,y1,x2,y2,x3,y3,x4,y4,d){var denom=((y4-y3)*(x2-x1))-((x4-x3)*(y2-y1));if(denom!=0){var ua=(((x4-x3)*(y1-y3))-((y4-y3)*(x1-x3)))/denom;if((ua>=0)&&(ua<=1)){var ub=(((x2-x1)*(y1-y3))-((y2-y1)*(x1-x3)))/denom;if((ub>=0)&&(ub<=1)){var x=x1+(ua*(x2-x1));var y=y1+(ua*(y2-y1));return{x:x,y:y,d:d};}}} return null;},ballIntercept:function(ball,rect,nx,ny){var pt;if(nx<0){pt=Game.Math.intercept(ball.x,ball.y,ball.x+nx,ball.y+ny,rect.right+ball.radius,rect.top-ball.radius,rect.right+ball.radius,rect.bottom+ball.radius,"right");} else if(nx>0){pt=Game.Math.intercept(ball.x,ball.y,ball.x+nx,ball.y+ny,rect.left-ball.radius,rect.top-ball.radius,rect.left-ball.radius,rect.bottom+ball.radius,"left");} if(!pt){if(ny<0){pt=Game.Math.intercept(ball.x,ball.y,ball.x+nx,ball.y+ny,rect.left-ball.radius,rect.bottom+ball.radius,rect.right+ball.radius,rect.bottom+ball.radius,"bottom");} else if(ny>0){pt=Game.Math.intercept(ball.x,ball.y,ball.x+nx,ball.y+ny,rect.left-ball.radius,rect.top-ball.radius,rect.right+ball.radius,rect.top-ball.radius,"top");}} return pt;}},Runner:{initialize:function(id,game,cfg){this.cfg=Object.extend(game.Defaults||{},cfg||{});this.fps=this.cfg.fps||60;this.interval=1000.0/this.fps;this.canvas=$(id);this.bounds=this.canvas.getBoundingClientRect();this.width=this.cfg.width||this.canvas.offsetWidth;this.height=this.cfg.height||this.canvas.offsetHeight;this.front=this.canvas;this.front.width=this.width;this.front.height=this.height;this.front2d=this.front.getContext('2d');this.addEvents();this.resetStats();this.resize();this.game=Object.construct(game,this,this.cfg);if(this.cfg.state) StateMachine.create(Object.extend({target:this.game},this.cfg.state));this.initCanvas();},start:function(){this.lastFrame=Game.timestamp();this.timer=setInterval(this.loop.bind(this),this.interval);},stop:function(){clearInterval(this.timer);},loop:function(){this._start=Game.timestamp();this.update((this._start-this.lastFrame)/1000.0);this._middle=Game.timestamp();this.draw();this._end=Game.timestamp();this.updateStats(this._middle-this._start,this._end-this._middle);this.lastFrame=this._start;},initCanvas:function(){if(this.game&&this.game.initCanvas) this.game.initCanvas(this.front2d);},update:function(dt){this.game.update(dt);},draw:function(){this.game.draw(this.front2d);this.drawStats(this.front2d);},resetStats:function(){this.stats={count:0,fps:0,update:0,draw:0,frame:0};},updateStats:function(update,draw){if(this.cfg.stats){this.stats.update=Math.max(1,update);this.stats.draw=Math.max(1,draw);this.stats.frame=this.stats.update+this.stats.draw;this.stats.count=this.stats.count==this.fps?0:this.stats.count+1;this.stats.fps=Math.min(this.fps,1000/this.stats.frame);}},strings:{frame:"frame: ",fps:"fps: ",update:"update: ",draw:"draw: ",ms:"ms"},drawStats:function(ctx){if(this.cfg.stats){ctx.fillText(this.strings.frame+Math.round(this.stats.count),this.width-100,this.height-60);ctx.fillText(this.strings.fps+Math.round(this.stats.fps),this.width-100,this.height-50);ctx.fillText(this.strings.update+Math.round(this.stats.update)+this.strings.ms,this.width-100,this.height-40);ctx.fillText(this.strings.draw+Math.round(this.stats.draw)+this.strings.ms,this.width-100,this.height-30);}},addEvents:function(){Game.addEvent(document,'keydown',this.onkeydown.bind(this));Game.addEvent(document,'keyup',this.onkeyup.bind(this));Game.addEvent(window,'resize',this.onresize.bind(this));},onresize:function(){this.stop();if(this.onresizeTimer) clearTimeout(this.onresizeTimer);this.onresizeTimer=setTimeout(this.onresizeend.bind(this),50);},onresizeend:function(){this.resize();this.start();},resize:function(){if((this.width!=this.canvas.offsetWidth)||(this.height!=this.front.offsetHeight)){this.width=this.front.width=this.front.offsetWidth;this.height=this.front.height=this.front.offsetHeight;if(this.game&&this.game.onresize) this.game.onresize(this.width,this.height);this.initCanvas();}},onkeydown:function(ev){if(this.game.onkeydown) return this.game.onkeydown(ev.keyCode);else if(this.cfg.keys) return this.onkey(ev.keyCode,'down');},onkeyup:function(ev){if(this.game.onkeyup) return this.game.onkeyup(ev.keyCode);else if(this.cfg.keys) return this.onkey(ev.keyCode,'up');},onkey:function(keyCode,mode){var n,k,i,state=this.game.current;for(n=0;n=0))){if(!k.state||(k.state==state)){if(k.mode==mode){k.action.call(this.game);}}}}},storage:function(){try{return this.localStorage=this.localStorage||window.localStorage||{};} catch(e){return this.localStorage={};}},alert:function(msg){this.stop();result=window.alert(msg);this.start();return result;},confirm:function(msg){this.stop();result=window.confirm(msg);this.start();return result;}}}