diff --git a/dist/crafty-min.js b/dist/crafty-min.js index da0d0811..08df0de5 100644 --- a/dist/crafty-min.js +++ b/dist/crafty-min.js @@ -1,12 +1,12 @@ /** - * crafty 0.6.0 + * crafty 0.6.3-beta * http://craftyjs.com/ * - * Copyright 2013, Louis Stowasser + * Copyright 2014, Louis Stowasser * Dual licensed under the MIT or GPL licenses. */ -(function e(t,i,n){function s(o,a){if(!i[o]){if(!t[o]){var h="function"==typeof require&&require;if(!a&&h)return h(o,!0);if(r)return r(o,!0);throw Error("Cannot find module '"+o+"'")}var c=i[o]={exports:{}};t[o][0].call(c.exports,function(e){var i=t[o][1][e];return s(i?i:e)},c,c.exports,e,t,i,n)}return i[o].exports}for(var r="function"==typeof require&&require,o=0;n.length>o;o++)s(n[o]);return s})({1:[function(t){var e=t("./core.js"),i=(window.document,t("./HashMap.js"));e._rectPool=function(){var t=[],e=0;return{get:function(i,n,s,r){e>=t.length&&t.push({});var o=t[e++];return o._x=i,o._y=n,o._w=s,o._h=r,o},copy:function(i){e>=t.length&&t.push({});var n=t[e++];return n._x=i._x,n._y=i._y,n._w=i._w,n._h=i._h,n},recycle:function(){e--}}}(),e.map=new i;var n=Math,s=(n.cos,n.sin,n.PI),r=s/180;e.c("2D",{_x:0,_y:0,_w:0,_h:0,_z:0,_rotation:0,_alpha:1,_visible:!0,_globalZ:null,_origin:null,_mbr:null,_entry:null,_children:null,_parent:null,_changed:!1,_defineGetterSetter_setter:function(){this.__defineSetter__("x",function(t){this._attr("_x",t)}),this.__defineSetter__("y",function(t){this._attr("_y",t)}),this.__defineSetter__("w",function(t){this._attr("_w",t)}),this.__defineSetter__("h",function(t){this._attr("_h",t)}),this.__defineSetter__("z",function(t){this._attr("_z",t)}),this.__defineSetter__("rotation",function(t){this._attr("_rotation",t)}),this.__defineSetter__("alpha",function(t){this._attr("_alpha",t)}),this.__defineSetter__("visible",function(t){this._attr("_visible",t)}),this.__defineGetter__("x",function(){return this._x}),this.__defineGetter__("y",function(){return this._y}),this.__defineGetter__("w",function(){return this._w}),this.__defineGetter__("h",function(){return this._h}),this.__defineGetter__("z",function(){return this._z}),this.__defineGetter__("rotation",function(){return this._rotation}),this.__defineGetter__("alpha",function(){return this._alpha}),this.__defineGetter__("visible",function(){return this._visible}),this.__defineGetter__("parent",function(){return this._parent}),this.__defineGetter__("numChildren",function(){return this._children.length})},_defineGetterSetter_defineProperty:function(){Object.defineProperty(this,"x",{set:function(t){this._attr("_x",t)},get:function(){return this._x},configurable:!0}),Object.defineProperty(this,"y",{set:function(t){this._attr("_y",t)},get:function(){return this._y},configurable:!0}),Object.defineProperty(this,"w",{set:function(t){this._attr("_w",t)},get:function(){return this._w},configurable:!0}),Object.defineProperty(this,"h",{set:function(t){this._attr("_h",t)},get:function(){return this._h},configurable:!0}),Object.defineProperty(this,"z",{set:function(t){this._attr("_z",t)},get:function(){return this._z},configurable:!0}),Object.defineProperty(this,"rotation",{set:function(t){this._attr("_rotation",t)},get:function(){return this._rotation},configurable:!0}),Object.defineProperty(this,"alpha",{set:function(t){this._attr("_alpha",t)},get:function(){return this._alpha},configurable:!0}),Object.defineProperty(this,"visible",{set:function(t){this._attr("_visible",t)},get:function(){return this._visible},configurable:!0})},_defineGetterSetter_fallback:function(){this.x=this._x,this.y=this._y,this.w=this._w,this.h=this._h,this.z=this._z,this.rotation=this._rotation,this.alpha=this._alpha,this.visible=this._visible,this.bind("EnterFrame",function(){if(this.x!==this._x||this.y!==this._y||this.w!==this._w||this.h!==this._h||this.z!==this._z||this.rotation!==this._rotation||this.alpha!==this._alpha||this.visible!==this._visible){var t=e._rectPool.copy(this);if(this.rotation!==this._rotation)this._rotate(this.rotation);else{var i=this._mbr,n=!1;i&&(this.x!==this._x?(i._x-=this.x-this._x,n=!0):this.y!==this._y?(i._y-=this.y-this._y,n=!0):this.w!==this._w?(i._w-=this.w-this._w,n=!0):this.h!==this._h?(i._h-=this.h-this._h,n=!0):this.z!==this._z&&(i._z-=this.z-this._z,n=!0)),n&&this.trigger("Move",t)}this._x=this.x,this._y=this.y,this._w=this.w,this._h=this.h,this._z=this.z,this._rotation=this.rotation,this._alpha=this.alpha,this._visible=this.visible,this.trigger("Change",t),this.trigger("Move",t),e._rectPool.recycle(t)}})},init:function(){this._globalZ=this[0],this._origin={x:0,y:0},this._children=[],e.support.setter?this._defineGetterSetter_setter():e.support.defineProperty?this._defineGetterSetter_defineProperty():this._defineGetterSetter_fallback(),this._entry=e.map.insert(this),this.bind("Move",function(t){var e=this._mbr||this;this._entry.update(e),this._children.length>0&&this._cascade(t)}),this.bind("Rotate",function(t){var e=this._mbr||this;this._entry.update(e),this._children.length>0&&this._cascade(t)}),this.bind("Remove",function(){if(this._children){for(var t=0;this._children.length>t;t++)delete this._children[t]._parent,this._children[t].destroy&&this._children[t].destroy();this._children=[]}this._parent&&this._parent.detach(this),e.map.remove(this),this.detach()})},_calculateMBR:function(t,e,i){if(0===i)return this._mbr=null,void 0;var n=Math.cos(i),s=Math.sin(i);n=1e-10>n&&n>-1e-10?0:n,s=1e-10>s&&s>-1e-10?0:s;var r=t+(this._x-t)*n+(this._y-e)*s,o=e-(this._x-t)*s+(this._y-e)*n,a=t+(this._x+this._w-t)*n+(this._y-e)*s,h=e-(this._x+this._w-t)*s+(this._y-e)*n,c=t+(this._x+this._w-t)*n+(this._y+this._h-e)*s,u=e-(this._x+this._w-t)*s+(this._y+this._h-e)*n,l=t+(this._x-t)*n+(this._y+this._h-e)*s,d=e-(this._x-t)*s+(this._y+this._h-e)*n,_=Math.floor(Math.min(r,a,c,l)),p=Math.floor(Math.min(o,h,u,d)),f=Math.ceil(Math.max(r,a,c,l)),g=Math.ceil(Math.max(o,h,u,d));this._mbr?(this._mbr._x=_,this._mbr._y=p,this._mbr._w=f-_,this._mbr._h=g-p):this._mbr={_x:_,_y:p,_w:f-_,_h:g-p}},_rotate:function(t){var e=-1*(t%360),i=this._rotation-t;if(0!==i){var n=e*r,s={x:this._origin.x+this._x,y:this._origin.y+this._y};this._calculateMBR(s.x,s.y,n);var o=i*r,a=Math.cos(n),h=Math.sin(n);this.trigger("Rotate",{cos:Math.cos(o),sin:Math.sin(o),deg:i,rad:o,o:s,matrix:{M11:a,M12:h,M21:-h,M22:a}})}},area:function(){return this._w*this._h},intersect:function(t,e,i,n){var s,r=this._mbr||this;return s="object"==typeof t?t:{x:t,y:e,w:i,h:n},r._xs.x&&r._ys.y},within:function(t,e,i,n){var s,r=this._mbr||this;return s="object"==typeof t?t:{_x:t,_y:e,_w:i,_h:n},s._x<=r._x&&s._x+s._w>=r._x+r._w&&s._y<=r._y&&s._y+s._h>=r._y+r._h},contains:function(t,e,i,n){var s,r=this._mbr||this;return s="object"==typeof t?t:{_x:t,_y:e,_w:i,_h:n},s._x>=r._x&&s._x+s._w<=r._x+r._w&&s._y>=r._y&&s._y+s._h<=r._y+r._h},pos:function(){return{_x:this._x,_y:this._y,_w:this._w,_h:this._h}},mbr:function(){return this._mbr?{_x:this._mbr._x,_y:this._mbr._y,_w:this._mbr._w,_h:this._mbr._h}:this.pos()},isAt:function(t,e){if(this.mapArea)return this.mapArea.containsPoint(t,e);if(this.map)return this.map.containsPoint(t,e);var i=this._mbr||this;return t>=i._x&&i._x+i._w>=t&&e>=i._y&&i._y+i._h>=e},move:function(t,e){return"n"===t.charAt(0)&&(this.y-=e),"s"===t.charAt(0)&&(this.y+=e),("e"===t||"e"===t.charAt(1))&&(this.x+=e),("w"===t||"w"===t.charAt(1))&&(this.x-=e),this},shift:function(t,e,i,n){return t&&(this.x+=t),e&&(this.y+=e),i&&(this.w+=i),n&&(this.h+=n),this},_cascade:function(t){if(t){var e,i=0,n=this._children,s=n.length;if(t.cos)for(;s>i;++i)e=n[i],"rotate"in e&&e.rotate(t);else for(var r=this._x-t._x,o=this._y-t._y,a=this._w-t._w,h=this._h-t._h;s>i;++i)e=n[i],e.shift(r,o,a,h)}},attach:function(){for(var t,e=0,i=arguments,n=arguments.length;n>e;++e)t=i[e],t._parent&&t._parent.detach(t),t._parent=this,this._children.push(t);return this},detach:function(t){var e;if(!t){for(e=0;this._children.length>e;e++)this._children[e]._parent=null;return this._children=[],this}for(e=0;this._children.length>e;e++)this._children[e]==t&&this._children.splice(e,1);return t._parent=null,this},origin:function(t,e){if("string"==typeof t)if("centre"===t||"center"===t||-1===t.indexOf(" "))t=this._w/2,e=this._h/2;else{var i=t.split(" ");"top"===i[0]?e=0:"bottom"===i[0]?e=this._h:("middle"===i[0]||"center"===i[1]||"centre"===i[1])&&(e=this._h/2),"center"===i[1]||"centre"===i[1]||"middle"===i[1]?t=this._w/2:"left"===i[1]?t=0:"right"===i[1]&&(t=this._w)}return this._origin.x=t,this._origin.y=e,this},flip:function(t){return t=t||"X",this["_flip"+t]||(this["_flip"+t]=!0,this.trigger("Change")),this},unflip:function(t){return t=t||"X",this["_flip"+t]&&(this["_flip"+t]=!1,this.trigger("Change")),this},rotate:function(t){var e,i;e=(this._x+this._origin.x-t.o.x)*t.cos+(this._y+this._origin.y-t.o.y)*t.sin+(t.o.x-this._origin.x),i=(this._y+this._origin.y-t.o.y)*t.cos-(this._x+this._origin.x-t.o.x)*t.sin+(t.o.y-this._origin.y),this._attr("_rotation",this._rotation-t.deg),this._attr("_x",e),this._attr("_y",i)},_attr:function(t,i){if(this[t]!==i){var n,s=e._rectPool.copy(this);if("_rotation"===t)this._rotate(i);else if("_z"===t)this._globalZ=parseInt(i+e.zeroFill(this[0],5),10),this.trigger("reorder");else if("_x"===t||"_y"===t)n=this._mbr,n&&(n[t]-=this[t]-i),this[t]=i,this.trigger("Move",s);else if("_h"===t||"_w"===t){n=this._mbr;var o=this[t];this[t]=i,n&&this._calculateMBR(this._origin.x+this._x,this._origin.y+this._y,-this._rotation*r),"_w"===t?this.trigger("Resize",{axis:"w",amount:i-o}):"_h"===t&&this.trigger("Resize",{axis:"h",amount:i-o}),this.trigger("Move",s)}this[t]=i,this.trigger("Change",s),e._rectPool.recycle(s)}}}),e.c("Gravity",{_gravityConst:.2,_gy:0,_falling:!0,_anti:null,init:function(){this.requires("2D")},gravity:function(t){return t&&(this._anti=t),isNaN(this._jumpSpeed)&&(this._jumpSpeed=0),this.bind("EnterFrame",this._enterFrame),this},gravityConst:function(t){return this._gravityConst=t,this},_enterFrame:function(){this._falling?(this._gy+=this._gravityConst,this.y+=this._gy):this._gy=0;var t,i,n,s=!1,r=this.pos(),o=0;for(r._y++,r.x=r._x,r.y=r._y,r.w=r._w,r.h=r._h,i=e.map.search(r),n=i.length;n>o;++o)if(t=i[o],t!==this&&t.has(this._anti)&&t.intersect(r)){s=t;break}s?this._falling&&(this._gy>this._jumpSpeed||!this._up)&&this.stopFalling(s):this._falling=!0},stopFalling:function(t){t&&(this.y=t._y-this._h),this._falling=!1,this._up&&(this._up=!1),this.trigger("hit")},antigravity:function(){this.unbind("EnterFrame",this._enterFrame)}}),e.polygon=function(t){arguments.length>1&&(t=Array.prototype.slice.call(arguments,0)),this.points=t},e.polygon.prototype={containsPoint:function(t,e){var i,n,s=this.points,r=!1;for(i=0,n=s.length-1;s.length>i;n=i++)s[i][1]>e!=s[n][1]>e&&(s[n][0]-s[i][0])*(e-s[i][1])/(s[n][1]-s[i][1])+s[i][0]>t&&(r=!r);return r},shift:function(t,e){for(var i,n=0,s=this.points.length;s>n;n++)i=this.points[n],i[0]+=t,i[1]+=e},rotate:function(t){for(var e,i,n,s=0,r=this.points.length;r>s;s++)e=this.points[s],i=t.o.x+(e[0]-t.o.x)*t.cos+(e[1]-t.o.y)*t.sin,n=t.o.y-(e[0]-t.o.x)*t.sin+(e[1]-t.o.y)*t.cos,e[0]=i,e[1]=n}},e.circle=function(t,e,i){this.x=t,this.y=e,this.radius=i,this.points=[];for(var n,s=0;8>s;s++)n=s*Math.PI/4,this.points[s]=[this.x+Math.sin(n)*i,this.y+Math.cos(n)*i]},e.circle.prototype={containsPoint:function(t,e){var i=this.radius,n=(Math.sqrt,this.x-t),s=this.y-e;return i*i>n*n+s*s},shift:function(t,e){this.x+=t,this.y+=e;for(var i,n=0,s=this.points.length;s>n;n++)i=this.points[n],i[0]+=t,i[1]+=e},rotate:function(){}},e.matrix=function(t){this.mtx=t,this.width=t[0].length,this.height=t.length},e.matrix.prototype={x:function(t){if(this.width==t.height){for(var i=[],n=0;this.height>n;n++){i[n]=[];for(var s=0;t.width>s;s++){for(var r=0,o=0;this.width>o;o++)r+=this.mtx[n][o]*t.mtx[o][s];i[n][s]=r}}return new e.matrix(i)}},e:function(t,e){return 1>t||t>this.mtx.length||1>e||e>this.mtx[0].length?null:this.mtx[t-1][e-1]}}},{"./HashMap.js":4,"./core.js":9}],2:[function(t){var e=t("./core.js"),i=window.document;e.c("DOM",{_element:null,_cssStyles:null,avoidCss3dTransforms:!1,init:function(){function t(){var t=0,e=this.__c,i="";for(t in e)i+=" "+t;i=i.substr(1),this._element.className=i}this._cssStyles={visibility:"",left:"",top:"",width:"",height:"",zIndex:"",opacity:"",transformOrigin:"",transform:""},this._element=i.createElement("div"),e.stage.inner.appendChild(this._element),this._element.style.position="absolute",this._element.id="ent"+this[0],this.bind("Change",function(){this._changed||(this._changed=!0,e.DrawManager.addDom(this))}),this.bind("NewComponent",t).bind("RemoveComponent",t),"ms"===e.support.prefix&&9>e.support.version&&(this._filters={},this.bind("Rotate",function(t){var e=t.matrix,i=(this._element.style,e.M11.toFixed(8)),n=e.M12.toFixed(8),s=e.M21.toFixed(8),r=e.M22.toFixed(8);this._filters.rotation="progid:DXImageTransform.Microsoft.Matrix(M11="+i+", M12="+n+", M21="+s+", M22="+r+",sizingMethod='auto expand')"})),this.bind("Remove",this.undraw),this.bind("RemoveComponent",function(t){"DOM"===t&&this.undraw()})},getDomId:function(){return this._element.id},DOM:function(t){return t&&t.nodeType&&(this.undraw(),this._element=t,this._element.style.position="absolute"),this},draw:function(){var t=this._element.style,i=this.__coord||[0,0,0,0],n={x:i[0],y:i[1],w:i[2],h:i[3]},s=e.support.prefix,r=[];if(this._cssStyles.visibility!==this._visible&&(this._cssStyles.visibility=this._visible,t.visibility=this._visible?"visible":"hidden"),e.support.css3dtransform&&!this.avoidCss3dTransforms?r.push("translate3d("+~~this._x+"px,"+~~this._y+"px,0)"):(this._cssStyles.left!==this._x&&(this._cssStyles.left=this._x,t.left=~~this._x+"px"),this._cssStyles.top!==this._y&&(this._cssStyles.top=this._y,t.top=~~this._y+"px")),this._cssStyles.width!==this._w&&(this._cssStyles.width=this._w,t.width=~~this._w+"px"),this._cssStyles.height!==this._h&&(this._cssStyles.height=this._h,t.height=~~this._h+"px"),this._cssStyles.zIndex!==this._z&&(this._cssStyles.zIndex=this._z,t.zIndex=this._z),this._cssStyles.opacity!==this._alpha&&(this._cssStyles.opacity=this._alpha,t.opacity=this._alpha,t[s+"Opacity"]=this._alpha),"ms"===s&&9>e.support.version&&(this._filters.alpha=8===e.support.version?"progid:DXImageTransform.Microsoft.Alpha(Opacity="+100*this._alpha+")":"alpha(opacity="+100*this._alpha+")"),this._mbr){var o=this._origin.x+"px "+this._origin.y+"px";t.transformOrigin=o,t[s+"TransformOrigin"]=o,e.support.css3dtransform?r.push("rotateZ("+this._rotation+"deg)"):r.push("rotate("+this._rotation+"deg)")}return this._flipX&&(r.push("scaleX(-1)"),"ms"===s&&9>e.support.version&&(this._filters.flipX="fliph")),this._flipY&&(r.push("scaleY(-1)"),"ms"===s&&9>e.support.version&&(this._filters.flipY="flipv")),"ms"===s&&9>e.support.version&&this.applyFilters(),this._cssStyles.transform!=r.join(" ")&&(this._cssStyles.transform=r.join(" "),t.transform=this._cssStyles.transform,t[s+"Transform"]=this._cssStyles.transform),this.trigger("Draw",{style:t,type:"DOM",co:n}),this},applyFilters:function(){this._element.style.filter="";var t="";for(var e in this._filters)this._filters.hasOwnProperty(e)&&(t+=this._filters[e]+" ");this._element.style.filter=t},undraw:function(){return this._element&&e.stage.inner.removeChild(this._element),this},css:function(t,i){var n,s,r=this._element,o=r.style;if("object"==typeof t)for(n in t)t.hasOwnProperty(n)&&(s=t[n],"number"==typeof s&&(s+="px"),o[e.DOM.camelize(n)]=s);else{if(!i)return e.DOM.getStyle(r,t);"number"==typeof i&&(i+="px"),o[e.DOM.camelize(t)]=i}return this.trigger("Change"),this}});try{i.execCommand("BackgroundImageCache",!1,!0)}catch(n){}e.extend({DOM:{window:{init:function(){this.width=window.innerWidth||window.document.documentElement.clientWidth||window.document.body.clientWidth,this.height=window.innerHeight||window.document.documentElement.clientHeight||window.document.body.clientHeight,e.unbind("RenderScene",e.DrawManager.renderDOM),e.bind("RenderScene",e.DrawManager.renderDOM)},width:0,height:0},inner:function(t){var e=t.getBoundingClientRect(),n=e.left+(window.pageXOffset?window.pageXOffset:i.body.scrollLeft),s=e.top+(window.pageYOffset?window.pageYOffset:i.body.scrollTop),r=parseInt(this.getStyle(t,"border-left-width")||0,10)||parseInt(this.getStyle(t,"borderLeftWidth")||0,10)||0,o=parseInt(this.getStyle(t,"border-top-width")||0,10)||parseInt(this.getStyle(t,"borderTopWidth")||0,10)||0;return n+=r,s+=o,{x:n,y:s}},getStyle:function(t,e){var n;return t.currentStyle?n=t.currentStyle[this.camelize(e)]:window.getComputedStyle&&(n=i.defaultView.getComputedStyle(t,null).getPropertyValue(this.csselize(e))),n},camelize:function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},csselize:function(t){return t.replace(/[A-Z]/g,function(t){return t?"-"+t.toLowerCase():""})},translate:function(t,n){return{x:(t-e.stage.x+i.body.scrollLeft+i.documentElement.scrollLeft-e.viewport._x)/e.viewport._scale,y:(n-e.stage.y+i.body.scrollTop+i.documentElement.scrollTop-e.viewport._y)/e.viewport._scale}}}})},{"./core.js":9}],3:[function(t){var e=t("./core.js"),i=window.document;e.c("DebugCanvas",{init:function(){this.requires("2D"),e.DebugCanvas.context||e.DebugCanvas.init(),e.DebugCanvas.add(this),this._debug={alpha:1,lineWidth:1},this.bind("RemoveComponent",this.onDebugRemove),this.bind("Remove",this.onDebugDestroy)},onDebugRemove:function(t){"DebugCanvas"===t&&e.DebugCanvas.remove(this)},onDebugDestroy:function(){e.DebugCanvas.remove(this)},debugAlpha:function(t){return this._debug.alpha=t,this},debugFill:function(t){return t===void 0&&(t="red"),this._debug.fillStyle=t,this},debugStroke:function(t){return t===void 0&&(t="red"),this._debug.strokeStyle=t,this},debugDraw:function(t){var e=t.globalAlpha,i=this._debug;i.alpha&&(t.globalAlpha=this._debug.alpha),i.strokeStyle&&(t.strokeStyle=i.strokeStyle),i.lineWidth&&(t.lineWidth=i.lineWidth),i.fillStyle&&(t.fillStyle=i.fillStyle),this.trigger("DebugDraw"),t.globalAlpha=e}}),e.c("DebugRectangle",{init:function(){this.requires("2D, DebugCanvas")},debugRectangle:function(t){return this.debugRect=t,this.unbind("DebugDraw",this.drawDebugRect),this.bind("DebugDraw",this.drawDebugRect),this},drawDebugRect:function(){ctx=e.DebugCanvas.context;var t=this.debugRect;null!==t&&void 0!==t&&t._h&&t._w&&(this._debug.fillStyle&&ctx.fillRect(t._x,t._y,t._w,t._h),this._debug.strokeStyle&&ctx.strokeRect(t._x,t._y,t._w,t._h))}}),e.c("VisibleMBR",{init:function(){this.requires("DebugRectangle").debugFill("purple").bind("EnterFrame",this._assignRect)},_assignRect:function(){this._mbr?this.debugRectangle(this._mbr):this.debugRectangle(this)}}),e.c("DebugPolygon",{init:function(){this.requires("2D, DebugCanvas")},debugPolygon:function(t){return this.polygon=t,this.unbind("DebugDraw",this.drawDebugPolygon),this.bind("DebugDraw",this.drawDebugPolygon),this},drawDebugPolygon:function(){if(void 0!==this.polygon){ctx=e.DebugCanvas.context,ctx.beginPath();for(var t in this.polygon.points)ctx.lineTo(this.polygon.points[t][0],this.polygon.points[t][1]);ctx.closePath(),this._debug.fillStyle&&ctx.fill(),this._debug.strokeStyle&&ctx.stroke()}}}),e.c("WiredHitBox",{init:function(){this.requires("DebugPolygon").debugStroke("red").matchHitBox(),this.bind("NewHitbox",this.matchHitBox)},matchHitBox:function(){this.debugPolygon(this.map)}}),e.c("SolidHitBox",{init:function(){this.requires("Collision, DebugPolygon").debugFill("orange").debugAlpha(.7).matchHitBox(),this.bind("NewHitbox",this.matchHitBox)},matchHitBox:function(){this.debugPolygon(this.map)}}),e.DebugCanvas={context:null,entities:[],onetimeEntities:[],add:function(t){this.entities.push(t)},remove:function(t){for(var e=this.entities,i=e.length-1;i>=0;i--)e[i]==t&&e.splice(i,1)},init:function(){if(!e.DebugCanvas.context){if(!e.support.canvas)return e.trigger("NoCanvas"),e.stop(),void 0;var t;t=i.createElement("canvas"),t.width=e.viewport.width,t.height=e.viewport.height,t.style.position="absolute",t.style.left="0px",t.style.top="0px",t.id="debug-canvas",t.style.zIndex=1e5,e.stage.elem.appendChild(t),e.DebugCanvas.context=t.getContext("2d"),e.DebugCanvas._canvas=t}e.unbind("RenderScene",e.DebugCanvas.renderScene),e.bind("RenderScene",e.DebugCanvas.renderScene)},renderScene:function(t){t=t||e.viewport.rect();var i,n=e.DebugCanvas.entities,s=0,r=n.length,o=e.DebugCanvas.context,a=e.viewport;for(o.setTransform(a._scale,0,0,a._scale,a._x,a._y),o.clearRect(t._x,t._y,t._w,t._h);r>s;s++)i=n[s],i.debugDraw(o)}}},{"./core.js":9}],4:[function(t,e){function i(t,e,i){this.keys=t,this.map=i,this.obj=e}t("./core.js"),window.document;var n,s=function(t){n=t||64,this.map={}},r=" ",o={};s.prototype={insert:function(t){var e,n,r=s.key(t),o=new i(r,t,this),a=0;for(a=r.x1;r.x2>=a;a++)for(e=r.y1;r.y2>=e;e++)n=a<<16^e,this.map[n]||(this.map[n]=[]),this.map[n].push(t);return o},search:function(t,e){var i,n,r,a=s.key(t,o),h=[];for(void 0===e&&(e=!0),i=a.x1;a.x2>=i;i++)for(n=a.y1;a.y2>=n;n++)if(cell=this.map[i<<16^n])for(r=0;cell.length>r;r++)h.push(cell[r]);if(e){var c,u,d=[],_={};for(i=0,l=h.length;l>i;i++)c=h[i],c&&(u=c[0],c=c._mbr||c,!_[u]&&c._xt._x&&c._yt._y&&(_[u]=h[i]));for(c in _)d.push(_[c]);return d}return h},remove:function(t,e){var i,n,r=0;for(1==arguments.length&&(e=t,t=s.key(e,o)),r=t.x1;t.x2>=r;r++)for(i=t.y1;t.y2>=i;i++)if(n=r<<16^i,this.map[n]){var a,h=this.map[n],c=h.length;for(a=0;c>a;a++)h[a]&&h[a][0]===e[0]&&h.splice(a,1)}},refresh:function(t){var e,i,n,r,o,a=t.keys,h=t.obj;for(i=a.x1;a.x2>=i;i++)for(n=a.y1;a.y2>=n;n++)if(e=this.map[i<<16^n])for(o=e.length,r=0;o>r;r++)e[r]&&e[r][0]===h[0]&&e.splice(r,1);for(s.key(h,a),i=a.x1;a.x2>=i;i++)for(n=a.y1;a.y2>=n;n++)e=this.map[i<<16^n],e||(e=this.map[i<<16^n]=[]),e.push(h);return t},boundaries:function(){var t,e,i={max:{x:-1/0,y:-1/0},min:{x:1/0,y:1/0}},n={max:{x:-1/0,y:-1/0},min:{x:1/0,y:1/0}};for(var s in this.map)if(this.map[s].length){var r=s>>16,o=s<<16>>16;if(0>o&&(r=-1^r),r>=i.max.x){i.max.x=r;for(t in this.map[s])e=this.map[s][t],"object"==typeof e&&"requires"in e&&(n.max.x=Math.max(n.max.x,e.x+e.w))}if(i.min.x>=r){i.min.x=r;for(t in this.map[s])e=this.map[s][t],"object"==typeof e&&"requires"in e&&(n.min.x=Math.min(n.min.x,e.x))}if(o>=i.max.y){i.max.y=o;for(t in this.map[s])e=this.map[s][t],"object"==typeof e&&"requires"in e&&(n.max.y=Math.max(n.max.y,e.y+e.h))}if(i.min.y>=o){i.min.y=o;for(t in this.map[s])e=this.map[s][t],"object"==typeof e&&"requires"in e&&(n.min.y=Math.min(n.min.y,e.y))}}return n}},s.key=function(t,e){return t._mbr&&(t=t._mbr),e||(e={}),e.x1=Math.floor(t._x/n),e.y1=Math.floor(t._y/n),e.x2=Math.floor((t._w+t._x)/n),e.y2=Math.floor((t._h+t._y)/n),e},s.hash=function(t){return t.x1+r+t.y1+r+t.x2+r+t.y2},i.prototype={update:function(t){s.hash(s.key(t,o))!=s.hash(this.keys)&&this.map.refresh(this)}},e.exports=s},{"./core.js":9}],5:[function(t){var e=t("./core.js");window.document,e.easing=function(t){this.timePerFrame=1e3/e.timer.FPS(),this.duration=t,this.reset()},e.easing.prototype={duration:0,clock:0,steps:null,complete:!1,paused:!1,reset:function(){this.loops=1,this.clock=0,this.complete=!1,this.paused=!1},repeat:function(t){this.loops=t},setProgress:function(t,e){this.clock=this.duration*t,e!==void 0&&(this.loops=e)},pause:function(){this.paused=!0},resume:function(){this.paused=!1,this.complete=!1},tick:function(t){if(!this.paused&&!this.complete)for(this.clock+=t,this.frames=Math.floor(this.clock/this.timePerFrame);this.clock>=this.duration&&this.complete===!1;)this.loops--,this.loops>0?this.clock-=this.duration:this.complete=!0},time:function(){return Math.min(this.clock/this.duration,1)},value:function(){return this.time()}},e.c("SpriteAnimation",{_reels:null,_currentReelId:null,_currentReel:null,_isPlaying:!1,init:function(){this._reels={}},reel:function(t,i,n,s,r){if(0===arguments.length)return this._currentReelId;if(1===arguments.length&&"string"==typeof t){if(this._reels[t]===void 0)throw"The specified reel "+t+" is undefined.";return this.pauseAnimation(),this._currentReelId!==t&&(this._currentReelId=t,this._currentReel=this._reels[t],this._updateSprite(),this.trigger("ReelChange",this._currentReel)),this}var o,a,h,c,u;if(h=this.__tile+parseInt(this.__padding[0]||0,10),c=this.__tileh+parseInt(this.__padding[1]||0,10),o={id:t,frames:[],currentFrame:0,easing:new e.easing(i),defaultLoops:1},o.duration=o.easing.duration,"number"==typeof n)if(a=n,y=s,r>=0)for(;n+r>a;a++)o.frames.push([a*h,y*c]);else for(;a>n+r;a--)o.frames.push([a*h,y*c]);else{if(3!==arguments.length||"object"!=typeof n)throw"Urecognized arguments. Please see the documentation for 'reel(...)'.";for(a=0,toX=n.length-1;toX>=a;a++)u=n[a],o.frames.push([u[0]*h,u[1]*c])}return this._reels[t]=o,this},animate:function(t,e){"string"==typeof t&&this.reel(t);var i=this._currentReel;if(i===void 0||null===i)throw"No reel is specified, and there is no currently active reel.";return this.pauseAnimation(),e===void 0&&(e="number"==typeof t?t:1),i.easing.reset(),this.loops(e),this._setFrame(0),this.bind("EnterFrame",this._animationTick),this._isPlaying=!0,this.trigger("StartAnimation",i),this},resumeAnimation:function(){return this._isPlaying===!1&&null!==this._currentReel&&(this.bind("EnterFrame",this._animationTick),this._isPlaying=!0,this._currentReel.easing.resume(),this.trigger("StartAnimation",this._currentReel)),this},pauseAnimation:function(){return this._isPlaying===!0&&(this.unbind("EnterFrame",this._animationTick),this._isPlaying=!1,this._reels[this._currentReelId].easing.pause()),this},resetAnimation:function(){var t=this._currentReel;if(null===t)throw"No active reel to reset.";return this.reelPosition(0),t.easing.repeat(t.defaultLoops),this},loops:function(t){return 0===arguments.length?null!==this._currentReel?this._currentReel.easing.loops:0:(null!==this._currentReel&&(0>t&&(t=1/0),this._currentReel.easing.repeat(t),this._currentReel.defaultLoops=t),this)},reelPosition:function(t){if(null===this._currentReel)throw"No active reel.";if(0===arguments.length)return this._currentReel.currentFrame;var e,i=this._currentReel.frames.length;if("end"===t&&(t=i-1),1>t&&t>0)e=t,t=Math.floor(i*e);else{if(t!==Math.floor(t))throw"Position "+t+" is invalid.";0>t&&(t=i-1+t),e=t/i}return t=Math.min(t,i-1),t=Math.max(t,0),this._setProgress(e),this._setFrame(t),this},_animationTick:function(t){var e=this._reels[this._currentReelId];e.easing.tick(t.dt);var i=e.easing.value(),n=Math.min(Math.floor(e.frames.length*i),e.frames.length-1);this._setFrame(n),e.easing.complete===!0&&(this.trigger("AnimationEnd",this._currentReel),this.pauseAnimation())},_setFrame:function(t){var e=this._currentReel;t!==e.currentFrame&&(e.currentFrame=t,this._updateSprite(),this.trigger("FrameChange",e))},_updateSprite:function(){var t=this._currentReel,e=t.frames[t.currentFrame];this.__coord[0]=e[0],this.__coord[1]=e[1],this.trigger("Change")},_setProgress:function(t,e){this._currentReel.easing.setProgress(t,e)},isPlaying:function(t){return this._isPlaying?t?this._currentReelId===t:!!this._currentReelId:!1},getReel:function(t){if(0===arguments.length){if(!this._currentReelId)return null;t=this._currentReelId}return this._reels[t]}}),e.c("Tween",{init:function(){this.tweenGroup={},this.tweenStart={},this.tweens=[],this.bind("EnterFrame",this._tweenTick)},_tweenTick:function(t){var e,i,n;for(n=this.tweens.length-1;n>=0;n--)e=this.tweens[n],e.easing.tick(t.dt),i=e.easing.value(),this._doTween(e.props,i),e.easing.complete&&(this.tweens.splice(n,1),this._endTween(e.props))},_doTween:function(t,e){for(var i in t)this[i]=(1-e)*this.tweenStart[i]+e*t[i]},tween:function(t,i){var n={props:t,easing:new e.easing(i)};for(var s in t)this.tweenGroup[s]!==void 0&&this.cancelTween(s),this.tweenStart[s]=this[s],this.tweenGroup[s]=t;return this.tweens.push(n),this},cancelTween:function(t){if("string"==typeof t)"object"==typeof this.tweenGroup[t]&&delete this.tweenGroup[t][t];else if("object"==typeof t)for(var e in t)this.cancelTween(e);return this},_endTween:function(t){for(var e in t)delete this.tweenGroup[e];this.trigger("TweenEnd",t)}})},{"./core.js":9}],6:[function(t){var e=t("./core.js"),i=window.document;e.c("Canvas",{init:function(){e.canvas.context||e.canvas.init(),e.DrawManager.total2D++,this.currentRect={},this._changed=!0,e.DrawManager.addCanvas(this),this.bind("Change",function(){this._changed===!1&&(this._changed=!0,e.DrawManager.addCanvas(this))}),this.bind("Remove",function(){e.DrawManager.total2D--,this._changed=!0,e.DrawManager.addCanvas(this)})},drawVars:{type:"canvas",pos:{},ctx:null,coord:[0,0,0,0],co:{x:0,y:0,w:0,h:0}},draw:function(t,i,n,s,r){if(this.ready){4===arguments.length&&(r=s,s=n,n=i,i=t,t=e.canvas.context);var o=this.drawVars.pos;o._x=this._x+(i||0),o._y=this._y+(n||0),o._w=s||this._w,o._h=r||this._h,context=t||e.canvas.context,coord=this.__coord||[0,0,0,0];var a=this.drawVars.co;a.x=coord[0]+(i||0),a.y=coord[1]+(n||0),a.w=s||coord[2],a.h=r||coord[3],this._mbr&&(context.save(),context.translate(this._origin.x+this._x,this._origin.y+this._y),o._x=-this._origin.x,o._y=-this._origin.y,context.rotate(this._rotation%360*(Math.PI/180))),(this._flipX||this._flipY)&&(context.save(),context.scale(this._flipX?-1:1,this._flipY?-1:1),this._flipX&&(o._x=-(o._x+o._w)),this._flipY&&(o._y=-(o._y+o._h)));var h;return 1>this._alpha&&(h=context.globalAlpha,context.globalAlpha=this._alpha),this.drawVars.ctx=context,this.trigger("Draw",this.drawVars),(this._mbr||this._flipX||this._flipY)&&context.restore(),h&&(context.globalAlpha=h),this}}}),e.extend({canvas:{context:null,init:function(){if(!e.support.canvas)return e.trigger("NoCanvas"),e.stop(),void 0;var t;t=i.createElement("canvas"),t.width=e.viewport.width,t.height=e.viewport.height,t.style.position="absolute",t.style.left="0px",t.style.top="0px",e.stage.elem.appendChild(t),e.canvas.context=t.getContext("2d"),e.canvas._canvas=t;var n=e.viewport._scale;1!=n&&e.canvas.context.scale(n,n),e.unbind("RenderScene",e.DrawManager.renderCanvas),e.bind("RenderScene",e.DrawManager.renderCanvas)}}})},{"./core.js":9}],7:[function(t){var e=t("./core.js"),i=(window.document,Math.PI/180);e.c("Collision",{init:function(){this.requires("2D"),this._mbr||this,this.collision()},collision:function(t){if(this.unbind("Resize",this._resizeMap),t||(t=new e.polygon([0,0],[this._w,0],[this._w,this._h],[0,this._h]),this.bind("Resize",this._resizeMap)),arguments.length>1){var n=Array.prototype.slice.call(arguments,0);t=new e.polygon(n)}return this.rotation&&t.rotate({cos:Math.cos(-this.rotation*i),sin:Math.sin(-this.rotation*i),o:{x:this._origin.x,y:this._origin.y}}),this.map=t,this.attach(this.map),this.map.shift(this._x,this._y),this.trigger("NewHitbox",t),this},_resizeMap:function(t){var e,n,s=this.rotation*i,r=this.map.points;"w"===t.axis?(s?(e=t.amount*Math.cos(s),n=t.amount*Math.sin(s)):(e=t.amount,n=0),r[1][0]+=e,r[1][1]+=n):(s?(n=t.amount*Math.cos(s),e=-t.amount*Math.sin(s)):(e=0,n=t.amount),r[3][0]+=e,r[3][1]+=n),r[2][0]+=e,r[2][1]+=n},hit:function(t){var i,n,s,r,o=this._mbr||this,a=e.map.search(o,!1),h=0,c=a.length,u={},l="map"in this&&"containsPoint"in this.map,d=[];if(!c)return!1;for(;c>h;++h)n=a[h],s=n._mbr||n,n&&(i=n[0],!u[i]&&this[0]!==i&&n.__c[t]&&s._xo._x&&s._yo._y&&(u[i]=n));for(r in u)if(n=u[r],l&&"map"in n){var _=this._SAT(this.map,n.map);_.obj=n,_.type="SAT",_&&d.push(_)}else d.push({obj:n,type:"MBR"});return d.length?d:!1},onHit:function(t,e,i){var n=!1;return this.bind("EnterFrame",function(){var s=this.hit(t);s?(n=!0,e.call(this,s)):n&&("function"==typeof i&&i.call(this),n=!1)}),this},_SAT:function(t,e){for(var i,n,s,r,o,a,h,c,u,l,d=t.points,_=e.points,p=0,f=d.length,g=_.length,m={x:0,y:0},v=null,y=null,x=null;f>p;p++){for(u=d[p==f-1?0:p+1],l=d[p],m.x=-(u[1]-l[1]),m.y=u[0]-l[0],n=Math.sqrt(m.x*m.x+m.y*m.y),m.x/=n,m.y/=n,s=r=-1,o=a=-1,i=0;f>i;++i)c=d[i][0]*m.x+d[i][1]*m.y,(c>o||-1===o)&&(o=c),(s>c||-1===s)&&(s=c);for(i=0;g>i;++i)c=_[i][0]*m.x+_[i][1]*m.y,(c>a||-1===a)&&(a=c),(r>c||-1===r)&&(r=c);if(r>s?(h=r-o,m.x=-m.x,m.y=-m.y):h=s-a,h>=0)return!1;(null===v||h>v)&&(v=h,x={x:m.x,y:m.y})}for(p=0;g>p;p++){for(u=_[p==g-1?0:p+1],l=_[p],m.x=-(u[1]-l[1]),m.y=u[0]-l[0],n=Math.sqrt(m.x*m.x+m.y*m.y),m.x/=n,m.y/=n,s=r=-1,o=a=-1,i=0;f>i;++i)c=d[i][0]*m.x+d[i][1]*m.y,(c>o||-1===o)&&(o=c),(s>c||-1===s)&&(s=c);for(i=0;g>i;++i)c=_[i][0]*m.x+_[i][1]*m.y,(c>a||-1===a)&&(a=c),(r>c||-1===r)&&(r=c); -if(r>s?(h=r-o,m.x=-m.x,m.y=-m.y):h=s-a,h>=0)return!1;(null===v||h>v)&&(v=h),(h>y||null===y)&&(y=h,x={x:m.x,y:m.y})}return{overlap:y,normal:x}}})},{"./core.js":9}],8:[function(t){var e=t("./core.js"),i=window.document;e.extend({over:null,mouseObjs:0,mousePos:{},lastEvent:null,keydown:{},selected:!1,detectBlur:function(t){var i=t.clientX>e.stage.x&&t.clientXe.stage.y&&t.clientYt.button?e.mouseButtons.LEFT:4==t.button?e.mouseButtons.MIDDLE:e.mouseButtons.RIGHT:2>t.which?e.mouseButtons.LEFT:2==t.which?e.mouseButtons.MIDDLE:e.mouseButtons.RIGHT,t.realX=r=e.mousePos.x=c.x,t.realY=o=e.mousePos.y=c.y,"CANVAS"!=l.nodeName){for(;"string"!=typeof l.id&&-1==l.id.indexOf("ent");)l=l.parentNode;ent=e(parseInt(l.id.replace("ent",""),10)),ent.has("Mouse")&&ent.isAt(r,o)&&(i=ent)}if(!i)for(n=e.map.search({_x:r,_y:o,_w:1,_h:1},!1),s=n.length;s>h;++h)if(n[h].__c.Mouse&&n[h]._visible){var _=n[h],p=!1;if(!u[_[0]]&&(u[_[0]]=!0,_.mapArea?_.mapArea.containsPoint(r,o)&&(p=!0):_.isAt(r,o)&&(p=!0),p&&(_._z>=a||-1===a))){if(_._z===a&&_[0]=112&&135>=t.key?void 0:(t.stopPropagation?t.stopPropagation():t.cancelBubble=!0,t.target&&"INPUT"!==t.target.nodeName&&"TEXTAREA"!==t.target.nodeName&&(t.preventDefault?t.preventDefault():t.returnValue=!1),!1)}}),e.bind("Load",function(){e.addEvent(this,"keydown",e.keyboardDispatch),e.addEvent(this,"keyup",e.keyboardDispatch),e.addEvent(this,e.stage.elem,"mousedown",e.mouseDispatch),e.addEvent(this,e.stage.elem,"mouseup",e.mouseDispatch),e.addEvent(this,i.body,"mouseup",e.detectBlur),e.addEvent(this,e.stage.elem,"mousemove",e.mouseDispatch),e.addEvent(this,e.stage.elem,"click",e.mouseDispatch),e.addEvent(this,e.stage.elem,"dblclick",e.mouseDispatch),e.addEvent(this,e.stage.elem,"touchstart",e.touchDispatch),e.addEvent(this,e.stage.elem,"touchmove",e.touchDispatch),e.addEvent(this,e.stage.elem,"touchend",e.touchDispatch),e.addEvent(this,e.stage.elem,"touchcancel",e.touchDispatch),e.addEvent(this,e.stage.elem,"touchleave",e.touchDispatch)}),e.bind("CraftyStop",function(){e.removeEvent(this,"keydown",e.keyboardDispatch),e.removeEvent(this,"keyup",e.keyboardDispatch),e.stage&&(e.removeEvent(this,e.stage.elem,"mousedown",e.mouseDispatch),e.removeEvent(this,e.stage.elem,"mouseup",e.mouseDispatch),e.removeEvent(this,e.stage.elem,"mousemove",e.mouseDispatch),e.removeEvent(this,e.stage.elem,"click",e.mouseDispatch),e.removeEvent(this,e.stage.elem,"dblclick",e.mouseDispatch),e.removeEvent(this,e.stage.elem,"touchstart",e.touchDispatch),e.removeEvent(this,e.stage.elem,"touchmove",e.touchDispatch),e.removeEvent(this,e.stage.elem,"touchend",e.touchDispatch),e.removeEvent(this,e.stage.elem,"touchcancel",e.touchDispatch),e.removeEvent(this,e.stage.elem,"touchleave",e.touchDispatch)),e.removeEvent(this,i.body,"mouseup",e.detectBlur)}),e.c("Mouse",{init:function(){e.mouseObjs++,this.bind("Remove",function(){e.mouseObjs--})},areaMap:function(t){if(arguments.length>1){var i=Array.prototype.slice.call(arguments,0);t=new e.polygon(i)}return t.shift(this._x,this._y),this.mapArea=t,this.attach(this.mapArea),this}}),e.c("Draggable",{_origMouseDOMPos:null,_oldX:null,_oldY:null,_dragging:!1,_dir:null,init:function(){this.requires("Mouse"),this.enableDrag()},_ondrag:function(t){var i=e.DOM.translate(t.clientX,t.clientY);if(0===i.x||0===i.y)return!1;if(this._dir){var n=(i.x-this._origMouseDOMPos.x)*this._dir.x+(i.y-this._origMouseDOMPos.y)*this._dir.y;this.x=this._oldX+n*this._dir.x,this.y=this._oldY+n*this._dir.y}else this.x=this._oldX+(i.x-this._origMouseDOMPos.x),this.y=this._oldY+(i.y-this._origMouseDOMPos.y);this.trigger("Dragging",t)},_ondown:function(t){t.mouseButton===e.mouseButtons.LEFT&&this._startDrag(t)},_onup:function(t){this._dragging===!0&&(e.removeEvent(this,e.stage.elem,"mousemove",this._ondrag),e.removeEvent(this,e.stage.elem,"mouseup",this._onup),this._dragging=!1,this.trigger("StopDrag",t))},dragDirection:function(t){if(t===void 0)this._dir=null;else if(""+parseInt(t,10)==t)this._dir={x:Math.cos(t/180*Math.PI),y:Math.sin(t/180*Math.PI)};else{var e=Math.sqrt(t.x*t.x+t.y*t.y);this._dir={x:t.x/e,y:t.y/e}}},_startDrag:function(t){this._origMouseDOMPos=e.DOM.translate(t.clientX,t.clientY),this._oldX=this._x,this._oldY=this._y,this._dragging=!0,e.addEvent(this,e.stage.elem,"mousemove",this._ondrag),e.addEvent(this,e.stage.elem,"mouseup",this._onup),this.trigger("StartDrag",t)},stopDrag:function(){return e.removeEvent(this,e.stage.elem,"mousemove",this._ondrag),e.removeEvent(this,e.stage.elem,"mouseup",this._onup),this._dragging=!1,this.trigger("StopDrag"),this},startDrag:function(){return this._dragging||this._startDrag(e.lastEvent),this},enableDrag:function(){return this.bind("MouseDown",this._ondown),e.addEvent(this,e.stage.elem,"mouseup",this._onup),this},disableDrag:function(){return this.unbind("MouseDown",this._ondown),this._dragging&&this.stopDrag(),this}}),e.c("Keyboard",{isDown:function(t){return"string"==typeof t&&(t=e.keys[t]),!!e.keydown[t]}}),e.c("Multiway",{_speed:3,_keydown:function(t){this._keys[t.key]&&(this._movement.x=Math.round(1e3*(this._movement.x+this._keys[t.key].x))/1e3,this._movement.y=Math.round(1e3*(this._movement.y+this._keys[t.key].y))/1e3,this.trigger("NewDirection",this._movement))},_keyup:function(t){this._keys[t.key]&&(this._movement.x=Math.round(1e3*(this._movement.x-this._keys[t.key].x))/1e3,this._movement.y=Math.round(1e3*(this._movement.y-this._keys[t.key].y))/1e3,this.trigger("NewDirection",this._movement))},_enterframe:function(){this.disableControls||(0!==this._movement.x&&(this.x+=this._movement.x,this.trigger("Moved",{x:this.x-this._movement.x,y:this.y})),0!==this._movement.y&&(this.y+=this._movement.y,this.trigger("Moved",{x:this.x,y:this.y-this._movement.y})))},_initializeControl:function(){return this.unbind("KeyDown",this._keydown).unbind("KeyUp",this._keyup).unbind("EnterFrame",this._enterframe).bind("KeyDown",this._keydown).bind("KeyUp",this._keyup).bind("EnterFrame",this._enterframe)},multiway:function(t,i){this._keyDirection={},this._keys={},this._movement={x:0,y:0},this._speed={x:3,y:3},i?void 0!==t.x&&void 0!==t.y?(this._speed.x=t.x,this._speed.y=t.y):(this._speed.x=t,this._speed.y=t):i=t,this._keyDirection=i,this.speed(this._speed),this._initializeControl();for(var n in i)e.keydown[e.keys[n]]&&this.trigger("KeyDown",{key:e.keys[n]});return this},enableControl:function(){return this.disableControls=!1,this},disableControl:function(){return this.disableControls=!0,this},speed:function(t){for(var i in this._keyDirection){var n=e.keys[i]||i;this._keys[n]={x:Math.round(1e3*Math.cos(this._keyDirection[i]*(Math.PI/180))*t.x)/1e3,y:Math.round(1e3*Math.sin(this._keyDirection[i]*(Math.PI/180))*t.y)/1e3}}return this}}),e.c("Fourway",{init:function(){this.requires("Multiway")},fourway:function(t){return this.multiway(t,{UP_ARROW:-90,DOWN_ARROW:90,RIGHT_ARROW:0,LEFT_ARROW:180,W:-90,S:90,D:0,A:180,Z:-90,Q:180}),this}}),e.c("Twoway",{_speed:3,_up:!1,init:function(){this.requires("Fourway, Keyboard")},twoway:function(t,i){return this.multiway(t,{RIGHT_ARROW:0,LEFT_ARROW:180,D:0,A:180,Q:180}),t&&(this._speed=t),this._jumpSpeed=2>arguments.length?2*this._speed:i,this.bind("EnterFrame",function(){this.disableControls||this._up&&(this.y-=this._jumpSpeed,this._falling=!0)}).bind("KeyDown",function(t){(t.key===e.keys.UP_ARROW||t.key===e.keys.W||t.key===e.keys.Z)&&(this._up=!0)}),this}})},{"./core.js":9}],9:[function(t,e,i){function n(){var t=r++;return t in h?n():t}function s(t){if(null===t||"object"!=typeof t)return t;var e=t.constructor();for(var i in t)e[i]=s(t[i]);return e}var r,o,a,h,c,u,l,d,_,p,f=t("./version"),g=function(t){return new g.fn.init(t)};initState=function(){r=1,a={},h={},c={},u=[],d=Array.prototype.slice,_=/\s*,\s*/,p=/\s+/},initState(),g.fn=g.prototype={init:function(t){if("string"!=typeof t)return t||(t=0,t in h||(h[t]=this)),t in h?(this[0]=t,this.length=1,this.__c||(this.__c={}),h[t]||(h[t]=this),h[t]):(this.length=0,this);var e,i,n,s,r,o,c,u=0,l=!1,d=!1;if("*"===t){o=0;for(e in h)this[o]=+e,o++;return this.length=o,1===o?h[this[0]]:this}-1!==t.indexOf(",")?(d=!0,n=_):-1!==t.indexOf(" ")&&(l=!0,n=p);for(e in h)if(h.hasOwnProperty(e))if(i=h[e],l||d){for(s=t.split(n),o=0,c=s.length,r=0;c>o;o++)i.__c[s[o]]&&r++;(l&&r===c||d&&r>0)&&(this[u++]=+e)}else i.__c[t]&&(this[u++]=+e);if(u>0&&!l&&!d&&this.extend(a[t]),s&&l)for(o=0;c>o;o++)this.extend(a[s[o]]);return this.length=u,1===u?h[this[u-1]]:this},setName:function(t){var e=t+"";return this._entityName=e,this.trigger("NewEntityName",e),this},addComponent:function(t){var e,i,n,s,r=[],o=0,h=0;if(arguments.length>1)for(i=arguments.length;i>h;h++)r.push(arguments[h]);else if(-1!==t.indexOf(","))for(n=t.split(_),i=n.length;i>h;h++)r.push(n[h]);else r.push(t);for(e=r.length;e>o;o++)this.__c[r[o]]!==!0&&(this.__c[r[o]]=!0,s=a[r[o]],this.extend(s),s&&"init"in s&&s.init.call(this));return this.trigger("NewComponent",r),this},toggleComponent:function(t){var e,i,n=0;if(arguments.length>1)for(e=arguments.length;e>n;n++)this.has(arguments[n])?this.removeComponent(arguments[n]):this.addComponent(arguments[n]);else if(-1!==t.indexOf(","))for(i=t.split(_),e=i.length;e>n;n++)this.has(i[n])?this.removeComponent(i[n]):this.addComponent(i[n]);else this.has(t)?this.removeComponent(t):this.addComponent(t);return this},requires:function(t){return this.addComponent(t)},removeComponent:function(t,e){var i=a[t];if(this.trigger("RemoveComponent",t),i&&"remove"in i&&i.remove.call(this,!1),e===!1&&i)for(var n in i)delete this[n];return delete this.__c[t],this},getId:function(){return this[0]},has:function(t){return!!this.__c[t]},attr:function(t,e){if(1===arguments.length)return"string"==typeof t?this[t]:(this.extend(t),this.trigger("Change",t),this);this[t]=e;var i={};return i[t]=e,this.trigger("Change",i),this},toArray:function(){return d.call(this,0)},timeout:function(t,e){return this.each(function(){var i=this;setTimeout(function(){t.call(i)},e)}),this},bind:function(t,e){if(1===this.length){c[t]||(c[t]={});var i=c[t];return i[this[0]]||(i[this[0]]=[]),i[this[0]].push(e),this}return this.each(function(){c[t]||(c[t]={});var i=c[t];i[this[0]]||(i[this[0]]=[]),i[this[0]].push(e)}),this},uniqueBind:function(t,e){this.unbind(t,e),this.bind(t,e)},one:function(t,e){var i=this,n=function(s){e.call(i,s),i.unbind(t,n)};return i.bind(t,n)},unbind:function(t,e){return this.each(function(){var i,n,s=c[t],r=0;if(!s||!s[this[0]])return this;if(i=s[this[0]].length,!e)return delete s[this[0]],this;for(;i>r;r++)n=s[this[0]],n[r]==e&&delete n[r]}),this},trigger:function(t,e){if(1===this.length){if(c[t]&&c[t][this[0]]){var i,n=c[t][this[0]];for(i=0;n.length>i;i++)n[i]===void 0?(n.splice(i,1),i--):n[i].call(this,e)}return this}return this.each(function(){if(c[t]&&c[t][this[0]]){var i,n=c[t][this[0]];for(i=0;n.length>i;i++)n[i]===void 0?(n.splice(i,1),i--):n[i].call(this,e)}}),this},each:function(t){for(var e=0,i=this.length;i>e;e++)h[this[e]]&&t.call(h[this[e]],e);return this},clone:function(){var t,e,i=this.__c,n=g.e();for(t in i)n.addComponent(t);for(e in this)"0"!=e&&"_global"!=e&&"_changed"!=e&&"function"!=typeof this[e]&&"object"!=typeof this[e]&&(n[e]=this[e]);return n},setter:function(t,e){return g.support.setter?this.__defineSetter__(t,e):g.support.defineProperty?Object.defineProperty(this,t,{set:e,configurable:!0}):l.push({prop:t,obj:this,fn:e}),this},destroy:function(){this.each(function(){var t;this.trigger("Remove");for(var e in this.__c)t=a[e],t&&"remove"in t&&t.remove.call(this,!0);for(var i in c)this.unbind(i);delete h[this[0]]})}},g.fn.init.prototype=g.fn,g.extend=g.fn.extend=function(t){var e,i=this;if(!t)return i;for(e in t)i!==t[e]&&(i[e]=t[e]);return i},g.extend({init:function(t,e,i){return g.viewport.init(t,e,i),this.trigger("Load"),this.timer.init(),this},getVersion:function(){return f},stop:function(t){if(this.timer.stop(),t){if(g.audio.remove(),g.stage&&g.stage.elem.parentNode){var e=document.createElement("div");e.id=g.stage.elem.id,g.stage.elem.parentNode.replaceChild(e,g.stage.elem)}initState()}return g.trigger("CraftyStop"),this},pause:function(t){return(1===arguments.length?t:!this._paused)?(this.trigger("Pause"),this._paused=!0,setTimeout(function(){g.timer.stop()},0),g.keydown={}):(this.trigger("Unpause"),this._paused=!1,setTimeout(function(){g.timer.init()},0)),this},isPaused:function(){return this._paused},timer:function(){var t,e,i,n="fixed",s=5,r=40,o=0,a=0,h=0,c=50,u=1e3/c;return{init:function(){i===void 0&&(i=(new Date).getTime()-u);var n=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||null;n?(t=function(){g.timer.step(),e=n(t)},t()):t=setInterval(function(){g.timer.step()},1e3/c)},stop:function(){g.trigger("CraftyStopTimer"),"number"==typeof t&&clearInterval(t);var i=window.cancelRequestAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelRequestAnimationFrame||window.oCancelRequestAnimationFrame||window.msCancelRequestAnimationFrame||null;i&&i(e),t=null},steptype:function(t,e){if("variable"===t||"semifixed"===t)n=t,e&&(r=e);else{if("fixed"!==t)throw"Invalid step type specified";n="fixed",e&&(s=e)}},step:function(){var t,e,c,l=0;if(currentTime=(new Date).getTime(),o>0&&g.trigger("MeasureWaitTime",currentTime-o),i+a>=currentTime)return o=currentTime,void 0;var d=currentTime-(i+a);d>20*u&&(a+=d-u,d=u),"fixed"===n?(l=Math.ceil(d/u),l=Math.min(l,s),e=u):"variable"===n?(l=1,e=d,e=Math.min(e,r)):"semifixed"===n&&(l=Math.ceil(d/r),e=d/l);for(var _=0;l>_;_++)c=currentTime,g.trigger("EnterFrame",{frame:h++,dt:e,gameTime:i}),i+=e,currentTime=(new Date).getTime(),g.trigger("MeasureFrameTime",currentTime-c);l>0&&(t=currentTime,g.trigger("RenderScene"),g.trigger("PostRender"),currentTime=(new Date).getTime(),g.trigger("MeasureRenderTime",currentTime-t)),o=currentTime},FPS:function(t){return t===void 0?c:(c=t,u=1e3/c,void 0)},simulateFrames:function(t,e){for(e===void 0&&(e=u);t-->0;)g.trigger("EnterFrame",{frame:h++,dt:e});g.trigger("RenderScene")}}}(),e:function(){var t,e=n();return h[e]=null,h[e]=t=g(e),arguments.length>0&&t.addComponent.apply(t,arguments),t.setName("Entity #"+e),t.addComponent("obj"),g.trigger("NewEntity",{id:e}),t},c:function(t,e){a[t]=e},trigger:function(t,e){var i,n,s,r,o=c[t];for(i in o)if(o.hasOwnProperty(i)&&(s=o[i],s&&0!==s.length))for(r=h[i]?g(+i):g,n=0;s.length>n;n++)s[n]===void 0?(s.splice(n,1),n--):s[n].call(r,e)},bind:function(t,e){c[t]||(c[t]={});var i=c[t];return i.global||(i.global=[]),i.global.push(e)-1},uniqueBind:function(t,e){this.unbind(t,e),this.bind(t,e)},one:function(t,e){var i=this,n=function(s){e.call(i,s),i.unbind(t,n)};return i.bind(t,n)},unbind:function(t,e){var i,n,s,r,o=c[t];if(void 0===o||void 0===o.global||0===o.global.length)return!1;if(1===arguments.length)return delete o.global,!0;for(s=o.global,r=!1,i=0,n=s.length;n>i;i++)s[i]===e&&(r=!0,delete s[i]);return r},frame:function(){return o},components:function(){return a},isComp:function(t){return t in a},debug:function(t){return"handlers"===t?c:h},settings:function(){var t={},e={};return{register:function(t,i){e[t]=i},modify:function(i,n){e[i]&&(e[i].call(t[i],n),t[i]=n)},get:function(e){return t[e]}}}(),clone:s}),g.bind("Load",function(){!g.support.setter&&g.support.defineProperty&&(l=[],g.bind("EnterFrame",function(){for(var t,e=0,i=l.length;i>e;++e)t=l[e],t.obj[t.prop]!==t.obj["_"+t.prop]&&t.fn.call(t.obj,t.obj[t.prop])}))}),"function"==typeof define?define("crafty",[],function(){return g}):"object"==typeof i&&(e.exports=g),window.Crafty=g},{"./version":25}],10:[function(t){var e=t("./core.js");window.document,e.extend({device:{_deviceOrientationCallback:!1,_deviceMotionCallback:!1,_normalizeDeviceOrientation:function(t){var i;window.DeviceOrientationEvent?i={tiltLR:t.gamma,tiltFB:t.beta,dir:t.alpha,motUD:null}:window.OrientationEvent&&(i={tiltLR:90*t.x,tiltFB:-90*t.y,dir:null,motUD:t.z}),e.device._deviceOrientationCallback(i)},_normalizeDeviceMotion:function(t){var i=t.accelerationIncludingGravity,n=i.z>0?1:-1,s={acceleration:i,rawAcceleration:"["+Math.round(i.x)+", "+Math.round(i.y)+", "+Math.round(i.z)+"]",facingUp:n,tiltLR:Math.round(-90*(i.x/9.81)),tiltFB:Math.round(90*((i.y+9.81)/9.81)*n)};e.device._deviceMotionCallback(s)},deviceOrientation:function(t){this._deviceOrientationCallback=t,e.support.deviceorientation&&(window.DeviceOrientationEvent?e.addEvent(this,window,"deviceorientation",this._normalizeDeviceOrientation):window.OrientationEvent&&e.addEvent(this,window,"MozOrientation",this._normalizeDeviceOrientation))},deviceMotion:function(t){this._deviceMotionCallback=t,e.support.devicemotion&&window.DeviceMotionEvent&&e.addEvent(this,window,"devicemotion",this._normalizeDeviceMotion)}}})},{"./core.js":9}],11:[function(t){var e=t("./core.js");window.document,e.extend({diamondIso:{_tile:{width:0,height:0,r:0},_map:{width:0,height:0,x:0,y:0},_origin:{x:0,y:0},init:function(t,e,i,n){return this._tile.width=parseInt(t,10),this._tile.height=parseInt(e,10)||parseInt(t,10)/2,this._tile.r=this._tile.width/this._tile.height,this._map.width=parseInt(i,10),this._map.height=parseInt(n,10)||parseInt(i,10),this._origin.x=this._map.height*this._tile.width/2,this},place:function(t,e,i,n){var s=this.pos2px(e,i);n||(n=1);var r=0,o=0;void 0!==t.__margin&&(r=t.__margin[0],o=t.__margin[1]),t.x=s.left+r,t.y=s.top+o-t.h,t.z=s.top*n},centerAt:function(t,i){var n=this.pos2px(t,i);e.viewport.x=-n.left+e.viewport.width/2-this._tile.width,e.viewport.y=-n.top+e.viewport.height/2},area:function(t){t||(t=0);var i=e.viewport.rect(),n=t*this._tile.width,s=t*this._tile.height;i._x-=this._tile.width/2+n,i._y-=this._tile.height/2+s,i._w+=this._tile.width/2+n,i._h+=this._tile.height/2+s;for(var r=[],o=i._y,a=i._y+i._h;a>o;o+=this._tile.height/2)for(var h=i._x,c=i._x+i._w;c>h;h+=this._tile.width/2){var u=this.px2pos(h,o);r.push([~~u.x,~~u.y])}return r},pos2px:function(t,e){return{left:(t-e)*this._tile.width/2+this._origin.x,top:(t+e)*this._tile.height/2}},px2pos:function(t,e){var i=(t-this._origin.x)/this._tile.r;return{x:(e+i)/this._tile.height,y:(e-i)/this._tile.height}},polygon:function(t){t.requires("Collision");var i=0,n=0;void 0!==t.__margin&&(i=t.__margin[0],n=t.__margin[1]);var s=[[i-0,t.h-n-this._tile.height/2],[i-this._tile.width/2,t.h-n-0],[i-this._tile.width,t.h-n-this._tile.height/2],[i-this._tile.width/2,t.h-n-this._tile.height]],r=new e.polygon(s);return r}}})},{"./core.js":9}],12:[function(t){var e=t("./core.js");window.document,e.c("Color",{_color:"",ready:!0,init:function(){this.bind("Draw",function(t){"DOM"===t.type?(t.style.backgroundColor=this._color,t.style.lineHeight=0):"canvas"===t.type&&(this._color&&(t.ctx.fillStyle=this._color),t.ctx.fillRect(t.pos._x,t.pos._y,t.pos._w,t.pos._h))})},color:function(t){return t?(this._color=t,this.trigger("Change"),this):this._color}}),e.c("Tint",{_color:null,_strength:1,init:function(){var t=function(t){var i=t.ctx||e.canvas.context;i.fillStyle=this._color||"rgba(0,0,0, 0)",i.fillRect(t.pos._x,t.pos._y,t.pos._w,t.pos._h)};this.bind("Draw",t).bind("RemoveComponent",function(e){"Tint"===e&&this.unbind("Draw",t)})},tint:function(t,i){return this._strength=i,this._color=e.toRGB(t,this._strength),this.trigger("Change"),this}}),e.c("Image",{_repeat:"repeat",ready:!1,init:function(){var t=function(t){if("canvas"===t.type){if(!this.ready||!this._pattern)return;var e=t.ctx;e.fillStyle=this._pattern,e.save(),e.translate(t.pos._x,t.pos._y),e.fillRect(0,0,this._w,this._h),e.restore()}else"DOM"===t.type&&this.__image&&(t.style.background="url("+this.__image+") "+this._repeat)};this.bind("Draw",t).bind("RemoveComponent",function(e){"Image"===e&&this.unbind("Draw",t)})},image:function(t,i){if(this.__image=t,this._repeat=i||"no-repeat",this.img=e.asset(t),!this.img){this.img=new Image,e.asset(t,this.img),this.img.src=t;var n=this;return this.img.onload=function(){n.has("Canvas")&&(n._pattern=e.canvas.context.createPattern(n.img,n._repeat)),n.ready=!0,"no-repeat"===n._repeat&&(n.w=n.img.width,n.h=n.img.height),n.trigger("Change")},this}return this.ready=!0,this.has("Canvas")&&(this._pattern=e.canvas.context.createPattern(this.img,this._repeat)),"no-repeat"===this._repeat&&(this.w=this.img.width,this.h=this.img.height),this.trigger("Change"),this}}),e.extend({_scenes:{},_current:null,scene:function(t,i,n){if(1===arguments.length){e.trigger("SceneDestroy",{newScene:t}),e.viewport.reset(),e("2D").each(function(){this.has("Persist")||this.destroy()}),null!==this._current&&"uninitialize"in this._scenes[this._current]&&this._scenes[this._current].uninitialize.call(this);var s=this._current;return this._current=t,e.trigger("SceneChange",{oldScene:s,newScene:t}),this._scenes[t].initialize.call(this),void 0}this._scenes[t]={},this._scenes[t].initialize=i,n!==void 0&&(this._scenes[t].uninitialize=n)},toRGB:function(t,e){t="#"===t.charAt(0)?t.substr(1):t;var i,n=[];return n[0]=parseInt(t.substr(0,2),16),n[1]=parseInt(t.substr(2,2),16),n[2]=parseInt(t.substr(4,2),16),i=void 0===e?"rgb("+n.join(",")+")":"rgba("+n.join(",")+","+e+")"}}),e.DrawManager=function(){function t(t,e){return t._globalZ-e._globalZ}var i=[],n=[],s=[],r=!1,o={merge:function(t,e,i){return i===void 0&&(i={}),i._h=Math.max(t._y+t._h,e._y+e._h),i._w=Math.max(t._x+t._w,e._x+e._w),i._x=Math.min(t._x,e._x),i._y=Math.min(t._y,e._y),i._w-=i._x,i._h-=i._y,i},clean:function(){var t,e,s;for(s=0,l=n.length;l>s;s++)e=n[s],t=e._mbr||e,e.staleRect===void 0&&(e.staleRect={}),e.staleRect._x=t._x,e.staleRect._y=t._y,e.staleRect._w=t._w,e.staleRect._h=t._h,e._changed=!1;n.length=0,i.length=0},createDirty:function(t){var e=t._mbr||t;if(t.staleRect){if(o.overlap(t.staleRect,e))return o.merge(t.staleRect,e,t.staleRect),i.push(t.staleRect),void 0;i.push(t.staleRect)}t.currentRect._x=e._x,t.currentRect._y=e._y,t.currentRect._w=e._w,t.currentRect._h=e._h,i.push(t.currentRect)},overlap:function(t,e){return t._xe._x&&t._y+t._h>e._y}};return e.bind("InvalidateViewport",function(){r=!0}),e.bind("PostRender",function(){r=!1}),{total2D:e("2D").length,onScreen:function(t){return e.viewport._x+t._x+t._w>0&&e.viewport._y+t._y+t._h>0&&e.viewport._x+t._xe;)o.overlap(t[e],t[e+1])?(o.merge(t[e],t[e+1],t[e]),t.splice(e+1,1),e>0&&e--):e++;return t},addCanvas:function(t){n.push(t)},addDom:function(t){s.push(t)},debug:function(){console.log(n,s)},drawAll:function(i){i=i||e.viewport.rect();var n,s=e.map.search(i),r=0,o=s.length,a=e.canvas.context;for(a.clearRect(i._x,i._y,i._w,i._h),s.sort(t);o>r;r++)n=s[r],n._visible&&n.__c.Canvas&&(n.draw(),n._changed=!1)},boundingRect:function(t){if(t&&t.length){var e,i,n=1,s=t.length,r=t[0];for(r=[r._x,r._y,r._x+r._w,r._y+r._h];s>n;)e=t[n],i=[e._x,e._y,e._x+e._w,e._y+e._h],i[0]r[2]&&(r[2]=i[2]),i[3]>r[3]&&(r[3]=i[3]),n++;return i=r,r={_x:i[0],_y:i[1],_w:i[2]-i[0],_h:i[3]-i[1]}}},renderCanvas:function(){var s=n.length;if(s||r){var a,h,c,u,l,d=0,_=e.canvas.context,p=e.DrawManager;if(r){var f=e.viewport;_.setTransform(f._scale,0,0,f._scale,f.x,f.y)}if(s/p.total2D>.6||r)return p.drawAll(),o.clean(),void 0;for(d=0;s>d;d++)o.createDirty(n[d]);i=p.mergeSet(i),s=i.length;var g=[],m=[];for(d=0;s>d;++d)if(a=i[d],g.length=0,m.length=0,a){for(a._w=a._x+a._w,a._h=a._y+a._h,a._x=a._x>0?0|a._x:(0|a._x)-1,a._y=a._y>0?0|a._y:(0|a._y)-1,a._w-=a._x,a._h-=a._y,a._w=a._w===(0|a._w)?a._w:(0|a._w)+1,a._h=a._h===(0|a._h)?a._h:(0|a._h)+1,h=e.map.search(a,!1),_.clearRect(a._x,a._y,a._w,a._h),_.save(),_.beginPath(),_.rect(a._x,a._y,a._w,a._h),_.clip(),c=0,u=h.length;u>c;++c)l=h[c],!g[l[0]]&&l._visible&&l.__c.Canvas&&(g[l[0]]=!0,m.push(l));for(m.sort(t),c=0,u=m.length;u>c;++c){l=m[c];var v=l._mbr||l;o.overlap(v,a)&&l.draw(),l._changed=!1}_.closePath(),_.restore()}if(e.DrawManager.debugDirty===!0)for(_.strokeStyle="red",d=0,s=i.length;s>d;++d)a=i[d],_.strokeRect(a._x,a._y,a._w,a._h);o.clean()}},renderDOM:function(){if(r){var t=e.stage.inner.style,i=e.viewport;t.transform=t[e.support.prefix+"Transform"]="scale("+i._scale+", "+i._scale+")",t.left=i.x+"px",t.top=i.y+"px",t.zIndex=10}if(s.length){for(var n=0,o=s.length;o>n;++n)s[n].draw()._changed=!1;s.length=0}}}}()},{"./core.js":9}],13:[function(t){var e=t("./core.js"),i=window.document;(function(){var t=e.support={},n=navigator.userAgent.toLowerCase(),s=/(webkit)[ \/]([\w.]+)/.exec(n)||/(o)pera(?:.*version)?[ \/]([\w.]+)/.exec(n)||/(ms)ie ([\w.]+)/.exec(n)||/(moz)illa(?:.*? rv:([\w.]+))?/.exec(n)||[],r=/iPad|iPod|iPhone|Android|webOS|IEMobile/i.exec(n);if(r&&(e.mobile=r[0]),t.setter="__defineSetter__"in this&&"__defineGetter__"in this,t.defineProperty=function(){if(!("defineProperty"in Object))return!1;try{Object.defineProperty({},"x",{})}catch(t){return!1}return!0}(),t.audio="Audio"in window,t.prefix=s[1]||s[0],"moz"===t.prefix&&(t.prefix="Moz"),"o"===t.prefix&&(t.prefix="O"),s[2]&&(t.versionName=s[2],t.version=+s[2].split(".")[0]),t.canvas="getContext"in i.createElement("canvas"),t.canvas){var o;try{o=i.createElement("canvas").getContext("experimental-webgl"),o.viewportWidth=t.canvas.width,o.viewportHeight=t.canvas.height}catch(a){}t.webgl=!!o}else t.webgl=!1;t.css3dtransform=i.createElement("div").style.Perspective!==void 0||i.createElement("div").style[t.prefix+"Perspective"]!==void 0,t.deviceorientation=window.DeviceOrientationEvent!==void 0||window.OrientationEvent!==void 0,t.devicemotion=window.DeviceMotionEvent!==void 0})(),e.extend({zeroFill:function(t,e){return e-=(""+t).length,e>0?Array(e+(/\./.test(t)?2:1)).join("0")+t:""+t},sprite:function(t,i,n,s,r,o){var a,h,c,u,l,d,_;"string"==typeof t&&(o=r,r=s,s=i,n=t,t=1,i=1),"string"==typeof i&&(o=r,r=s,s=n,n=i,i=t),!o&&r&&(o=r),r=parseInt(r||0,10),o=parseInt(o||0,10);var p=function(){this.ready=!0,this.trigger("Change")};_=e.asset(n),_||(_=new Image,_.src=n,e.asset(n,_),_.onload=function(){for(var t in s)e(t).each(p)});var f=function(){this.requires("2D, Sprite"),this.__trim=[0,0,0,0],this.__image=n,this.__coord=[this.__coord[0],this.__coord[1],this.__coord[2],this.__coord[3]],this.__tile=t,this.__tileh=i,this.__padding=[r,o],this.img=_,this.img.complete&&this.img.width>0&&(this.ready=!0,this.trigger("Change")),this.w=this.__coord[2],this.h=this.__coord[3]};for(a in s)s.hasOwnProperty(a)&&(h=s[a],c=h[0]*(t+r),u=h[1]*(i+o),l=h[2]*t||t,d=h[3]*i||i,e.c(a,{ready:!1,__coord:[c,u,l,d],init:f}));return this},_events:{},addEvent:function(t,e,i,n){3===arguments.length&&(n=i,i=e,e=window.document);var s=function(e){e=e||window.event,"function"==typeof n&&n.call(t,e)},r=t[0]||"";this._events[r+e+i+n]||(this._events[r+e+i+n]=s,e.attachEvent?e.attachEvent("on"+i,s):e.addEventListener(i,s,!1))},removeEvent:function(t,e,i,n){3===arguments.length&&(n=i,i=e,e=window.document);var s=t[0]||"",r=this._events[s+e+i+n];r&&(e.detachEvent?e.detachEvent("on"+i,r):e.removeEventListener(i,r,!1),delete this._events[s+e+i+n])},background:function(t){e.stage.elem.style.background=t},keys:{BACKSPACE:8,TAB:9,ENTER:13,PAUSE:19,CAPS:20,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT_ARROW:37,UP_ARROW:38,RIGHT_ARROW:39,DOWN_ARROW:40,INSERT:45,DELETE:46,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,A:65,B:66,C:67,D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,NUMPAD_0:96,NUMPAD_1:97,NUMPAD_2:98,NUMPAD_3:99,NUMPAD_4:100,NUMPAD_5:101,NUMPAD_6:102,NUMPAD_7:103,NUMPAD_8:104,NUMPAD_9:105,MULTIPLY:106,ADD:107,SUBSTRACT:109,DECIMAL:110,DIVIDE:111,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,SHIFT:16,CTRL:17,ALT:18,PLUS:187,COMMA:188,MINUS:189,PERIOD:190,PULT_UP:29460,PULT_DOWN:29461,PULT_LEFT:4,PULT_RIGHT:5},mouseButtons:{LEFT:0,MIDDLE:1,RIGHT:2}})},{"./core.js":9}],14:[function(t){var e=t("./core.js");window.document,e.c("HTML",{inner:"",init:function(){this.requires("2D, DOM")},replace:function(t){return this.inner=t,this._element.innerHTML=t,this},append:function(t){return this.inner+=t,this._element.innerHTML+=t,this},prepend:function(t){return this.inner=t+this.inner,this._element.innerHTML=t+this.inner,this}})},{"./core.js":9}],15:[function(t){var e=t("./core.js"),i=window.document;e["import"]=function(t,n){if("string"!=typeof t){var s,r,o,a,h=0;if(t.n&&"object"==typeof t.n)for(r=t.n.length;r>h;++h)o=t.n[h],a=e.e(o.c),delete o.c,a.attr(o);for(s in t)a=e(s),a.attr(t[s])}else if(levelData)n?e.import(levelData[n]):e.import(levelData);else{var c;c=i.createElement("script"),c.onload=function(){n?e.import(levelData[n]):e.import(levelData)},c.src=t}}},{"./core.js":9}],16:[function(t){var e=t("./core.js");window.document,e.extend({isometric:{_tile:{width:0,height:0},_elements:{},_pos:{x:0,y:0},_z:0,size:function(t,e){return this._tile.width=t,this._tile.height=e>0?e:t/2,this},place:function(t,i,n,s){var r=this.pos2px(t,i);return r.top-=n*(this._tile.height/2),s.attr({x:r.left+e.viewport._x,y:r.top+e.viewport._y}).z+=n,this},pos2px:function(t,e){return{left:t*this._tile.width+(1&e)*(this._tile.width/2),top:e*this._tile.height/2}},px2pos:function(t,e){return{x:-Math.ceil(-t/this._tile.width-.5*(1&e)),y:2*(e/this._tile.height)}},centerAt:function(t,i){if("number"==typeof t&&"number"==typeof i){var n=this.pos2px(t,i);return e.viewport._x=-n.left+e.viewport.width/2-this._tile.width/2,e.viewport._y=-n.top+e.viewport.height/2-this._tile.height/2,this}return{top:-e.viewport._y+e.viewport.height/2-this._tile.height/2,left:-e.viewport._x+e.viewport.width/2-this._tile.width/2}},area:function(){var t=this.centerAt(),i=this.px2pos(-t.left+e.viewport.width/2,-t.top+e.viewport.height/2),n=this.px2pos(-t.left-e.viewport.width/2,-t.top-e.viewport.height/2);return{x:{start:i.x,end:n.x},y:{start:i.y,end:n.y}} -}}})},{"./core.js":9}],17:[function(t){var e=t("./core.js"),i=window.document;e.extend({assets:{},asset:function(t,i){return 1===arguments.length?e.assets[t]:e.assets[t]?void 0:(e.assets[t]=i,this.trigger("NewAsset",{key:t,value:i}),i)},image_whitelist:["jpg","jpeg","gif","png","svg"],load:function(t,i,n,s){function r(){var t=this.src;this.removeEventListener&&this.removeEventListener("canplaythrough",r,!1),++d,n&&n({loaded:d,total:l,percent:100*(d/l),src:t}),d===l&&i&&i()}function o(){var t=this.src;s&&s({loaded:d,total:l,percent:100*(d/l),src:t}),d++,d===l&&i&&i()}for(var a,h,c=0,u=t.length,l=u,d=0,_="";u>c;++c){if(a=t[c],_=a.substr(a.lastIndexOf(".")+1,3).toLowerCase(),h=e.asset(a)||null,e.audio.supports(_)){if(!h){var p=a.substr(a.lastIndexOf("/")+1).toLowerCase();h=e.audio.create(p,a).obj}h.addEventListener&&h.addEventListener("canplaythrough",r,!1)}else{if(!(e.image_whitelist.indexOf(_)>=0)){l--;continue}h||(h=new Image,e.asset(a,h)),h.onload=r,"webkit"===e.support.prefix&&(h.src=""),h.src=a}h.onerror=o}0===l&&i()},modules:function(t,e,n){2===arguments.length&&"object"==typeof t&&(n=e,e=t,t="http://cdn.craftycomponents.com");var s=function(){function t(t,e,i){for(i=0,j=t.length;j>i;++i)if(!e(t[i]))return f;return 1}function e(e,i){t(e,function(t){return!i(t)})}function n(i,o,a){function h(t){return t.call?t():l[t]}function u(){if(!--y){l[v]=1,m&&m();for(var i in _)t(i.split("|"),h)&&!e(_[i],h)&&(_[i]=[])}}i=i[g]?i:[i];var f=o&&o.call,m=f?o:a,v=f?i.join(""):o,y=i.length;return setTimeout(function(){e(i,function(t){return p[t]?(v&&(d[v]=1),2==p[t]&&u()):(p[t]=1,v&&(d[v]=1),s(!c.test(t)&&r?r+t+".js":t,u),void 0)})},0),n}function s(t,e){var i=a.createElement("script"),n=f;i.onload=i.onerror=i[x]=function(){i[v]&&!/^c|loade/.test(i[v])||n||(i.onload=i[x]=null,n=1,p[t]=2,e())},i.async=1,i.src=t,h.insertBefore(i,h.firstChild)}var r,o=this,a=i,h=a.getElementsByTagName("head")[0],c=/^https?:\/\//,u=o.$script,l={},d={},_={},p={},f=!1,g="push",m="DOMContentLoaded",v="readyState",y="addEventListener",x="onreadystatechange";return!a[v]&&a[y]&&(a[y](m,function w(){a.removeEventListener(m,w,f),a[v]="complete"},f),a[v]="loading"),n.get=s,n.order=function(t,e,i){(function s(r){r=t.shift(),t.length?n(r,s):n(r,e,i)})()},n.path=function(t){r=t},n.ready=function(i,s,r){i=i[g]?i:[i];var o=[];return!e(i,function(t){l[t]||o[g](t)})&&t(i,function(t){return l[t]})?s():!function(t){_[t]=_[t]||[],_[t][g](s),r&&r(o)}(i.join("|")),n},n.noConflict=function(){return o.$script=u,this},n}(),r=[],o=/^(https?|file):\/\//;for(var a in e)o.test(a)?r.push(a):r.push(t+"/"+a.toLowerCase()+"-"+e[a].toLowerCase()+".js");s(r,function(){n&&n()})}})},{"./core.js":9}],18:[function(t){var e=t("./core.js");window.document,e.math={abs:function(t){return 0>t?-t:t},amountOf:function(t,e,i){return i>e?(t-e)/(i-e):(t-i)/(e-i)},clamp:function(t,e,i){return t>i?i:e>t?e:t},degToRad:function(t){return t*Math.PI/180},distance:function(t,i,n,s){var r=e.math.squaredDistance(t,i,n,s);return Math.sqrt(parseFloat(r))},lerp:function(t,e,i){return t+(e-t)*i},negate:function(t){return t>Math.random()?-1:1},radToDeg:function(t){return 180*t/Math.PI},randomElementOfArray:function(t){return t[Math.floor(t.length*Math.random())]},randomInt:function(t,e){return t+Math.floor((1+e-t)*Math.random())},randomNumber:function(t,e){return t+(e-t)*Math.random()},squaredDistance:function(t,e,i,n){return(t-i)*(t-i)+(e-n)*(e-n)},withinRange:function(t,e,i){return t>=e&&i>=t}},e.math.Vector2D=function(){function t(e,i){if(e instanceof t)this.x=e.x,this.y=e.y;else if(2===arguments.length)this.x=e,this.y=i;else if(arguments.length>0)throw"Unexpected number of arguments for Vector2D()"}return t.prototype.x=0,t.prototype.y=0,t.prototype.add=function(t){return this.x+=t.x,this.y+=t.y,this},t.prototype.angleBetween=function(t){return Math.atan2(this.x*t.y-this.y*t.x,this.x*t.x+this.y*t.y)},t.prototype.angleTo=function(t){return Math.atan2(t.y-this.y,t.x-this.x)},t.prototype.clone=function(){return new t(this)},t.prototype.distance=function(t){return Math.sqrt((t.x-this.x)*(t.x-this.x)+(t.y-this.y)*(t.y-this.y))},t.prototype.distanceSq=function(t){return(t.x-this.x)*(t.x-this.x)+(t.y-this.y)*(t.y-this.y)},t.prototype.divide=function(t){return this.x/=t.x,this.y/=t.y,this},t.prototype.dotProduct=function(t){return this.x*t.x+this.y*t.y},t.prototype.equals=function(e){return e instanceof t&&this.x==e.x&&this.y==e.y},t.prototype.getNormal=function(e){return void 0===e?new t(-this.y,this.x):new t(e.y-this.y,this.x-e.x).normalize()},t.prototype.isZero=function(){return 0===this.x&&0===this.y},t.prototype.magnitude=function(){return Math.sqrt(this.x*this.x+this.y*this.y)},t.prototype.magnitudeSq=function(){return this.x*this.x+this.y*this.y},t.prototype.multiply=function(t){return this.x*=t.x,this.y*=t.y,this},t.prototype.negate=function(){return this.x=-this.x,this.y=-this.y,this},t.prototype.normalize=function(){var t=Math.sqrt(this.x*this.x+this.y*this.y);return 0===t?(this.x=1,this.y=0):(this.x/=t,this.y/=t),this},t.prototype.scale=function(t,e){return void 0===e&&(e=t),this.x*=t,this.y*=e,this},t.prototype.scaleToMagnitude=function(t){var e=t/this.magnitude();return this.x*=e,this.y*=e,this},t.prototype.setValues=function(e,i){return e instanceof t?(this.x=e.x,this.y=e.y):(this.x=e,this.y=i),this},t.prototype.subtract=function(t){return this.x-=t.x,this.y-=t.y,this},t.prototype.toString=function(){return"Vector2D("+this.x+", "+this.y+")"},t.prototype.translate=function(t,e){return void 0===e&&(e=t),this.x+=t,this.y+=e,this},t.tripleProduct=function(t,i,n){var s=t.dotProduct(n),r=i.dotProduct(n);return new e.math.Vector2D(i.x*s-t.x*r,i.y*s-t.y*r)},t}(),e.math.Matrix2D=function(){return Matrix2D=function(t,e,i,n,s,r){if(t instanceof Matrix2D)this.a=t.a,this.b=t.b,this.c=t.c,this.d=t.d,this.e=t.e,this.f=t.f;else if(6===arguments.length)this.a=t,this.b=e,this.c=i,this.d=n,this.e=s,this.f=r;else if(arguments.length>0)throw"Unexpected number of arguments for Matrix2D()"},Matrix2D.prototype.a=1,Matrix2D.prototype.b=0,Matrix2D.prototype.c=0,Matrix2D.prototype.d=1,Matrix2D.prototype.e=0,Matrix2D.prototype.f=0,Matrix2D.prototype.apply=function(t){var e=t.x;return t.x=e*this.a+t.y*this.c+this.e,t.y=e*this.b+t.y*this.d+this.f,t},Matrix2D.prototype.clone=function(){return new Matrix2D(this)},Matrix2D.prototype.combine=function(t){var e=this.a;return this.a=e*t.a+this.b*t.c,this.b=e*t.b+this.b*t.d,e=this.c,this.c=e*t.a+this.d*t.c,this.d=e*t.b+this.d*t.d,e=this.e,this.e=e*t.a+this.f*t.c+t.e,this.f=e*t.b+this.f*t.d+t.f,this},Matrix2D.prototype.equals=function(t){return t instanceof Matrix2D&&this.a==t.a&&this.b==t.b&&this.c==t.c&&this.d==t.d&&this.e==t.e&&this.f==t.f},Matrix2D.prototype.determinant=function(){return this.a*this.d-this.b*this.c},Matrix2D.prototype.invert=function(){var t=this.determinant();if(0!==t){var e={a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f};this.a=e.d/t,this.b=-e.b/t,this.c=-e.c/t,this.d=e.a/t,this.e=(e.c*e.f-e.e*e.d)/t,this.f=(e.e*e.b-e.a*e.f)/t}return this},Matrix2D.prototype.isIdentity=function(){return 1===this.a&&0===this.b&&0===this.c&&1===this.d&&0===this.e&&0===this.f},Matrix2D.prototype.isInvertible=function(){return 0!==this.determinant()},Matrix2D.prototype.preRotate=function(t){var e=Math.cos(t),i=Math.sin(t),n=this.a;return this.a=e*n-i*this.b,this.b=i*n+e*this.b,n=this.c,this.c=e*n-i*this.d,this.d=i*n+e*this.d,this},Matrix2D.prototype.preScale=function(t,e){return void 0===e&&(e=t),this.a*=t,this.b*=e,this.c*=t,this.d*=e,this},Matrix2D.prototype.preTranslate=function(t,e){return"number"==typeof t?(this.e+=t,this.f+=e):(this.e+=t.x,this.f+=t.y),this},Matrix2D.prototype.rotate=function(t){var e=Math.cos(t),i=Math.sin(t),n=this.a;return this.a=e*n-i*this.b,this.b=i*n+e*this.b,n=this.c,this.c=e*n-i*this.d,this.d=i*n+e*this.d,n=this.e,this.e=e*n-i*this.f,this.f=i*n+e*this.f,this},Matrix2D.prototype.scale=function(t,e){return void 0===e&&(e=t),this.a*=t,this.b*=e,this.c*=t,this.d*=e,this.e*=t,this.f*=e,this},Matrix2D.prototype.setValues=function(t,e,i,n,s,r){return t instanceof Matrix2D?(this.a=t.a,this.b=t.b,this.c=t.c,this.d=t.d,this.e=t.e,this.f=t.f):(this.a=t,this.b=e,this.c=i,this.d=n,this.e=s,this.f=r),this},Matrix2D.prototype.toString=function(){return"Matrix2D(["+this.a+", "+this.c+", "+this.e+"] ["+this.b+", "+this.d+", "+this.f+"] [0, 0, 1])"},Matrix2D.prototype.translate=function(t,e){return"number"==typeof t?(this.e+=this.a*t+this.c*e,this.f+=this.b*t+this.d*e):(this.e+=this.a*t.x+this.c*t.y,this.f+=this.b*t.x+this.d*t.y),this},Matrix2D}()},{"./core.js":9}],19:[function(t){var e=t("./core.js"),i=window.document;e.c("Particles",{init:function(){this._Particles=e.clone(this._Particles),this._Particles.parentEntity=this},particles:function(t){if(!e.support.canvas||e.deactivateParticles)return this;var n,s,r,o,a;n=i.createElement("canvas"),n.width=e.viewport.width,n.height=e.viewport.height,n.style.position="absolute",n.style.left="0px",n.style.top="0px",e.stage.elem.appendChild(n),s=n.getContext("2d"),this._Particles.init(t),this.bind("Remove",function(){e.stage.elem.removeChild(n)}).bind("RemoveComponent",function(t){"particles"===t&&e.stage.elem.removeChild(n)}),r=this.x+e.viewport.x,o=this.y+e.viewport.y,this._Particles.position=this._Particles.vectorHelpers.create(r,o);var h={x:e.viewport.x,y:e.viewport.y};return this.bind("EnterFrame",function(){r=this.x+e.viewport.x,o=this.y+e.viewport.y,this._Particles.viewportDelta={x:e.viewport.x-h.x,y:e.viewport.y-h.y},h={x:e.viewport.x,y:e.viewport.y},this._Particles.position=this._Particles.vectorHelpers.create(r,o),"function"==typeof e.DrawManager.boundingRect?(a=e.DrawManager.boundingRect(this._Particles.register),a&&s.clearRect(a._x,a._y,a._w,a._h)):s.clearRect(0,0,e.viewport.width,e.viewport.height),this._Particles.update(),this._Particles.render(s)}),this},_Particles:{presets:{maxParticles:150,size:18,sizeRandom:4,speed:1,speedRandom:1.2,lifeSpan:29,lifeSpanRandom:7,angle:65,angleRandom:34,startColour:[255,131,0,1],startColourRandom:[48,50,45,0],endColour:[245,35,0,0],endColourRandom:[60,60,60,0],sharpness:20,sharpnessRandom:10,spread:10,duration:-1,fastMode:!1,gravity:{x:0,y:.1},jitter:0,particles:[],active:!0,particleCount:0,elapsedFrames:0,emissionRate:0,emitCounter:0,particleIndex:0},init:function(t){this.position=this.vectorHelpers.create(0,0),t===void 0&&(t={});for(var e in this.presets)this[e]=t[e]!==void 0?t[e]:this.presets[e];this.emissionRate=this.maxParticles/this.lifeSpan,this.positionRandom=this.vectorHelpers.create(this.spread,this.spread)},addParticle:function(){if(this.particleCount==this.maxParticles)return!1;var t=new this.particle(this.vectorHelpers);return this.initParticle(t),this.particles[this.particleCount]=t,this.particleCount++,!0},RANDM1TO1:function(){return 2*Math.random()-1},initParticle:function(t){t.position.x=this.position.x+this.positionRandom.x*this.RANDM1TO1(),t.position.y=this.position.y+this.positionRandom.y*this.RANDM1TO1();var e=(this.angle+this.angleRandom*this.RANDM1TO1())*(Math.PI/180),i=this.vectorHelpers.create(Math.sin(e),-Math.cos(e)),n=this.speed+this.speedRandom*this.RANDM1TO1();t.direction=this.vectorHelpers.multiply(i,n),t.size=this.size+this.sizeRandom*this.RANDM1TO1(),t.size=0>t.size?0:~~t.size,t.timeToLive=this.lifeSpan+this.lifeSpanRandom*this.RANDM1TO1(),t.sharpness=this.sharpness+this.sharpnessRandom*this.RANDM1TO1(),t.sharpness=t.sharpness>100?100:0>t.sharpness?0:t.sharpness,t.sizeSmall=~~(t.size/200*t.sharpness);var s=[this.startColour[0]+this.startColourRandom[0]*this.RANDM1TO1(),this.startColour[1]+this.startColourRandom[1]*this.RANDM1TO1(),this.startColour[2]+this.startColourRandom[2]*this.RANDM1TO1(),this.startColour[3]+this.startColourRandom[3]*this.RANDM1TO1()],r=[this.endColour[0]+this.endColourRandom[0]*this.RANDM1TO1(),this.endColour[1]+this.endColourRandom[1]*this.RANDM1TO1(),this.endColour[2]+this.endColourRandom[2]*this.RANDM1TO1(),this.endColour[3]+this.endColourRandom[3]*this.RANDM1TO1()];t.colour=s,t.deltaColour[0]=(r[0]-s[0])/t.timeToLive,t.deltaColour[1]=(r[1]-s[1])/t.timeToLive,t.deltaColour[2]=(r[2]-s[2])/t.timeToLive,t.deltaColour[3]=(r[3]-s[3])/t.timeToLive},update:function(){if(this.active&&this.emissionRate>0){var t=1/this.emissionRate;for(this.emitCounter++;this.particleCountt;)this.addParticle(),this.emitCounter-=t;this.elapsedFrames++,-1!=this.duration&&this.duration0){i.direction=this.vectorHelpers.add(i.direction,this.gravity),i.position=this.vectorHelpers.add(i.position,i.direction),i.position=this.vectorHelpers.add(i.position,this.viewportDelta),this.jitter&&(i.position.x+=this.jitter*this.RANDM1TO1(),i.position.y+=this.jitter*this.RANDM1TO1()),i.timeToLive--;var n=i.colour[0]+=i.deltaColour[0],s=i.colour[1]+=i.deltaColour[1],r=i.colour[2]+=i.deltaColour[2],o=i.colour[3]+=i.deltaColour[3];e=[],e.push("rgba("+(n>255?255:0>n?0:~~n)),e.push(s>255?255:0>s?0:~~s),e.push(r>255?255:0>r?0:~~r),e.push((o>1?1:0>o?0:o.toFixed(2))+")"),i.drawColour=e.join(","),this.fastMode||(e[3]="0)",i.drawColourEnd=e.join(",")),this.particleIndex++}else this.particleIndex!=this.particleCount-1&&(this.particles[this.particleIndex]=this.particles[this.particleCount-1]),this.particleCount--;var a={};a._x=~~i.position.x,a._y=~~i.position.y,a._w=i.size,a._h=i.size,this.register.push(a)}},stop:function(){this.active=!1,this.elapsedFrames=0,this.emitCounter=0,this.parentEntity.trigger("ParticleEnd")},render:function(t){for(var i=0,n=this.particleCount;n>i;i++){var s=this.particles[i],r=s.size,o=r>>1;if(!(0>s.position.x+r||0>s.position.y+r||s.position.x-r>e.viewport.width||s.position.y-r>e.viewport.height)){var a=~~s.position.x,h=~~s.position.y;if(this.fastMode)t.fillStyle=s.drawColour;else{var c=t.createRadialGradient(a+o,h+o,s.sizeSmall,a+o,h+o,o);c.addColorStop(0,s.drawColour),c.addColorStop(.9,s.drawColourEnd),t.fillStyle=c}t.fillRect(a,h,r,r)}}},particle:function(t){this.position=t.create(0,0),this.direction=t.create(0,0),this.size=0,this.sizeSmall=0,this.timeToLive=0,this.colour=[],this.drawColour="",this.deltaColour=[],this.sharpness=0},vectorHelpers:{create:function(t,e){return{x:t,y:e}},multiply:function(t,e){return t.x*=e,t.y*=e,t},add:function(t,e){return t.x+=e.x,t.y+=e.y,t}}}})},{"./core.js":9}],20:[function(t){var e=t("./core.js"),i=window.document;e.extend({audio:{sounds:{},supported:null,codecs:{ogg:'audio/ogg; codecs="vorbis"',wav:'audio/wav; codecs="1"',webma:'audio/webm; codecs="vorbis"',mp3:'audio/mpeg; codecs="mp3"',m4a:'audio/mp4; codecs="mp4a.40.2"'},volume:1,muted:!1,paused:!1,playCheck:null,_canPlay:function(){if(this.supported={},e.support.audio){var t,i=this.audioElement();for(var n in this.codecs)t=i.canPlayType(this.codecs[n]),this.supported[n]=""!==t&&"no"!==t?!0:!1}},supports:function(t){return null===this.supported&&this._canPlay(),this.supported[t]?!0:!1},audioElement:function(){return"undefined"!=typeof Audio?new Audio(""):i.createElement("audio")},create:function(t,i){var n=i.substr(i.lastIndexOf(".")+1).toLowerCase();if(!this.supports(n))return!1;var s=this.audioElement();return s.id=t,s.preload="auto",s.volume=e.audio.volume,s.src=i,e.asset(i,s),this.sounds[t]={obj:s,played:0,volume:e.audio.volume},this.sounds[t]},add:function(t,i){if(e.support.audio){var n;if(1===arguments.length&&"object"==typeof t)for(var s in t)for(n in t[s])if(e.audio.create(s,t[s][n]))break;if("string"==typeof t&&("string"==typeof i&&e.audio.create(t,i),"object"==typeof i))for(n in i)if(e.audio.create(t,i[n]))break}},play:function(t,i,n){if(0!==i&&e.support.audio&&this.sounds[t]){var s=this.sounds[t],r=this.getOpenChannel();if(!r)return null;r.id=t,r.repeat=i;var o=r.obj;return r.volume=s.volume=s.obj.volume=n||e.audio.volume,o.volume=s.volume,o.src=s.obj.src,this.muted&&(o.volume=0),o.play(),s.played++,r.onEnd=function(){s.playedt&&(this.channels.length=t)},channels:[],getOpenChannel:function(){for(var t=0;this.channels.length>t;t++){var e=this.channels[t];if(e.active===!1||e.obj.ended&&e.repeat<=this.sounds[e.id].played)return e.active=!0,e}if(this.maxChannels>t){var i={obj:this.audioElement(),active:!0,_is:function(t){return this.id===t&&this.active}};return this.channels.push(i),i}return null},remove:function(t){if(e.support.audio){var i;if(t)this.sounds[t]&&(i=this.sounds[t],e.audio.stop(t),delete e.assets[i.obj.src],delete e.audio.sounds[t]);else for(var n in this.sounds)i=this.sounds[n],e.audio.stop(t),delete e.assets[i.obj.src],delete e.audio.sounds[t]}},stop:function(t){if(e.support.audio)for(var i in this.channels)c=this.channels[i],(!t&&c.active||c._is(t))&&(c.active=!1,c.obj.pause())},_mute:function(t){if(e.support.audio){var i;for(var n in this.channels)i=this.channels[n],i.obj.volume=t?0:i.volume;this.muted=t}},toggleMute:function(){this.muted?this._mute(!1):this._mute(!0)},mute:function(){this._mute(!0)},unmute:function(){this._mute(!1)},pause:function(t){if(e.support.audio&&t&&this.sounds[t]){var i;for(var n in this.channels)i=this.channels[n],i._is(t)&&!i.obj.paused&&i.obj.pause()}},unpause:function(t){if(e.support.audio&&t&&this.sounds[t]){var i;for(var n in this.channels)i=this.channels[n],i._is(t)&&i.obj.paused&&i.obj.play()}},togglePause:function(t){if(e.support.audio&&t&&this.sounds[t]){var i;for(var n in this.channels)i=this.channels[n],i._is(t)&&(i.obj.paused?i.obj.play():i.obj.pause())}}}})},{"./core.js":9}],21:[function(t){var e=t("./core.js");window.document,e.c("Sprite",{__image:"",__tile:0,__tileh:0,__padding:null,__trim:null,img:null,ready:!1,init:function(){this.__trim=[0,0,0,0];var t=function(t){var e=t.co,i=t.pos,n=t.ctx;if("canvas"===t.type)n.drawImage(this.img,e.x,e.y,e.w,e.h,i._x,i._y,i._w,i._h);else if("DOM"===t.type){var s=this._h/e.h,r=this._w/e.w,o=this._element.style;o.background=o.backgroundColor+" url('"+this.__image+"') no-repeat -"+e.x*r+"px -"+e.y*s+"px",(1!=s||1!=r)&&(o.backgroundSize=this.img.width*r+"px"+" "+this.img.height*s+"px")}};this.bind("Draw",t).bind("RemoveComponent",function(e){"Sprite"===e&&this.unbind("Draw",t)})},sprite:function(t,e,i,n){return this.__coord=[t*(this.__tile+this.__padding[0])+this.__trim[0],e*(this.__tileh+this.__padding[1])+this.__trim[1],this.__trim[2]||i*this.__tile||this.__tile,this.__trim[3]||n*this.__tileh||this.__tileh],this.trigger("Change"),this},crop:function(t,e,i,n){var s=this._mbr||this.pos();return this.__trim=[],this.__trim[0]=t,this.__trim[1]=e,this.__trim[2]=i,this.__trim[3]=n,this.__coord[0]+=t,this.__coord[1]+=e,this.__coord[2]=i,this.__coord[3]=n,this._w=i,this._h=n,this.trigger("Change",s),this}})},{"./core.js":9}],22:[function(require,module,exports){var Crafty=require("./core.js"),document=window.document;Crafty.storage=function(){function process(t){if(t.c){var e=Crafty.e(t.c).attr(t.attr).trigger("LoadData",t,process);return e}if("object"==typeof t)for(var i in t)t[i]=process(t[i]);return t}function unserialize(str){if("string"!=typeof str)return null;var data=JSON?JSON.parse(str):eval("("+str+")");return process(data)}function prep(t){if(t.__c){var e={c:[],attr:{}};t.trigger("SaveData",e,prep);for(var i in t.__c)e.c.push(i);e.c=e.c.join(", "),t=e}else if("object"==typeof t)for(var n in t)t[n]=prep(t[n]);return t}function serialize(t){if(JSON){var e=prep(t);return JSON.stringify(e)}return alert("Crafty does not support saving on your browser. Please upgrade to a newer browser."),!1}function external(t){url=t}function openExternal(){if(void 0!==url){var xml=new XMLHttpRequest;xhr.open("POST",url),xhr.onreadystatechange=function(evt){if(4==xhr.readyState&&200==xhr.status){var data=eval("("+xhr.responseText+")");for(var i in data)Crafty.storage.check(data[i].key,data[i].timestamp)&&loadExternal(data[i].key)}},xhr.send("mode=timestamps&game="+gameName)}}function saveExternal(t,e,i){if(void 0!==url){var n=new XMLHttpRequest;n.open("POST",url),n.send("mode=save&key="+t+"&data="+encodeURIComponent(e)+"&ts="+i+"&game="+gameName)}}function loadExternal(key){if(void 0!==url){var xhr=new XMLHttpRequest;xhr.open("POST",url),xhr.onreadystatechange=function(evt){if(4==xhr.readyState&&200==xhr.status){var data=eval("("+xhr.responseText+")");Crafty.storage.save(key,"save",data)}},xhr.send("mode=load&key="+key+"&game="+gameName)}}function ts(){var t=new Date;return t.getTime()}var db=null,url,gameName,timestamps={},transactionType={READ:"readonly",READ_WRITE:"readwrite"};return"object"!=typeof indexedDB&&(window.indexedDB=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB,window.IDBTransaction=window.IDBTransaction||window.webkitIDBTransaction,window.IDBKeyRange=window.IDBKeyRange||window.webkitIDBKeyRange||window.msIDBKeyRange,"object"==typeof IDBTransaction&&(transactionType.READ=IDBTransaction.READ||IDBTransaction.readonly||transactionType.READ||"read",transactionType.READ_WRITE=IDBTransaction.READ_WRITE||IDBTransaction.readwrite||transactionType.READ_WRITE||"readwrite")),"object"==typeof indexedDB?{open:function(t){function e(){try{var t=db.transaction(["save"],"read"),e=t.objectStore("save"),i=e.getAll();i.onsuccess=function(){for(var t=0,e=event.target.result,i=e.length;i>t;t++)timestamps[e[t].key]=e[t].timestamp}}catch(n){}}function i(){var t=db.setVersion("1.0");t.onsuccess=function(){for(var t=0;n.length>t;t++){var e=n[t];db.objectStoreNames.contains(e)||db.createObjectStore(e,{keyPath:"key"})}}}gameName=t;var n=[];if(1==arguments.length?(n.push("save"),n.push("cache")):(n=arguments,n.shift(),n.push("save"),n.push("cache")),null===db){var s=indexedDB.open(gameName);s.onsuccess=function(t){db=t.target.result,e(),openExternal()},s.onupgradeneeded=function(){i()}}else i(),e(),openExternal()},save:function(t,e,i,n){if(null===db)return setTimeout(function(){Crafty.storage.save(t,e,i)},1),void 0;var s=serialize(i),r=ts();"save"==e&&saveExternal(t,s,r);try{var o=db.transaction([e],transactionType.READ_WRITE).objectStore(e).add({data:s,timestamp:r,key:t});"function"==typeof n&&(o.onsuccess=n)}catch(a){console.error(a)}},load:function(t,e,i){if(null===db)return setTimeout(function(){Crafty.storage.load(t,e,i)},1),void 0;try{var n=db.transaction([e],transactionType.READ).objectStore(e).get(t);n.onsuccess=function(t){i(unserialize(t.target.result.data))}}catch(s){console.error(s)}},getAllKeys:function(t,e){null===db&&setTimeout(function(){Crafty.storage.getAllkeys(t,e)},1);try{var i=db.transaction([t],transactionType.READ).objectStore(t).openCursor(),n=[];i.onsuccess=function(t){var i=t.target.result;i?(n.push(i.key),i["continue"]()):e(n)}}catch(s){console.error(s)}},check:function(t,e){return timestamps[t]>e},external:external}:"function"==typeof openDatabase?{open:function(t){if(gameName=t,1==arguments.length)db={save:openDatabase(t+"_save","1.0","Saves games for "+t,5242880),cache:openDatabase(t+"_cache","1.0","Cache for "+t,5242880)};else{var e=arguments,i=0;for(e.shift();e.length>i;i++)db[e[i]]===void 0&&(db[e[i]]=openDatabase(gameName+"_"+e[i],"1.0",type,5242880))}db.save.transaction(function(t){t.executeSql("SELECT key, timestamp FROM data",[],function(t,e){for(var i=0,n=e.rows,s=n.length;s>i;i++)timestamps[n.item(i).key]=n.item(i).timestamp})})},save:function(t,e,i){db[e]===void 0&&""!==gameName&&this.open(gameName,e);var n=serialize(i),s=ts();"save"==e&&saveExternal(t,n,s),db[e].transaction(function(e){e.executeSql("CREATE TABLE IF NOT EXISTS data (key unique, text, timestamp)"),e.executeSql("SELECT * FROM data WHERE key = ?",[t],function(e,i){i.rows.length?e.executeSql("UPDATE data SET text = ?, timestamp = ? WHERE key = ?",[n,s,t]):e.executeSql("INSERT INTO data VALUES (?, ?, ?)",[t,n,s])})})},load:function(t,e,i){return db[e]===void 0?(setTimeout(function(){Crafty.storage.load(t,e,i)},1),void 0):(db[e].transaction(function(e){e.executeSql("SELECT text FROM data WHERE key = ?",[t],function(t,e){e.rows.length&&(res=unserialize(e.rows.item(0).text),i(res))})}),void 0)},getAllKeys:function(t,e){return db[t]===void 0?(setTimeout(function(){Crafty.storage.getAllKeys(t,e)},1),void 0):(db[t].transaction(function(t){t.executeSql("SELECT key FROM data",[],function(t,i){e(i.rows)})}),void 0)},check:function(t,e){return timestamps[t]>e},external:external}:"object"==typeof window.localStorage?{open:function(t){gameName=t},save:function(t,e,i){var n=gameName+"."+e+"."+t,s=serialize(i),r=ts();"save"==e&&saveExternal(t,s,r),window.localStorage[n]=s,"save"==e&&(window.localStorage[n+".ts"]=r)},load:function(t,e,i){var n=gameName+"."+e+"."+t,s=window.localStorage[n];i(unserialize(s))},getAllKeys:function(t,e){var i={},n=[],s=gameName+"."+t;for(var r in window.localStorage)if(-1!=r.indexOf(s)){var o=r.replace(s,"").replace(".ts","");i[o]=!0}for(r in i)n.push(r);e(n)},check:function(t,e){var i=window.localStorage[gameName+".save."+t+".ts"];return parseInt(e,10)>parseInt(i,10)},external:external}:{open:function(t){gameName=t},save:function(t,e,i){if("save"==e){var n=serialize(i),s=ts();"save"==e&&saveExternal(t,n,s),document.cookie=gameName+"_"+t+"="+n+"; "+gameName+"_"+t+"_ts="+s+"; expires=Thur, 31 Dec 2099 23:59:59 UTC; path=/"}},load:function(t,e,i){if("save"==e){var n=RegExp(gameName+"_"+t+"=[^;]*"),s=n.exec(document.cookie),r=unserialize(s[0].replace(gameName+"_"+t+"=",""));i(r)}},getAllKeys:function(t,e){if("save"==t){for(var i=RegExp(gameName+"_[^_=]","g"),n=i.exec(document.cookie),s=0,r=n.length,o={},a=[];r>s;s++){var h=n[s].replace(gameName+"_","");o[h]=!0}for(s in o)a.push(s);e(a)}},check:function(t,e){var i=gameName+"_"+t+"_ts",n=RegExp(i+"=[^;]"),s=n.exec(document.cookie),r=s[0].replace(i+"=","");return parseInt(e,10)>parseInt(r,10)},external:external}}()},{"./core.js":9}],23:[function(t){var e=t("./core.js");window.document,e.c("Text",{_text:"",defaultSize:"10px",defaultFamily:"sans-serif",ready:!0,init:function(){this.requires("2D"),this._textFont={type:"",weight:"",size:this.defaultSize,family:this.defaultFamily},this.bind("Draw",function(t){var e=this._fontString();if("DOM"===t.type){var i=this._element,n=i.style;n.color=this._textColor,n.font=e,i.innerHTML=this._text}else if("canvas"===t.type){var s=t.ctx;s.save(),s.textBaseline="top",s.fillStyle=this._textColor||"rgb(0,0,0)",s.font=e,s.fillText(this._text,this._x,this._y),s.restore()}})},_getFontHeight:function(){var t=/([a-zA-Z]+)\b/,e={px:1,pt:4/3,pc:16,cm:96/2.54,mm:96/25.4,"in":96,em:void 0,ex:void 0};return function(i){var n=parseFloat(i),s=t.exec(i),r=s?s[1]:"px";return void 0!==e[r]?Math.ceil(n*e[r]):Math.ceil(n)}}(),text:function(t){return void 0===t||null===t?this._text:(this._text="function"==typeof t?t.call(this):t,this.has("Canvas")&&this._resizeForCanvas(),this.trigger("Change"),this)},_resizeForCanvas:function(){var t=e.canvas.context;t.font=this._fontString(),this.w=t.measureText(this._text).width;var i=this._textFont.size||this.defaultSize;this.h=1.1*this._getFontHeight(i)},_fontString:function(){return this._textFont.type+" "+this._textFont.weight+" "+this._textFont.size+" "+this._textFont.family},textColor:function(t,i){return this._strength=i,this._textColor=e.toRGB(t,this._strength),this.trigger("Change"),this},textFont:function(t,e){if(1===arguments.length){if("string"==typeof t)return this._textFont[t];if("object"==typeof t)for(var i in t)this._textFont[i]="family"==i?"'"+t[i]+"'":t[i]}else this._textFont[t]=e;return this.has("Canvas")&&this._resizeForCanvas(),this.trigger("Change"),this},unselectable:function(){return this.has("DOM")&&(this.css({"-webkit-touch-callout":"none","-webkit-user-select":"none","-khtml-user-select":"none","-moz-user-select":"none","-ms-user-select":"none","user-select":"none"}),this.trigger("Change")),this}})},{"./core.js":9}],24:[function(t){var e=t("./core.js");window.document,e.c("Delay",{init:function(){this._delays=[],this.bind("EnterFrame",function(){for(var t=(new Date).getTime(),e=this._delays.length;--e>=0;){var i=this._delays[e];t>i.start+i.delay+i.pause&&(i.func.call(this),i.repeat>0?(i.start=t,i.pause=0,i.pauseBuffer=0,i.repeat--):0>=i.repeat&&this._delays.splice(e,1))}}),this.bind("Pause",function(){var t=(new Date).getTime();for(var e in this._delays)this._delays[e].pauseBuffer=t}),this.bind("Unpause",function(){var t=(new Date).getTime();for(var e in this._delays){var i=this._delays[e];i.pause+=t-i.pauseBuffer}})},delay:function(t,e,i){return this._delays.push({start:(new Date).getTime(),func:t,delay:e,repeat:(0>i?1/0:i)||0,pauseBuffer:0,pause:0}),this}})},{"./core.js":9}],25:[function(t,e){e.exports="0.6.0"},{}],26:[function(t){var e=t("./core.js"),i=window.document;e.extend({viewport:{clampToEntities:!0,width:0,height:0,_x:0,_y:0,_scale:1,bounds:null,scroll:function(t,i){i=Math.floor(i),this[t]=i,e.trigger("ViewportScroll"),e.trigger("InvalidateViewport")},rect:function(){return{_x:-this._x/this._scale,_y:-this._y/this._scale,_w:this.width/this._scale,_h:this.height/this._scale}},pan:function(){function t(){var t=0;for(var n in i){var s=i[n];s.remTime>0?(s.current+=s.diff,s.remTime--,e.viewport[n]=Math.floor(s.current),t++):delete i[n]}t&&e.viewport._clamp()}var i={},n=!1;return function(s,r,o){if(e.viewport.follow(),"reset"!=s)0===o&&(o=1),i[s]={diff:-r/o,current:e.viewport[s],remTime:o},n||(e.bind("EnterFrame",t),n=!0);else for(var a in i)i[a].remTime=0}}(),follow:function(){function t(){e.viewport.scroll("_x",-(this.x+this.w/2-e.viewport.width/2-n)),e.viewport.scroll("_y",-(this.y+this.h/2-e.viewport.height/2-s)),e.viewport._clamp()}var i,n,s;return function(r,o,a){i&&i.unbind("Change",t),r&&r.has("2D")&&(e.viewport.pan("reset"),i=r,n=o!==void 0?o:0,s=a!==void 0?a:0,r.bind("Change",t),t.call(r))}}(),centerOn:function(t,i){var n=t.x+e.viewport.x,s=t.y+e.viewport.y,r=t.w/2,o=t.h/2,a=e.viewport.width/2,h=e.viewport.height/2,c=n+r-a,u=s+o-h;e.viewport.pan("reset"),e.viewport.pan("x",c,i),e.viewport.pan("y",u,i)},_zoom:1,zoom:function(){function t(){if(s>0){isFinite(e.viewport._zoom)&&(i=e.viewport._zoom);var t={width:a.width*i,height:a.height*i};i+=n,e.viewport._zoom=i;var o={width:a.width*i,height:a.height*i},c={width:o.width-t.width,height:o.height-t.height};if(e.stage.inner.style[r]="scale("+i+","+i+")",e.canvas._canvas){var u=i/(i-n);e.canvas.context.scale(u,u),e.trigger("InvalidateViewport")}e.viewport.x-=c.width*h.width,e.viewport.y-=c.height*h.height,s--}}var i=1,n=0,s=0,r=e.support.prefix+"Transform",o=!1,a={},h={};return function(r,c,u,l){var d=this.bounds||e.map.boundaries(),_=r?i*r:1;r||(i=1,this._zoom=1),a.width=d.max.x-d.min.x,a.height=d.max.y-d.min.y,h.width=c/a.width,h.height=u/a.height,0===l&&(l=1),n=(_-i)/l,s=l,e.viewport.pan("reset"),o||(e.bind("EnterFrame",t),o=!0)}}(),scale:function(){return function(t){var i=(this.bounds||e.map.boundaries(),t?t:1);this._zoom=i,this._scale=i,e.trigger("InvalidateViewport"),e.trigger("ViewportScale")}}(),mouselook:function(){var t=!1,i=!1,n={};return old={},function(s,r){if("boolean"==typeof s)return t=s,t?e.mouseObjs++:e.mouseObjs=Math.max(0,e.mouseObjs-1),void 0;if(t)switch(s){case"move":case"drag":if(!i)return;diff={x:r.clientX-n.x,y:r.clientY-n.y},n.x=r.clientX,n.y=r.clientY,e.viewport.x+=diff.x,e.viewport.y+=diff.y,e.viewport._clamp();break;case"start":n.x=r.clientX,n.y=r.clientY,i=!0;break;case"stop":i=!1}}}(),_clamp:function(){if(this.clampToEntities){var t=this.bounds||e.map.boundaries();t.max.x*=this._zoom,t.min.x*=this._zoom,t.max.y*=this._zoom,t.min.y*=this._zoom,t.max.x-t.min.x>e.viewport.width?(t.max.x-=e.viewport.width,e.viewport.x<-t.max.x?e.viewport.x=-t.max.x:e.viewport.x>-t.min.x&&(e.viewport.x=-t.min.x)):e.viewport.x=-1*(t.min.x+(t.max.x-t.min.x)/2-e.viewport.width/2),t.max.y-t.min.y>e.viewport.height?(t.max.y-=e.viewport.height,e.viewport.y<-t.max.y?e.viewport.y=-t.max.y:e.viewport.y>-t.min.y&&(e.viewport.y=-t.min.y)):e.viewport.y=-1*(t.min.y+(t.max.y-t.min.y)/2-e.viewport.height/2) -}},init:function(t,n,s){e.DOM.window.init(),this.width=!t||e.mobile?e.DOM.window.width:t,this.height=!n||e.mobile?e.DOM.window.height:n,s===void 0&&(s="cr-stage");var r;if("string"==typeof s)r=i.getElementById(s);else{if(!("undefined"!=typeof HTMLElement?s instanceof HTMLElement:s instanceof Element))throw new TypeError("stage_elem must be a string or an HTMLElement");r=s}e.stage={x:0,y:0,fullscreen:!1,elem:r?r:i.createElement("div"),inner:i.createElement("div")},(!t&&!n||e.mobile)&&(i.body.style.overflow="hidden",e.stage.fullscreen=!0),e.addEvent(this,window,"resize",e.viewport.reload),e.addEvent(this,window,"blur",function(){e.settings.get("autoPause")&&(e._paused||e.pause())}),e.addEvent(this,window,"focus",function(){e._paused&&e.settings.get("autoPause")&&e.pause()}),e.settings.register("stageSelectable",function(t){e.stage.elem.onselectstart=t?function(){return!0}:function(){return!1}}),e.settings.modify("stageSelectable",!1),e.settings.register("stageContextMenu",function(t){e.stage.elem.oncontextmenu=t?function(){return!0}:function(){return!1}}),e.settings.modify("stageContextMenu",!1),e.settings.register("autoPause",function(){}),e.settings.modify("autoPause",!1),r||(i.body.appendChild(e.stage.elem),e.stage.elem.id=s);var o,a=e.stage.elem.style;if(e.stage.elem.appendChild(e.stage.inner),e.stage.inner.style.position="absolute",e.stage.inner.style.zIndex="1",e.stage.inner.style.transformStyle="preserve-3d",a.width=this.width+"px",a.height=this.height+"px",a.overflow="hidden",e.mobile){a.position="absolute",a.left="0px",a.top="0px",void 0!==typeof a.webkitTapHighlightColor&&(a.webkitTapHighlightColor="rgba(0,0,0,0)");var h=i.createElement("meta"),c=i.getElementsByTagName("HEAD")[0];h.setAttribute("name","viewport"),h.setAttribute("content","width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"),c.appendChild(h),h=i.createElement("meta"),h.setAttribute("name","apple-mobile-web-app-capable"),h.setAttribute("content","yes"),c.appendChild(h),setTimeout(function(){window.scrollTo(0,1)},0),e.addEvent(this,window,"touchmove",function(t){t.preventDefault()}),e.stage.x=0,e.stage.y=0}else a.position="relative",o=e.DOM.inner(e.stage.elem),e.stage.x=o.x,e.stage.y=o.y;e.support.setter?(this.__defineSetter__("x",function(t){this.scroll("_x",t)}),this.__defineSetter__("y",function(t){this.scroll("_y",t)}),this.__defineGetter__("x",function(){return this._x}),this.__defineGetter__("y",function(){return this._y})):e.support.defineProperty?(Object.defineProperty(this,"x",{set:function(t){this.scroll("_x",t)},get:function(){return this._x},configurable:!0}),Object.defineProperty(this,"y",{set:function(t){this.scroll("_y",t)},get:function(){return this._y},configurable:!0})):(this.x=this._x,this.y=this._y,e.bind("EnterFrame",function(){e.viewport._x!==e.viewport.x&&e.viewport.scroll("_x",e.viewport.x),e.viewport._y!==e.viewport.y&&e.viewport.scroll("_y",e.viewport.y)}))},reload:function(){e.DOM.window.init();var t,i=e.DOM.window.width,n=e.DOM.window.height;e.stage.fullscreen&&(this.width=i,this.height=n,e.stage.elem.style.width=i+"px",e.stage.elem.style.height=n+"px",e.canvas._canvas&&(e.canvas._canvas.width=i,e.canvas._canvas.height=n,e.trigger("InvalidateViewport"))),t=e.DOM.inner(e.stage.elem),e.stage.x=t.x,e.stage.y=t.y},reset:function(){e.viewport.pan("reset"),e.viewport.follow(),e.viewport.mouselook("stop"),e.viewport.scale()}}})},{"./core.js":9}]},{},[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]); \ No newline at end of file +!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g0?new Array(b+(/\./.test(a)?2:1)).join("0")+a:a.toString()}}),b.c("2D",{_x:0,_y:0,_w:0,_h:0,_z:0,_rotation:0,_alpha:1,_visible:!0,_globalZ:null,_origin:null,_mbr:null,_entry:null,_children:null,_parent:null,_changed:!1,_define2DProperties:function(){Object.defineProperty(this,"x",{set:function(a){this._attr("_x",a)},get:function(){return this._x},configurable:!0,enumerable:!0}),Object.defineProperty(this,"_x",{enumerable:!1}),Object.defineProperty(this,"y",{set:function(a){this._attr("_y",a)},get:function(){return this._y},configurable:!0,enumerable:!0}),Object.defineProperty(this,"_y",{enumerable:!1}),Object.defineProperty(this,"w",{set:function(a){this._attr("_w",a)},get:function(){return this._w},configurable:!0,enumerable:!0}),Object.defineProperty(this,"_w",{enumerable:!1}),Object.defineProperty(this,"h",{set:function(a){this._attr("_h",a)},get:function(){return this._h},configurable:!0,enumerable:!0}),Object.defineProperty(this,"_h",{enumerable:!1}),Object.defineProperty(this,"z",{set:function(a){this._attr("_z",a)},get:function(){return this._z},configurable:!0,enumerable:!0}),Object.defineProperty(this,"_z",{enumerable:!1}),Object.defineProperty(this,"rotation",{set:function(a){this._attr("_rotation",a)},get:function(){return this._rotation},configurable:!0,enumerable:!0}),Object.defineProperty(this,"_rotation",{enumerable:!1}),Object.defineProperty(this,"alpha",{set:function(a){this._attr("_alpha",a)},get:function(){return this._alpha},configurable:!0,enumerable:!0}),Object.defineProperty(this,"_alpha",{enumerable:!1}),Object.defineProperty(this,"visible",{set:function(a){this._attr("_visible",a)},get:function(){return this._visible},configurable:!0,enumerable:!0}),Object.defineProperty(this,"_visible",{enumerable:!1})},init:function(){this._globalZ=this[0],this._origin={x:0,y:0},this._bx1=0,this._bx2=0,this._by1=0,this._by2=0,this._children=[],this._define2DProperties(),this._entry=b.map.insert(this),this.bind("Move",function(a){var b=this._cbr||this._mbr||this;this._entry.update(b),this._children.length>0&&this._cascade(a)}),this.bind("Rotate",function(a){var b=this._cbr||this._mbr||this;this._entry.update(b),this._children.length>0&&this._cascade(a)}),this.bind("Remove",function(){if(this._children){for(var a=0;ai&&i>-1e-10?0:i,j=1e-10>j&&j>-1e-10?0:j;var k=d*i+g*j,l=-d*j+g*i,m=e*i+g*j,n=-e*j+g*i,o=e*i+h*j,p=-e*j+h*i,q=d*i+h*j,r=-d*j+h*i,s=Math.floor(Math.min(k,m,o,q)+a),t=Math.floor(Math.min(l,n,p,r)+b),u=Math.ceil(Math.max(k,m,o,q)+a),v=Math.ceil(Math.max(l,n,p,r)+b);if(this._mbr?(this._mbr._x=s,this._mbr._y=t,this._mbr._w=u-s,this._mbr._h=v-t):this._mbr={_x:s,_y:t,_w:u-s,_h:v-t},this._cbr){var w=this._cbr,x=w.cx,y=w.cy,z=w.r,A=a+(x+this._x-a)*i+(y+this._y-b)*j,B=b-(x+this._x-a)*j+(y+this._y-b)*i;w._x=Math.min(A-z,s),w._y=Math.min(B-z,t),w._w=Math.max(A+z,u)-w._x,w._h=Math.max(B+z,v)-w._y}},_rotate:function(a){var b=-1*(a%360),c=this._rotation-a;if(0!==c){this._rotation=a;var d=b*f,e={x:this._origin.x+this._x,y:this._origin.y+this._y};this._calculateMBR();{var g=c*f;Math.cos(d),Math.sin(d)}this.trigger("Rotate",{cos:Math.cos(g),sin:Math.sin(g),deg:c,rad:g,o:e})}},area:function(){return this._w*this._h},intersect:function(a,b,c,d){var e,f=this._mbr||this;return e="object"==typeof a?a:{x:a,y:b,w:c,h:d},f._xe.x&&f._ye.y},within:function(a,b,c,d){var e,f=this._mbr||this;return e="object"==typeof a?a:{_x:a,_y:b,_w:c,_h:d},e._x<=f._x&&e._x+e._w>=f._x+f._w&&e._y<=f._y&&e._y+e._h>=f._y+f._h},contains:function(a,b,c,d){var e,f=this._mbr||this;return e="object"==typeof a?a:{_x:a,_y:b,_w:c,_h:d},e._x>=f._x&&e._x+e._w<=f._x+f._w&&e._y>=f._y&&e._y+e._h<=f._y+f._h},pos:function(){return{_x:this._x,_y:this._y,_w:this._w,_h:this._h}},mbr:function(){return this._mbr?{_x:this._mbr._x,_y:this._mbr._y,_w:this._mbr._w,_h:this._mbr._h}:this.pos()},isAt:function(a,b){if(this.mapArea)return this.mapArea.containsPoint(a,b);if(this.map)return this.map.containsPoint(a,b);var c=this._mbr||this;return c._x<=a&&c._x+c._w>=a&&c._y<=b&&c._y+c._h>=b},move:function(a,b){return"n"===a.charAt(0)&&(this.y-=b),"s"===a.charAt(0)&&(this.y+=b),("e"===a||"e"===a.charAt(1))&&(this.x+=b),("w"===a||"w"===a.charAt(1))&&(this.x-=b),this},shift:function(a,b,c,d){return a&&(this.x+=a),b&&(this.y+=b),c&&(this.w+=c),d&&(this.h+=d),this},_cascade:function(a){if(a){var b,c=0,d=this._children,e=d.length;if("cos"in a||"sin"in a)for(;e>c;++c)b=d[c],"rotate"in b&&b.rotate(a);else for(var f=this._x-a._x,g=this._y-a._y,h=this._w-a._w,i=this._h-a._h;e>c;++c)b=d[c],b.shift(f,g,h,i)}},attach:function(){for(var a,b=0,c=arguments,d=arguments.length;d>b;++b)a=c[b],a._parent&&a._parent.detach(a),a._parent=this,this._children.push(a);return this},detach:function(a){var b;if(!a){for(b=0;bg;++g)if(a=c[g],a!==this&&a.has(this._anti)&&a.intersect(f)){e=a;break}e?this._falling&&(this._gy>this._jumpSpeed||!this._up)&&this.stopFalling(e):this._falling=!0},stopFalling:function(a){a&&(this.y=a._y-this._h),this._falling=!1,this._up&&(this._up=!1),this.trigger("hit")},antigravity:function(){this.unbind("EnterFrame",this._enterFrame)}}),b.polygon=function(a){arguments.length>1&&(a=Array.prototype.slice.call(arguments,0)),this.points=a},b.polygon.prototype={containsPoint:function(a,b){var c,d,e=this.points,f=!1;for(c=0,d=e.length-1;cb!=e[d][1]>b&&a<(e[d][0]-e[c][0])*(b-e[c][1])/(e[d][1]-e[c][1])+e[c][0]&&(f=!f);return f},shift:function(a,b){for(var c,d=0,e=this.points.length;e>d;d++)c=this.points[d],c[0]+=a,c[1]+=b},rotate:function(a){for(var b,c,d,e=0,f=this.points.length;f>e;e++)b=this.points[e],c=a.o.x+(b[0]-a.o.x)*a.cos+(b[1]-a.o.y)*a.sin,d=a.o.y-(b[0]-a.o.x)*a.sin+(b[1]-a.o.y)*a.cos,b[0]=c,b[1]=d}},b.circle=function(a,b,c){this.x=a,this.y=b,this.radius=c,this.points=[];for(var d,e=0;8>e;e++)d=e*Math.PI/4,this.points[e]=[this.x+Math.sin(d)*c,this.y+Math.cos(d)*c]},b.circle.prototype={containsPoint:function(a,b){var c=this.radius,d=(Math.sqrt,this.x-a),e=this.y-b;return c*c>d*d+e*e},shift:function(a,b){this.x+=a,this.y+=b;for(var c,d=0,e=this.points.length;e>d;d++)c=this.points[d],c[0]+=a,c[1]+=b},rotate:function(){}},b.matrix=function(a){this.mtx=a,this.width=a[0].length,this.height=a.length},b.matrix.prototype={x:function(a){if(this.width==a.height){for(var c=[],d=0;da||a>this.mtx.length||1>b||b>this.mtx[0].length?null:this.mtx[a-1][b-1]}}},{"./HashMap.js":4,"./core.js":10}],2:[function(a){var b=a("./core.js"),c=window.document;b.c("DOM",{_element:null,_cssStyles:null,avoidCss3dTransforms:!1,init:function(){this._cssStyles={visibility:"",left:"",top:"",width:"",height:"",zIndex:"",opacity:"",transformOrigin:"",transform:""},this._element=c.createElement("div"),b.stage.inner.appendChild(this._element),this._element.style.position="absolute",this._element.id="ent"+this[0],this.bind("Invalidate",this._invalidateDOM),this.bind("NewComponent",this._updateClass),this.bind("RemoveComponent",this._removeClass),this._invalidateDOM()},remove:function(){this.undraw(),this.unbind("NewComponent",this._updateClass),this.unbind("RemoveComponent",this._removeClass),this.unbind("Invalidate",this._invalidateDOM)},getDomId:function(){return this._element.id},_removeClass:function(a){var b=0,c=this.__c,d="";for(b in c)b!=a&&(d+=" "+b);d=d.substr(1),this._element.className=d},_updateClass:function(){var a=0,b=this.__c,c="";for(a in b)c+=" "+a;c=c.substr(1),this._element.className=c},_invalidateDOM:function(){this._changed||(this._changed=!0,b.DrawManager.addDom(this))},DOM:function(a){return a&&a.nodeType&&(this.undraw(),this._element=a,this._element.style.position="absolute"),this},draw:function(){var a=this._element.style,c=this.__coord||[0,0,0,0],d={x:c[0],y:c[1],w:c[2],h:c[3]},e=b.support.prefix,f=[];if(this._cssStyles.visibility!==this._visible&&(this._cssStyles.visibility=this._visible,a.visibility=this._visible?"visible":"hidden"),b.support.css3dtransform&&!this.avoidCss3dTransforms?f.push("translate3d("+~~this._x+"px,"+~~this._y+"px,0)"):(this._cssStyles.left!==this._x&&(this._cssStyles.left=this._x,a.left=~~this._x+"px"),this._cssStyles.top!==this._y&&(this._cssStyles.top=this._y,a.top=~~this._y+"px")),this._cssStyles.width!==this._w&&(this._cssStyles.width=this._w,a.width=~~this._w+"px"),this._cssStyles.height!==this._h&&(this._cssStyles.height=this._h,a.height=~~this._h+"px"),this._cssStyles.zIndex!==this._z&&(this._cssStyles.zIndex=this._z,a.zIndex=this._z),this._cssStyles.opacity!==this._alpha&&(this._cssStyles.opacity=this._alpha,a.opacity=this._alpha,a[e+"Opacity"]=this._alpha),this._mbr){var g=this._origin.x+"px "+this._origin.y+"px";a.transformOrigin=g,a[e+"TransformOrigin"]=g,f.push(b.support.css3dtransform?"rotateZ("+this._rotation+"deg)":"rotate("+this._rotation+"deg)")}return this._flipX&&f.push("scaleX(-1)"),this._flipY&&f.push("scaleY(-1)"),this._cssStyles.transform!=f.join(" ")&&(this._cssStyles.transform=f.join(" "),a.transform=this._cssStyles.transform,a[e+"Transform"]=this._cssStyles.transform),this.trigger("Draw",{style:a,type:"DOM",co:d}),this},undraw:function(){var a=this._element;return a&&null!==a.parentNode&&a.parentNode.removeChild(a),this},css:function(a,c){var d,e,f=this._element,g=f.style;if("object"==typeof a)for(d in a)a.hasOwnProperty(d)&&(e=a[d],"number"==typeof e&&(e+="px"),g[b.DOM.camelize(d)]=e);else{if(!c)return b.DOM.getStyle(f,a);"number"==typeof c&&(c+="px"),g[b.DOM.camelize(a)]=c}return this.trigger("Invalidate"),this}}),b.extend({DOM:{window:{init:function(){this.width=window.innerWidth||window.document.documentElement.clientWidth||window.document.body.clientWidth,this.height=window.innerHeight||window.document.documentElement.clientHeight||window.document.body.clientHeight,b.uniqueBind("RenderScene",b.DrawManager.renderDOM),b.uniqueBind("ViewportResize",this._resize),b.uniqueBind("PixelartSet",this._setPixelArt)},_resize:function(){b.stage.elem.style.width=b.viewport.width+"px",b.stage.elem.style.height=b.viewport.height+"px"},_setPixelArt:function(a){var c=b.stage.inner.style;a?(c[b.DOM.camelize("image-rendering")]="optimizeSpeed",c[b.DOM.camelize("image-rendering")]="-moz-crisp-edges",c[b.DOM.camelize("image-rendering")]="-o-crisp-edges",c[b.DOM.camelize("image-rendering")]="-webkit-optimize-contrast",c[b.DOM.camelize("-ms-interpolation-mode")]="nearest-neighbor",c[b.DOM.camelize("image-rendering")]="optimize-contrast",c[b.DOM.camelize("image-rendering")]="pixelated",c[b.DOM.camelize("image-rendering")]="crisp-edges"):(c[b.DOM.camelize("image-rendering")]="optimizeQuality",c[b.DOM.camelize("-ms-interpolation-mode")]="bicubic",c[b.DOM.camelize("image-rendering")]="auto")},width:0,height:0},inner:function(a){var b=a.getBoundingClientRect(),d=b.left+(window.pageXOffset?window.pageXOffset:c.body.scrollLeft),e=b.top+(window.pageYOffset?window.pageYOffset:c.body.scrollTop),f=parseInt(this.getStyle(a,"border-left-width")||0,10)||parseInt(this.getStyle(a,"borderLeftWidth")||0,10)||0,g=parseInt(this.getStyle(a,"border-top-width")||0,10)||parseInt(this.getStyle(a,"borderTopWidth")||0,10)||0;return d+=f,e+=g,{x:d,y:e}},getStyle:function(a,b){var d;return a.currentStyle?d=a.currentStyle[this.camelize(b)]:window.getComputedStyle&&(d=c.defaultView.getComputedStyle(a,null).getPropertyValue(this.csselize(b))),d},camelize:function(a){return a.replace(/-+(.)?/g,function(a,b){return b?b.toUpperCase():""})},csselize:function(a){return a.replace(/[A-Z]/g,function(a){return a?"-"+a.toLowerCase():""})},translate:function(a,d){var e=c.documentElement,f=c.body;return{x:(a-b.stage.x+(e&&e.scrollLeft||f&&f.scrollLeft||0))/b.viewport._scale-b.viewport._x,y:(d-b.stage.y+(e&&e.scrollTop||f&&f.scrollTop||0))/b.viewport._scale-b.viewport._y}}}})},{"./core.js":10}],3:[function(a){var b=a("./core.js"),c=window.document;b.c("DebugCanvas",{init:function(){this.requires("2D"),b.DebugCanvas.context||b.DebugCanvas.init(),b.DebugCanvas.add(this),this._debug={alpha:1,lineWidth:1},this.bind("RemoveComponent",this.onDebugRemove),this.bind("Remove",this.onDebugDestroy)},onDebugRemove:function(a){"DebugCanvas"===a&&b.DebugCanvas.remove(this)},onDebugDestroy:function(){b.DebugCanvas.remove(this)},debugAlpha:function(a){return this._debug.alpha=a,this},debugFill:function(a){return"undefined"==typeof a&&(a="red"),this._debug.fillStyle=a,this},debugStroke:function(a){return"undefined"==typeof a&&(a="red"),this._debug.strokeStyle=a,this},debugDraw:function(a){var b=a.globalAlpha,c=this._debug;c.alpha&&(a.globalAlpha=this._debug.alpha),c.strokeStyle&&(a.strokeStyle=c.strokeStyle),c.lineWidth&&(a.lineWidth=c.lineWidth),c.fillStyle&&(a.fillStyle=c.fillStyle),this.trigger("DebugDraw"),a.globalAlpha=b}}),b.c("DebugRectangle",{init:function(){this.requires("2D, DebugCanvas")},debugRectangle:function(a){return this.debugRect=a,this.unbind("DebugDraw",this.drawDebugRect),this.bind("DebugDraw",this.drawDebugRect),this},drawDebugRect:function(){ctx=b.DebugCanvas.context;var a=this.debugRect;null!==a&&void 0!==a&&a._h&&a._w&&(this._debug.fillStyle&&ctx.fillRect(a._x,a._y,a._w,a._h),this._debug.strokeStyle&&ctx.strokeRect(a._x,a._y,a._w,a._h))}}),b.c("VisibleMBR",{init:function(){this.requires("DebugRectangle").debugFill("purple").bind("EnterFrame",this._assignRect)},_assignRect:function(){this.debugRectangle(this._mbr?this._mbr:this)}}),b.c("DebugPolygon",{init:function(){this.requires("2D, DebugCanvas")},debugPolygon:function(a){return this.polygon=a,this.unbind("DebugDraw",this.drawDebugPolygon),this.bind("DebugDraw",this.drawDebugPolygon),this},drawDebugPolygon:function(){if("undefined"!=typeof this.polygon){ctx=b.DebugCanvas.context,ctx.beginPath();for(var a in this.polygon.points)ctx.lineTo(this.polygon.points[a][0],this.polygon.points[a][1]);ctx.closePath(),this._debug.fillStyle&&ctx.fill(),this._debug.strokeStyle&&ctx.stroke()}}}),b.c("WiredHitBox",{init:function(){this.requires("DebugPolygon").debugStroke("red").matchHitBox(),this.bind("NewHitbox",this.matchHitBox)},matchHitBox:function(){this.debugPolygon(this.map)}}),b.c("SolidHitBox",{init:function(){this.requires("Collision, DebugPolygon").debugFill("orange").debugAlpha(.7).matchHitBox(),this.bind("NewHitbox",this.matchHitBox)},matchHitBox:function(){this.debugPolygon(this.map)}}),b.DebugCanvas={context:null,entities:[],onetimeEntities:[],add:function(a){this.entities.push(a)},remove:function(a){for(var b=this.entities,c=b.length-1;c>=0;c--)b[c]==a&&b.splice(c,1)},init:function(){if(!b.DebugCanvas.context){if(!b.support.canvas)return b.trigger("NoCanvas"),void b.stop();var a;a=c.createElement("canvas"),a.width=b.viewport.width,a.height=b.viewport.height,a.style.position="absolute",a.style.left="0px",a.style.top="0px",a.id="debug-canvas",a.style.zIndex=1e5,b.stage.elem.appendChild(a),b.DebugCanvas.context=a.getContext("2d"),b.DebugCanvas._canvas=a}b.unbind("RenderScene",b.DebugCanvas.renderScene),b.bind("RenderScene",b.DebugCanvas.renderScene)},renderScene:function(a){a=a||b.viewport.rect();var c,d=b.DebugCanvas.entities,e=0,f=d.length,g=b.DebugCanvas.context,h=b.viewport;for(g.setTransform(h._scale,0,0,h._scale,Math.round(h._x*h._scale),Math.round(h._y*h._scale)),g.clearRect(a._x,a._y,a._w,a._h);f>e;e++)c=d[e],c.debugDraw(g)}}},{"./core.js":10}],4:[function(a,b){function c(a,b,c){this.keys=a,this.map=c,this.obj=b}var d,e=(a("./core.js"),window.document,function(a){d=a||64,this.map={}}),f=" ",g={};e.prototype={insert:function(a){var b,d,f=e.key(a),g=new c(f,a,this),h=0;for(h=f.x1;h<=f.x2;h++)for(b=f.y1;b<=f.y2;b++)d=h<<16^b,this.map[d]||(this.map[d]=[]),this.map[d].push(a);return g},search:function(a,b){var c,d,f,h=e.key(a,g),i=[];for(void 0===b&&(b=!0),c=h.x1;c<=h.x2;c++)for(d=h.y1;d<=h.y2;d++)if(cell=this.map[c<<16^d])for(f=0;fc;c++)j=i[c],j&&(k=j[0],j=j._mbr||j,!n[k]&&j._xa._x&&j._ya._y&&(n[k]=i[c]));for(j in n)m.push(n[j]);return m}return i},remove:function(a,b){var c,d,f=0;for(1==arguments.length&&(b=a,a=e.key(b,g)),f=a.x1;f<=a.x2;f++)for(c=a.y1;c<=a.y2;c++)if(d=f<<16^c,this.map[d]){var h,i=this.map[d],j=i.length;for(h=0;j>h;h++)i[h]&&i[h][0]===b[0]&&i.splice(h,1)}},refresh:function(a){var b,c,d,f,g,h=a.keys,i=a.obj;for(c=h.x1;c<=h.x2;c++)for(d=h.y1;d<=h.y2;d++)if(b=this.map[c<<16^d])for(g=b.length,f=0;g>f;f++)b[f]&&b[f][0]===i[0]&&b.splice(f,1);for(e.key(i,h),c=h.x1;c<=h.x2;c++)for(d=h.y1;d<=h.y2;d++)b=this.map[c<<16^d],b||(b=this.map[c<<16^d]=[]),b.push(i);return a},boundaries:function(){var a,b,c={max:{x:-1/0,y:-1/0},min:{x:1/0,y:1/0}},d={max:{x:-1/0,y:-1/0},min:{x:1/0,y:1/0}};for(var e in this.map)if(this.map[e].length){var f=e>>16,g=e<<16>>16;if(0>g&&(f=-1^f),f>=c.max.x){c.max.x=f;for(a in this.map[e])b=this.map[e][a],"object"==typeof b&&"requires"in b&&(d.max.x=Math.max(d.max.x,b.x+b.w))}if(f<=c.min.x){c.min.x=f;for(a in this.map[e])b=this.map[e][a],"object"==typeof b&&"requires"in b&&(d.min.x=Math.min(d.min.x,b.x))}if(g>=c.max.y){c.max.y=g;for(a in this.map[e])b=this.map[e][a],"object"==typeof b&&"requires"in b&&(d.max.y=Math.max(d.max.y,b.y+b.h))}if(g<=c.min.y){c.min.y=g;for(a in this.map[e])b=this.map[e][a],"object"==typeof b&&"requires"in b&&(d.min.y=Math.min(d.min.y,b.y))}}return d}},e.key=function(a,b){return a._mbr&&(a=a._mbr),b||(b={}),b.x1=Math.floor(a._x/d),b.y1=Math.floor(a._y/d),b.x2=Math.floor((a._w+a._x)/d),b.y2=Math.floor((a._h+a._y)/d),b},e.hash=function(a){return a.x1+f+a.y1+f+a.x2+f+a.y2},c.prototype={update:function(a){e.hash(e.key(a,g))!=e.hash(this.keys)&&this.map.refresh(this)}},b.exports=e},{"./core.js":10}],5:[function(a){{var b=a("./core.js");window.document}b.easing=function(a){this.timePerFrame=1e3/b.timer.FPS(),this.duration=a,this.reset()},b.easing.prototype={duration:0,clock:0,steps:null,complete:!1,paused:!1,reset:function(){this.loops=1,this.clock=0,this.complete=!1,this.paused=!1},repeat:function(a){this.loops=a},setProgress:function(a,b){this.clock=this.duration*a,"undefined"!=typeof b&&(this.loops=b)},pause:function(){this.paused=!0},resume:function(){this.paused=!1,this.complete=!1},tick:function(a){if(!this.paused&&!this.complete)for(this.clock+=a,this.frames=Math.floor(this.clock/this.timePerFrame);this.clock>=this.duration&&this.complete===!1;)this.loops--,this.loops>0?this.clock-=this.duration:this.complete=!0},time:function(){return Math.min(this.clock/this.duration,1)},value:function(){return this.time()}},b.c("Tween",{init:function(){this.tweenGroup={},this.tweenStart={},this.tweens=[],this.bind("EnterFrame",this._tweenTick)},_tweenTick:function(a){var b,c,d;for(d=this.tweens.length-1;d>=0;d--)b=this.tweens[d],b.easing.tick(a.dt),c=b.easing.value(),this._doTween(b.props,c),b.easing.complete&&(this.tweens.splice(d,1),this._endTween(b.props))},_doTween:function(a,b){for(var c in a)this[c]=(1-b)*this.tweenStart[c]+b*a[c]},tween:function(a,c){var d={props:a,easing:new b.easing(c)};for(var e in a)"undefined"!=typeof this.tweenGroup[e]&&this.cancelTween(e),this.tweenStart[e]=this[e],this.tweenGroup[e]=a;return this.tweens.push(d),this},cancelTween:function(a){if("string"==typeof a)"object"==typeof this.tweenGroup[a]&&delete this.tweenGroup[a][a];else if("object"==typeof a)for(var b in a)this.cancelTween(b);return this},_endTween:function(a){for(var b in a)delete this.tweenGroup[b];this.trigger("TweenEnd",a)}})},{"./core.js":10}],6:[function(a){var b=a("./core.js"),c=window.document;b.c("Canvas",{init:function(){b.canvas.context||b.canvas.init(),b.DrawManager.total2D++,this.currentRect={},this._changed=!0,b.DrawManager.addCanvas(this),this.bind("Invalidate",function(){this._changed===!1&&(this._changed=!0,b.DrawManager.addCanvas(this))}),this.bind("Remove",function(){b.DrawManager.total2D--,this._changed=!0,b.DrawManager.addCanvas(this)})},drawVars:{type:"canvas",pos:{},ctx:null,coord:[0,0,0,0],co:{x:0,y:0,w:0,h:0}},draw:function(a,c,d,e,f){if(this.ready){4===arguments.length&&(f=e,e=d,d=c,c=a,a=b.canvas.context);var g=this.drawVars.pos;g._x=this._x+(c||0),g._y=this._y+(d||0),g._w=e||this._w,g._h=f||this._h,context=a||b.canvas.context,coord=this.__coord||[0,0,0,0];var h=this.drawVars.co;h.x=coord[0]+(c||0),h.y=coord[1]+(d||0),h.w=e||coord[2],h.h=f||coord[3],0!==this._rotation&&(context.save(),context.translate(this._origin.x+this._x,this._origin.y+this._y),g._x=-this._origin.x,g._y=-this._origin.y,context.rotate(this._rotation%360*(Math.PI/180))),(this._flipX||this._flipY)&&(context.save(),context.scale(this._flipX?-1:1,this._flipY?-1:1),this._flipX&&(g._x=-(g._x+g._w)),this._flipY&&(g._y=-(g._y+g._h)));var i;return this._alpha<1&&(i=context.globalAlpha,context.globalAlpha=this._alpha),this.drawVars.ctx=context,this.trigger("Draw",this.drawVars),(0!==this._rotation||this._flipX||this._flipY)&&context.restore(),i&&(context.globalAlpha=i),this}}}),b.extend({canvas:{context:null,init:function(){if(!b.support.canvas)return b.trigger("NoCanvas"),void b.stop();var a;a=c.createElement("canvas"),a.width=b.viewport.width,a.height=b.viewport.height,a.style.position="absolute",a.style.left="0px",a.style.top="0px",b.stage.elem.appendChild(a),b.canvas.context=a.getContext("2d"),b.canvas._canvas=a;var d=b.viewport._scale;1!=d&&b.canvas.context.scale(d,d),this._setPixelart(b._pixelartEnabled),b.uniqueBind("PixelartSet",this._setPixelart),b.uniqueBind("RenderScene",b.DrawManager.renderCanvas),b.uniqueBind("ViewportResize",this._resize)},_resize:function(){var a=b.canvas._canvas;a.width=b.viewport.width,a.height=b.viewport.height},_setPixelart:function(a){var c=b.canvas.context;c.imageSmoothingEnabled=!a,c.mozImageSmoothingEnabled=!a,c.webkitImageSmoothingEnabled=!a,c.oImageSmoothingEnabled=!a,c.msImageSmoothingEnabled=!a}}})},{"./core.js":10}],7:[function(a){var b=a("./core.js"),c=(window.document,Math.PI/180);b.c("Collision",{init:function(){this.requires("2D"),this._collisionData={},this.collision()},remove:function(){this._cbr=null,this.unbind("Resize",this._resizeMap),this.unbind("Resize",this._checkBounds)},collision:function(a){if(this.unbind("Resize",this._resizeMap),this.unbind("Resize",this._checkBounds),a){if(arguments.length>1){var d=Array.prototype.slice.call(arguments,0);a=new b.polygon(d)}this._findBounds(a.points)}else a=new b.polygon([0,0],[this._w,0],[this._w,this._h],[0,this._h]),this.bind("Resize",this._resizeMap),this._cbr=null;return this.rotation&&a.rotate({cos:Math.cos(-this.rotation*c),sin:Math.sin(-this.rotation*c),o:{x:this._origin.x,y:this._origin.y}}),this.map=a,this.attach(this.map),this.map.shift(this._x,this._y),this.trigger("NewHitbox",a),this},_findBounds:function(a){for(var b,c=1/0,d=-1/0,e=1/0,f=-1/0,g=0;gd&&(d=b[0]),b[1]f&&(f=b[1]);var h={cx:(c+d)/2,cy:(e+f)/2,r:Math.sqrt((d-c)*(d-c)+(f-e)*(f-e))/2};return c>=0&&e>=0&&(this._checkBounds=function(){null===this._cbr&&this._w=0&&e>=0&&d<=this._w&&f<=this._h?(this._cbr=null,!1):(this._cbr=h,this._calculateMBR(),!0)},_resizeMap:function(a){var b,d,e=this.rotation*c,f=this.map.points;"w"===a.axis?(e?(b=a.amount*Math.cos(e),d=a.amount*Math.sin(e)):(b=a.amount,d=0),f[1][0]+=b,f[1][1]+=d):(e?(d=a.amount*Math.cos(e),b=-a.amount*Math.sin(e)):(b=0,d=a.amount),f[3][0]+=b,f[3][1]+=d),f[2][0]+=b,f[2][1]+=d},hit:function(a){var c,d,e,f,g=this._cbr||this._mbr||this,h=b.map.search(g,!1),i=0,j=h.length,k={},l="map"in this&&"containsPoint"in this.map,m=[];if(!j)return!1;for(;j>i;++i)d=h[i],e=d._cbr||d._mbr||d,d&&(c=d[0],!k[c]&&this[0]!==c&&d.__c[a]&&e._xg._x&&e._yg._y&&(k[c]=d));for(f in k)if(d=k[f],l&&"map"in d){var n=this._SAT(this.map,d.map);n.obj=d,n.type="SAT",n&&m.push(n)}else m.push({obj:d,type:"MBR"});return m.length?m:!1},onHit:function(a,b,c){var d=!1;return this.bind("EnterFrame",function(){var e=this.hit(a);e?(d=!0,b.call(this,e)):d&&("function"==typeof c&&c.call(this),d=!1)}),this},_createCollisionHandler:function(a,b){return function(){var c=this.hit(a);if(b.occurring===!0){if(c!==!1)return;b.occurring=!1,this.trigger("HitOff",a)}else c!==!1&&(b.occurring=!0,this.trigger("HitOn",c))}},checkHits:function(){var a=arguments,b=0;for(1===a.length&&(a=a[0].split(/\s*,\s*/));bo;o++){for(k=m[o==p-1?0:o+1],l=m[o],r.x=-(k[1]-l[1]),r.y=k[0]-l[0],d=Math.sqrt(r.x*r.x+r.y*r.y),r.x/=d,r.y/=d,e=f=-1,g=h=-1,c=0;p>c;++c)j=m[c][0]*r.x+m[c][1]*r.y,(j>g||-1===g)&&(g=j),(e>j||-1===e)&&(e=j);for(c=0;q>c;++c)j=n[c][0]*r.x+n[c][1]*r.y,(j>h||-1===h)&&(h=j),(f>j||-1===f)&&(f=j);if(f>e?(i=f-g,r.x=-r.x,r.y=-r.y):i=e-h,i>=0)return!1;(null===s||i>s)&&(s=i,u={x:r.x,y:r.y})}for(o=0;q>o;o++){for(k=n[o==q-1?0:o+1],l=n[o],r.x=-(k[1]-l[1]),r.y=k[0]-l[0],d=Math.sqrt(r.x*r.x+r.y*r.y),r.x/=d,r.y/=d,e=f=-1,g=h=-1,c=0;p>c;++c)j=m[c][0]*r.x+m[c][1]*r.y,(j>g||-1===g)&&(g=j),(e>j||-1===e)&&(e=j);for(c=0;q>c;++c)j=n[c][0]*r.x+n[c][1]*r.y,(j>h||-1===h)&&(h=j),(f>j||-1===f)&&(f=j);if(f>e?(i=f-g,r.x=-r.x,r.y=-r.y):i=e-h,i>=0)return!1;(null===s||i>s)&&(s=i),(i>t||null===t)&&(t=i,u={x:r.x,y:r.y})}return{overlap:t,normal:u}}})},{"./core.js":10}],8:[function(a){var b=a("./core.js"),c=window.document;b.extend({assignColor:function(){function a(a){return a._red=a._blue=a._green=0,a}function b(a){var b=a.toString(16);return 1==b.length&&(b="0"+b),b}function d(a,c,d){return"#"+b(a)+b(c)+b(d)}function e(b,c){var d;if(7===b.length)d=2;else{if(4!==b.length)return a(c);d=1}return c._red=parseInt(b.substr(1,d),16),c._green=parseInt(b.substr(1+d,d),16),c._blue=parseInt(b.substr(1+2*d,d),16),c}function f(b,c){var d=l.exec(b);return null===d||4!=d.length&&5!=d.length?a(c):(c._red=Math.round(parseFloat(d[1])),c._green=Math.round(parseFloat(d[2])),c._blue=Math.round(parseFloat(d[3])),d[4]&&(c._strength=parseFloat(d[4])),c)}function g(a,b){if("undefined"==typeof k[a]){j===!1&&(window.document.body.appendChild(i),j=!0),i.style.color=a;var c=window.getComputedStyle(i).color;f(c,b),k[a]=d(b._red,b._green,b._blue)}else e(k[a],b);return b}function h(a){return"rgba("+a._red+", "+a._green+", "+a._blue+", "+a._strength+")"}var i=c.createElement("div");i.style.display="none";var j=!1,k={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#00ff00",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",orange:"#ffa500",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00"},l=/rgba?\s*\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,?\s*([0-9.]+)?\)/;return function(a,b){b=b||{},a=a.trim().toLowerCase();var c=null;c="#"===a[0]?e(a,b):"r"===a[0]&&"g"===a[1]&&"b"===a[2]?f(a,b):g(a,b),b._strength=b._strength||1,b._color=h(b)}}()}),b.c("Color",{_red:0,_green:0,_blue:0,_strength:1,_color:"",ready:!0,init:function(){this.bind("Draw",this._drawColor),this.trigger("Invalidate")},remove:function(){this.unbind("Draw",this._drawColor),this.has("DOM")&&(this._element.style.backgroundColor="transparent"),this.trigger("Invalidate")},_drawColor:function(a){this._color&&("DOM"===a.type?(a.style.backgroundColor=this._color,a.style.lineHeight=0):"canvas"===a.type&&(a.ctx.fillStyle=this._color,a.ctx.fillRect(a.pos._x,a.pos._y,a.pos._w,a.pos._h))) +},color:function(a){return 0===arguments.length?this._color:(arguments.length>=3?(this._red=arguments[0],this._green=arguments[1],this._blue=arguments[2],"number"==typeof arguments[3]&&(this._strength=arguments[3])):(b.assignColor(a,this),"number"==typeof arguments[1]&&(this._strength=arguments[1])),this._color="rgba("+this._red+", "+this._green+", "+this._blue+", "+this._strength+")",this.trigger("Invalidate"),this)}})},{"./core.js":10}],9:[function(a){var b=a("./core.js"),c=window.document;b.extend({over:null,mouseObjs:0,mousePos:{},lastEvent:null,keydown:{},selected:!1,detectBlur:function(a){var c=a.clientX>b.stage.x&&a.clientXb.stage.y&&a.clientYi;++i)if(d[i].__c.Mouse&&d[i]._visible){var n=d[i],o=!1;if(!k[n[0]]&&(k[n[0]]=!0,n.mapArea?n.mapArea.containsPoint(f,g)&&(o=!0):n.isAt(f,g)&&(o=!0),o&&(n._z>=h||-1===h))){if(n._z===h&&n[0]=112&&a.key<=135)?(a.stopPropagation?a.stopPropagation():a.cancelBubble=!0,a.target&&"INPUT"!==a.target.nodeName&&"TEXTAREA"!==a.target.nodeName&&(a.preventDefault?a.preventDefault():a.returnValue=!1),!1):void 0}}),b.bind("Load",function(){b.addEvent(this,"keydown",b.keyboardDispatch),b.addEvent(this,"keyup",b.keyboardDispatch),b.addEvent(this,b.stage.elem,"mousedown",b.mouseDispatch),b.addEvent(this,b.stage.elem,"mouseup",b.mouseDispatch),b.addEvent(this,c.body,"mouseup",b.detectBlur),b.addEvent(this,window,"blur",b.resetKeyDown),b.addEvent(this,b.stage.elem,"mousemove",b.mouseDispatch),b.addEvent(this,b.stage.elem,"click",b.mouseDispatch),b.addEvent(this,b.stage.elem,"dblclick",b.mouseDispatch),b.addEvent(this,b.stage.elem,"touchstart",b.touchDispatch),b.addEvent(this,b.stage.elem,"touchmove",b.touchDispatch),b.addEvent(this,b.stage.elem,"touchend",b.touchDispatch),b.addEvent(this,b.stage.elem,"touchcancel",b.touchDispatch),b.addEvent(this,b.stage.elem,"touchleave",b.touchDispatch)}),b.bind("CraftyStop",function(){b.removeEvent(this,"keydown",b.keyboardDispatch),b.removeEvent(this,"keyup",b.keyboardDispatch),b.stage&&(b.removeEvent(this,b.stage.elem,"mousedown",b.mouseDispatch),b.removeEvent(this,b.stage.elem,"mouseup",b.mouseDispatch),b.removeEvent(this,b.stage.elem,"mousemove",b.mouseDispatch),b.removeEvent(this,b.stage.elem,"click",b.mouseDispatch),b.removeEvent(this,b.stage.elem,"dblclick",b.mouseDispatch),b.removeEvent(this,b.stage.elem,"touchstart",b.touchDispatch),b.removeEvent(this,b.stage.elem,"touchmove",b.touchDispatch),b.removeEvent(this,b.stage.elem,"touchend",b.touchDispatch),b.removeEvent(this,b.stage.elem,"touchcancel",b.touchDispatch),b.removeEvent(this,b.stage.elem,"touchleave",b.touchDispatch)),b.removeEvent(this,c.body,"mouseup",b.detectBlur),b.removeEvent(this,window,"blur",b.resetKeyDown)}),b.c("Mouse",{init:function(){b.mouseObjs++,this.bind("Remove",function(){b.mouseObjs--})},areaMap:function(a){if(arguments.length>1){var c=Array.prototype.slice.call(arguments,0);a=new b.polygon(c)}return a.shift(this._x,this._y),this.mapArea=a,this.attach(this.mapArea),this}}),b.c("Draggable",{_origMouseDOMPos:null,_oldX:null,_oldY:null,_dragging:!1,_dir:null,init:function(){this.requires("Mouse"),this.enableDrag()},_ondrag:function(a){var c=b.DOM.translate(a.clientX,a.clientY);if(0===c.x||0===c.y)return!1;if(this._dir){var d=(c.x-this._origMouseDOMPos.x)*this._dir.x+(c.y-this._origMouseDOMPos.y)*this._dir.y;this.x=this._oldX+d*this._dir.x,this.y=this._oldY+d*this._dir.y}else this.x=this._oldX+(c.x-this._origMouseDOMPos.x),this.y=this._oldY+(c.y-this._origMouseDOMPos.y);this.trigger("Dragging",a)},_ondown:function(a){a.mouseButton===b.mouseButtons.LEFT&&this._startDrag(a)},_onup:function(a){a.mouseButton===b.mouseButtons.LEFT&&this._dragging===!0&&(b.removeEvent(this,b.stage.elem,"mousemove",this._ondrag),b.removeEvent(this,b.stage.elem,"mouseup",this._onup),this._dragging=!1,this.trigger("StopDrag",a))},dragDirection:function(a){if("undefined"==typeof a)this._dir=null;else if(""+parseInt(a,10)==a)this._dir={x:Math.cos(a/180*Math.PI),y:Math.sin(a/180*Math.PI)};else{var b=Math.sqrt(a.x*a.x+a.y*a.y);this._dir={x:a.x/b,y:a.y/b}}},_startDrag:function(a){this._origMouseDOMPos=b.DOM.translate(a.clientX,a.clientY),this._oldX=this._x,this._oldY=this._y,this._dragging=!0,b.addEvent(this,b.stage.elem,"mousemove",this._ondrag),b.addEvent(this,b.stage.elem,"mouseup",this._onup),this.trigger("StartDrag",a)},stopDrag:function(){return b.removeEvent(this,b.stage.elem,"mousemove",this._ondrag),b.removeEvent(this,b.stage.elem,"mouseup",this._onup),this._dragging=!1,this.trigger("StopDrag"),this},startDrag:function(){return this._dragging||this._startDrag(b.lastEvent),this},enableDrag:function(){return this.bind("MouseDown",this._ondown),b.addEvent(this,b.stage.elem,"mouseup",this._onup),this},disableDrag:function(){return this.unbind("MouseDown",this._ondown),this._dragging&&this.stopDrag(),this}}),b.c("Keyboard",{isDown:function(a){return"string"==typeof a&&(a=b.keys[a]),!!b.keydown[a]}}),b.c("Multiway",{_speed:3,_keydown:function(a){this._keys[a.key]&&(this._movement.x=Math.round(1e3*(this._movement.x+this._keys[a.key].x))/1e3,this._movement.y=Math.round(1e3*(this._movement.y+this._keys[a.key].y))/1e3,this.trigger("NewDirection",this._movement))},_keyup:function(a){this._keys[a.key]&&(this._movement.x=Math.round(1e3*(this._movement.x-this._keys[a.key].x))/1e3,this._movement.y=Math.round(1e3*(this._movement.y-this._keys[a.key].y))/1e3,this.trigger("NewDirection",this._movement))},_enterframe:function(){this.disableControls||(0!==this._movement.x&&(this.x+=this._movement.x,this.trigger("Moved",{x:this.x-this._movement.x,y:this.y})),0!==this._movement.y&&(this.y+=this._movement.y,this.trigger("Moved",{x:this.x,y:this.y-this._movement.y})))},_initializeControl:function(){return this.unbind("KeyDown",this._keydown).unbind("KeyUp",this._keyup).unbind("EnterFrame",this._enterframe).bind("KeyDown",this._keydown).bind("KeyUp",this._keyup).bind("EnterFrame",this._enterframe)},multiway:function(a,c){this._keyDirection={},this._keys={},this._movement={x:0,y:0},this._speed={x:3,y:3},c?void 0!==a.x&&void 0!==a.y?(this._speed.x=a.x,this._speed.y=a.y):(this._speed.x=a,this._speed.y=a):c=a,this._keyDirection=c,this.speed(this._speed),this._initializeControl();for(var d in c)b.keydown[b.keys[d]]&&this.trigger("KeyDown",{key:b.keys[d]});return this},enableControl:function(){return this.disableControls=!1,this},disableControl:function(){return this.disableControls=!0,this},speed:function(a){for(var c in this._keyDirection){var d=b.keys[c]||c;this._keys[d]={x:Math.round(1e3*Math.cos(this._keyDirection[c]*(Math.PI/180))*a.x)/1e3,y:Math.round(1e3*Math.sin(this._keyDirection[c]*(Math.PI/180))*a.y)/1e3}}return this}}),b.c("Fourway",{init:function(){this.requires("Multiway")},fourway:function(a){return this.multiway(a,{UP_ARROW:-90,DOWN_ARROW:90,RIGHT_ARROW:0,LEFT_ARROW:180,W:-90,S:90,D:0,A:180,Z:-90,Q:180}),this}}),b.c("Twoway",{_speed:3,_up:!1,init:function(){this.requires("Fourway, Keyboard, Gravity")},twoway:function(a,c){return this.multiway(a,{RIGHT_ARROW:0,LEFT_ARROW:180,D:0,A:180,Q:180}),a&&(this._speed=a),this._jumpSpeed=arguments.length<2?2*this._speed:c,this.bind("EnterFrame",function(){this.disableControls||this._up&&(this.y-=this._jumpSpeed,this._falling=!0,this.trigger("Moved",{x:this._x,y:this._y+this._jumpSpeed}))}).bind("KeyDown",function(a){this._falling||a.key!==b.keys.UP_ARROW&&a.key!==b.keys.W&&a.key!==b.keys.Z||(this._up=!0)}),this}})},{"./core.js":10}],10:[function(a,b){function c(){var a=e++;return a in h?c():a}function d(a){if(null===a||"object"!=typeof a)return a;var b=a.constructor();for(var c in a)b[c]=d(a[c]);return b}var e,f,g,h,i,j,k,l,m,n=a("./version"),o=function(a){return new o.fn.init(a)};initState=function(){e=1,f=0,g={},h={},i={},j=[],k=Array.prototype.slice,l=/\s*,\s*/,m=/\s+/},initState(),o.fn=o.prototype={init:function(a){if("string"!=typeof a)return a||(a=0,a in h||(h[a]=this)),a in h?(this[0]=a,this.length=1,this.__c||(this.__c={}),h[a]||(h[a]=this),h[a]):(this.length=0,this);var b,c,d,e,f,i,j,k=0,n=!1,o=!1;if("*"===a){i=0;for(b in h)this[i]=+b,i++;return this.length=i,1===i?h[this[0]]:this}-1!==a.indexOf(",")?(o=!0,d=l):-1!==a.indexOf(" ")&&(n=!0,d=m);for(b in h)if(h.hasOwnProperty(b))if(c=h[b],n||o){for(e=a.split(d),i=0,j=e.length,f=0;j>i;i++)c.__c[e[i]]&&f++;(n&&f===j||o&&f>0)&&(this[k++]=+b)}else c.__c[a]&&(this[k++]=+b);if(k>0&&!n&&!o&&this.extend(g[a]),e&&n)for(i=0;j>i;i++)this.extend(g[e[i]]);return this.length=k,1===k?h[this[k-1]]:this},setName:function(a){var b=String(a);return this._entityName=b,this.trigger("NewEntityName",b),this},addComponent:function(a){var b=[],c=0;if(arguments.length>1)for(var d=0;d1)for(b=arguments.length;b>d;d++)this.has(arguments[d])?this.removeComponent(arguments[d]):this.addComponent(arguments[d]);else if(-1!==a.indexOf(","))for(c=a.split(l),b=c.length;b>d;d++)this.has(c[d])?this.removeComponent(c[d]):this.addComponent(c[d]);else this.has(a)?this.removeComponent(a):this.addComponent(a);return this},requires:function(a){return this.addComponent(a)},removeComponent:function(a,b){var c=g[a];if(this.trigger("RemoveComponent",a),c&&"remove"in c&&c.remove.call(this,!1),b===!1&&c)for(var d in c)delete this[d];return delete this.__c[a],this},getId:function(){return this[0]},has:function(a){return!!this.__c[a]},attr:function(a,b,c,d){return 1===arguments.length&&"string"==typeof arguments[0]?this._attr_get(a):this._attr_set(a,b,c,d)},_attr_get:function(a,b){var c,d,e;return("undefined"==typeof b||null===b)&&(b=this),a.indexOf(".")>-1?(d=a.split("."),c=d.shift(),e=d.join("."),this._attr_get(d.join("."),b[c])):b[a]},_attr_set:function(){var a,b,c;return"string"==typeof arguments[0]?(a=this._set_create_object(arguments[0],arguments[1]),b=!!arguments[2],c=arguments[3]||arguments[0].indexOf(".")>-1):(a=arguments[0],b=!!arguments[1],c=!!arguments[2]),b||this.trigger("Change",a),c?this._recursive_extend(a,this):this.extend.call(this,a),this},_set_create_object:function(a,b){var c,d,e,f={};return a.indexOf(".")>-1?(c=a.split("."),d=c.shift(),e=c.join("."),f[d]=this._set_create_object(e,b)):f[a]=b,f},_recursive_extend:function(a,b){var c;for(c in a)b[c]="Object"===a[c].constructor.name?this._recursive_extend(a[c],b[c]):a[c];return b},toArray:function(){return k.call(this,0)},timeout:function(a,b){return this.each(function(){var c=this;setTimeout(function(){a.call(c)},b)}),this},bind:function(a,b){if(1===this.length){i[a]||(i[a]={});var c=i[a];return c[this[0]]||(c[this[0]]=[]),c[this[0]].push(b),this}return this.each(function(){i[a]||(i[a]={});var c=i[a];c[this[0]]||(c[this[0]]=[]),c[this[0]].push(b)}),this},uniqueBind:function(a,b){this.unbind(a,b),this.bind(a,b)},one:function(a,b){var c=this,d=function(e){b.call(c,e),c.unbind(a,d)};return c.bind(a,d)},unbind:function(a,b){return this.each(function(){var c,d,e=i[a],f=0;if(!e||!e[this[0]])return this;if(c=e[this[0]].length,!b)return delete e[this[0]],this;for(;c>f;f++)d=e[this[0]],d[f]==b&&delete d[f]}),this},trigger:function(a,b){if(1===this.length){if(i[a]&&i[a][this[0]]){var c,d=i[a][this[0]];for(c=0;cb;b++)h[this[b]]&&a.call(h[this[b]],b);return this},get:function(a){var b=this.length;if("undefined"!=typeof a)return a>=b||0>a+b?void 0:a>=0?h[this[a]]:h[this[a+b]];for(var c=0,d=[];b>c;c++)h[this[c]]&&d.push(h[this[c]]);return d},clone:function(){var a,b,c=this.__c,d=o.e();for(a in c)d.addComponent(a);for(b in this)"0"!=b&&"_global"!=b&&"_changed"!=b&&"function"!=typeof this[b]&&"object"!=typeof this[b]&&(d[b]=this[b]);return d},setter:function(a,b){return o.support.setter?this.__defineSetter__(a,b):o.support.defineProperty&&Object.defineProperty(this,a,{set:b,configurable:!0}),this},destroy:function(){this.each(function(){var a;this.trigger("Remove");for(var b in this.__c)a=g[b],a&&"remove"in a&&a.remove.call(this,!0);for(var c in i)this.unbind(c);delete h[this[0]]})}},o.fn.init.prototype=o.fn,o.extend=o.fn.extend=function(a){var b,c=this;if(!a)return c;for(b in a)c!==a[b]&&(c[b]=a[b]);return c},o.extend({init:function(a,b,c){return o.viewport.init(a,b,c),this.trigger("Load"),this.timer.init(),this},getVersion:function(){return n},stop:function(a){if(this.timer.stop(),a){if(o.audio.remove(),o.stage&&o.stage.elem.parentNode){var b=document.createElement("div");b.id=o.stage.elem.id,o.stage.elem.parentNode.replaceChild(b,o.stage.elem)}initState()}return o.trigger("CraftyStop"),this},pause:function(a){return(1===arguments.length?a:!this._paused)?(this.trigger("Pause"),this._paused=!0,setTimeout(function(){o.timer.stop()},0),o.keydown={}):(this.trigger("Unpause"),this._paused=!1,setTimeout(function(){o.timer.init()},0)),this},isPaused:function(){return this._paused},timer:function(){var a,b,c,d="fixed",e=5,g=40,h=0,i=0,j=50,k=1e3/j;return{init:function(){"undefined"==typeof c&&(c=(new Date).getTime()-k);var d=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||null;d?(a=function(){o.timer.step(),b=d(a)})():a=setInterval(function(){o.timer.step()},1e3/j)},stop:function(){o.trigger("CraftyStopTimer"),"number"==typeof a&&clearInterval(a);var c=window.cancelAnimationFrame||window.cancelRequestAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelRequestAnimationFrame||window.oCancelRequestAnimationFrame||window.msCancelRequestAnimationFrame||null;c&&c(b),a=null},steptype:function(a,b){if("variable"===a||"semifixed"===a)d=a,b&&(g=b);else{if("fixed"!==a)throw"Invalid step type specified";d="fixed",b&&(e=b)}},step:function(){var a,b,j,l=0;if(currentTime=(new Date).getTime(),h>0&&o.trigger("MeasureWaitTime",currentTime-h),c+i>=currentTime)return void(h=currentTime);var m=currentTime-(c+i);m>20*k&&(i+=m-k,m=k),"fixed"===d?(l=Math.ceil(m/k),l=Math.min(l,e),b=k):"variable"===d?(l=1,b=m,b=Math.min(b,g)):"semifixed"===d&&(l=Math.ceil(m/g),b=m/l);for(var n=0;l>n;n++){j=currentTime;var p={frame:f++,dt:b,gameTime:c};o.trigger("EnterFrame",p),o.trigger("ExitFrame",p),c+=b,currentTime=(new Date).getTime(),o.trigger("MeasureFrameTime",currentTime-j)}l>0&&(a=currentTime,o.trigger("PreRender"),o.trigger("RenderScene"),o.trigger("PostRender"),currentTime=(new Date).getTime(),o.trigger("MeasureRenderTime",currentTime-a)),h=currentTime},FPS:function(a){return"undefined"==typeof a?j:(j=a,void(k=1e3/j))},simulateFrames:function(a,b){for("undefined"==typeof b&&(b=k);a-->0;){var c={frame:f++,dt:b};o.trigger("EnterFrame",c),o.trigger("ExitFrame",c)}o.trigger("PreRender"),o.trigger("RenderScene"),o.trigger("PostRender")}}}(),e:function(){var a=c();return h[a]=null,h[a]=o(a),arguments.length>0&&h[a].addComponent.apply(h[a],arguments),h[a].setName("Entity #"+a),h[a].addComponent("obj"),o.trigger("NewEntity",{id:a}),h[a]},c:function(a,b){g[a]=b},trigger:function(a,b){var c,d,e,f,g=i[a];for(c in g)if(g.hasOwnProperty(c)&&(e=g[c],e&&0!==e.length))for(f=h[c]?o(+c):o,d=0;dc;c++)e[c]===b&&(f=!0,delete e[c]);return f},frame:function(){return f},components:function(){return g},isComp:function(a){return a in g},debug:function(a){return"handlers"===a?i:h},settings:function(){var a={},b={};return{register:function(a,c){b[a]=c},modify:function(c,d){b[c]&&(b[c].call(a[c],d),a[c]=d)},get:function(b){return a[b]}}}(),clone:d}),"function"==typeof define&&define("crafty",[],function(){return o}),b.exports=o},{"./version":30}],11:[function(a){var b=a("./core");a("./2D"),a("./animation"),a("./canvas"),a("./collision"),a("./color"),a("./controls"),a("./DebugLayer"),a("./device"),a("./diamondiso"),a("./DOM"),a("./drawing"),a("./extensions"),a("./HashMap"),a("./html"),a("./isometric"),a("./keycodes"),a("./loader"),a("./math"),a("./model"),a("./particles"),a("./scenes"),a("./sound"),a("./sprite-animation"),a("./sprite"),a("./storage"),a("./text"),a("./time"),a("./version"),a("./viewport"),window.Crafty=b},{"./2D":1,"./DOM":2,"./DebugLayer":3,"./HashMap":4,"./animation":5,"./canvas":6,"./collision":7,"./color":8,"./controls":9,"./core":10,"./device":12,"./diamondiso":13,"./drawing":14,"./extensions":15,"./html":16,"./isometric":17,"./keycodes":18,"./loader":19,"./math":20,"./model":21,"./particles":22,"./scenes":23,"./sound":24,"./sprite":26,"./sprite-animation":25,"./storage":27,"./text":28,"./time":29,"./version":30,"./viewport":31}],12:[function(a){{var b=a("./core.js");window.document}b.extend({device:{_deviceOrientationCallback:!1,_deviceMotionCallback:!1,_normalizeDeviceOrientation:function(a){var c;window.DeviceOrientationEvent?c={tiltLR:a.gamma,tiltFB:a.beta,dir:a.alpha,motUD:null}:window.OrientationEvent&&(c={tiltLR:90*a.x,tiltFB:-90*a.y,dir:null,motUD:a.z}),b.device._deviceOrientationCallback(c)},_normalizeDeviceMotion:function(a){var c=a.accelerationIncludingGravity,d=c.z>0?1:-1,e={acceleration:c,rawAcceleration:"["+Math.round(c.x)+", "+Math.round(c.y)+", "+Math.round(c.z)+"]",facingUp:d,tiltLR:Math.round(c.x/9.81*-90),tiltFB:Math.round((c.y+9.81)/9.81*90*d)};b.device._deviceMotionCallback(e)},deviceOrientation:function(a){this._deviceOrientationCallback=a,b.support.deviceorientation&&(window.DeviceOrientationEvent?b.addEvent(this,window,"deviceorientation",this._normalizeDeviceOrientation):window.OrientationEvent&&b.addEvent(this,window,"MozOrientation",this._normalizeDeviceOrientation))},deviceMotion:function(a){this._deviceMotionCallback=a,b.support.devicemotion&&window.DeviceMotionEvent&&b.addEvent(this,window,"devicemotion",this._normalizeDeviceMotion)}}})},{"./core.js":10}],13:[function(a){{var b=a("./core.js");window.document}b.extend({diamondIso:{_tile:{width:0,height:0,r:0},_map:{width:0,height:0,x:0,y:0},_origin:{x:0,y:0},init:function(a,b,c,d){return this._tile.width=parseInt(a,10),this._tile.height=parseInt(b,10)||parseInt(a,10)/2,this._tile.r=this._tile.width/this._tile.height,this._map.width=parseInt(c,10),this._map.height=parseInt(d,10)||parseInt(c,10),this._origin.x=this._map.height*this._tile.width/2,this},place:function(a,b,c,d){var e=this.pos2px(b,c);d||(d=1);var f=0,g=0;void 0!==a.__margin&&(f=a.__margin[0],g=a.__margin[1]),a.x=e.left+f,a.y=e.top+g-a.h,a.z=e.top*d},centerAt:function(a,c){var d=this.pos2px(a,c);b.viewport.x=-d.left+b.viewport.width/2-this._tile.width,b.viewport.y=-d.top+b.viewport.height/2},area:function(a){a||(a=0);var c=b.viewport.rect(),d=c._x,e=c._y,f=c._w,g=c._h,h=a*this._tile.width,i=a*this._tile.height;d-=this._tile.width/2+h,e-=this._tile.height/2+i,f+=this._tile.width/2+h,g+=this._tile.height/2+i;var j=[];for(yl=e+g;yl>e;e+=this._tile.height/2)for(xl=d+f;xl>d;d+=this._tile.width/2){var k=this.px2pos(d,e);j.push([~~k.x,~~k.y])}return j},pos2px:function(a,b){return{left:(a-b)*this._tile.width/2+this._origin.x,top:(a+b)*this._tile.height/2}},px2pos:function(a,b){var c=(a-this._origin.x)/this._tile.r;return{x:(b+c)/this._tile.height,y:(b-c)/this._tile.height}},polygon:function(a){a.requires("Collision");var c=0,d=0;void 0!==a.__margin&&(c=a.__margin[0],d=a.__margin[1]);var e=[[c-0,a.h-d-this._tile.height/2],[c-this._tile.width/2,a.h-d-0],[c-this._tile.width,a.h-d-this._tile.height/2],[c-this._tile.width/2,a.h-d-this._tile.height]],f=new b.polygon(e);return f}}})},{"./core.js":10}],14:[function(a){{var b=a("./core.js");window.document}b.c("Image",{_repeat:"repeat",ready:!1,init:function(){var a=function(a){if("canvas"===a.type){if(!this.ready||!this._pattern)return;var b=a.ctx;b.fillStyle=this._pattern,b.save(),b.translate(a.pos._x,a.pos._y),b.fillRect(0,0,this._w,this._h),b.restore()}else"DOM"===a.type&&this.__image&&(a.style.backgroundImage="url("+this.__image+")",a.style.backgroundRepeat=this._repeat)};this.bind("Draw",a).bind("RemoveComponent",function(b){"Image"===b&&this.unbind("Draw",a)})},image:function(a,c){if(this.__image=a,this._repeat=c||"no-repeat",this.img=b.asset(a),!this.img){this.img=new Image,b.asset(a,this.img),this.img.src=a;var d=this;return this.img.onload=function(){d.has("Canvas")&&(d._pattern=b.canvas.context.createPattern(d.img,d._repeat)),d.ready=!0,"no-repeat"===d._repeat&&(d.w=d.img.width,d.h=d.img.height),d.trigger("Invalidate")},this}return this.ready=!0,this.has("Canvas")&&(this._pattern=b.canvas.context.createPattern(this.img,this._repeat)),"no-repeat"===this._repeat&&(this.w=this.img.width,this.h=this.img.height),this.trigger("Invalidate"),this}}),b.DrawManager=function(){function a(a,b){return a._globalZ-b._globalZ}var c=[],d=[],e=[],f=!1,g={merge:function(a,b,c){return"undefined"==typeof c&&(c={}),c._h=Math.max(a._y+a._h,b._y+b._h),c._w=Math.max(a._x+a._w,b._x+b._w),c._x=Math.min(a._x,b._x),c._y=Math.min(a._y,b._y),c._w-=c._x,c._h-=c._y,c},clean:function(){var a,b,e;for(e=0,l=d.length;l>e;e++)b=d[e],a=b._mbr||b,"undefined"==typeof b.staleRect&&(b.staleRect={}),b.staleRect._x=a._x,b.staleRect._y=a._y,b.staleRect._w=a._w,b.staleRect._h=a._h,b._changed=!1;d.length=0,c.length=0},createDirty:function(a){var b=a._mbr||a;if(a.staleRect){if(g.overlap(a.staleRect,b))return g.merge(a.staleRect,b,a.staleRect),void c.push(a.staleRect);c.push(a.staleRect)}a.currentRect._x=b._x,a.currentRect._y=b._y,a.currentRect._w=b._w,a.currentRect._h=b._h,c.push(a.currentRect)},overlap:function(a,b){return a._xb._x&&a._y+a._h>b._y}};return b.bind("InvalidateViewport",function(){f=!0}),b.bind("PostRender",function(){f=!1}),{total2D:b("2D").length,onScreen:function(a){return b.viewport._x+a._x+a._w>0&&b.viewport._y+a._y+a._h>0&&b.viewport._x+a._x0&&b--):b++;return a},addCanvas:function(a){d.push(a)},addDom:function(a){e.push(a)},debug:function(){console.log(d,e)},drawAll:function(c){c=c||b.viewport.rect();var d,e=b.map.search(c),f=0,g=e.length,h=b.canvas.context;for(h.clearRect(c._x,c._y,c._w,c._h),e.sort(a);g>f;f++)d=e[f],d._visible&&d.__c.Canvas&&(d.draw(),d._changed=!1)},boundingRect:function(a){if(a&&a.length){var b,c,d=1,e=a.length,f=a[0];for(f=[f._x,f._y,f._x+f._w,f._y+f._h];e>d;)b=a[d],c=[b._x,b._y,b._x+b._w,b._y+b._h],c[0]f[2]&&(f[2]=c[2]),c[3]>f[3]&&(f[3]=c[3]),d++;return c=f,f={_x:c[0],_y:c[1],_w:c[2]-c[0],_h:c[3]-c[1]}}},renderCanvas:function(){var e=d.length;if(e||f){var h,i,j,k,l,m=0,n=b.canvas.context,o=b.DrawManager;if(f){var p=b.viewport;n.setTransform(p._scale,0,0,p._scale,Math.round(p._x*p._scale),Math.round(p._y*p._scale))}if(e/o.total2D>.6||f)return o.drawAll(),void g.clean();for(m=0;e>m;m++)g.createDirty(d[m]);c=o.mergeSet(c),e=c.length;var q=[],r=[];for(m=0;e>m;++m)if(h=c[m],q.length=0,r.length=0,h){for(h._w=h._x+h._w,h._h=h._y+h._h,h._x=h._x>0?0|h._x:(0|h._x)-1,h._y=h._y>0?0|h._y:(0|h._y)-1,h._w-=h._x,h._h-=h._y,h._w=h._w===(0|h._w)?h._w:(0|h._w)+1,h._h=h._h===(0|h._h)?h._h:(0|h._h)+1,i=b.map.search(h,!1),n.clearRect(h._x,h._y,h._w,h._h),n.save(),n.beginPath(),n.rect(h._x,h._y,h._w,h._h),n.clip(),j=0,k=i.length;k>j;++j)l=i[j],!q[l[0]]&&l._visible&&l.__c.Canvas&&(q[l[0]]=!0,r.push(l));for(r.sort(a),j=0,k=r.length;k>j;++j){l=r[j];var s=l._mbr||l;g.overlap(s,h)&&l.draw(),l._changed=!1}n.closePath(),n.restore()}if(b.DrawManager.debugDirty===!0)for(n.strokeStyle="red",m=0,e=c.length;e>m;++m)h=c[m],n.strokeRect(h._x,h._y,h._w,h._h);g.clean()}},renderDOM:function(){if(f){var a=b.stage.inner.style,c=b.viewport;a.transform=a[b.support.prefix+"Transform"]="scale("+c._scale+", "+c._scale+")",a.left=Math.round(c._x*c._scale)+"px",a.top=Math.round(c._y*c._scale)+"px",a.zIndex=10}if(e.length){for(var d=0,g=e.length;g>d;++d)e[d].draw()._changed=!1;e.length=0}}}}(),b.extend({_pixelartEnabled:!1,pixelart:function(a){b._pixelartEnabled=a,b.trigger("PixelartSet",a)}})},{"./core.js":10}],15:[function(a){var b=a("./core.js"),c=window.document;!function(){var a=b.support={},d=navigator.userAgent.toLowerCase(),e=/(webkit)[ \/]([\w.]+)/.exec(d)||/(o)pera(?:.*version)?[ \/]([\w.]+)/.exec(d)||/(ms)ie ([\w.]+)/.exec(d)||/(moz)illa(?:.*? rv:([\w.]+))?/.exec(d)||[],f=/iPad|iPod|iPhone|Android|webOS|IEMobile/i.exec(d);if(f&&(b.mobile=f[0]),a.defineProperty=function(){if(!("defineProperty"in Object))return!1;try{Object.defineProperty({},"x",{})}catch(a){return!1}return!0}(),a.audio="Audio"in window,a.prefix=e[1]||e[0],"moz"===a.prefix&&(a.prefix="Moz"),"o"===a.prefix&&(a.prefix="O"),e[2]&&(a.versionName=e[2],a.version=+e[2].split(".")[0]),a.canvas="getContext"in c.createElement("canvas"),a.canvas){var g;try{g=c.createElement("canvas").getContext("experimental-webgl"),g.viewportWidth=a.canvas.width,g.viewportHeight=a.canvas.height}catch(h){}a.webgl=!!g}else a.webgl=!1;a.css3dtransform="undefined"!=typeof c.createElement("div").style.Perspective||"undefined"!=typeof c.createElement("div").style[a.prefix+"Perspective"],a.deviceorientation="undefined"!=typeof window.DeviceOrientationEvent||"undefined"!=typeof window.OrientationEvent,a.devicemotion="undefined"!=typeof window.DeviceMotionEvent}(),b.extend({_events:{},addEvent:function(a,b,c,d){3===arguments.length&&(d=c,c=b,b=window.document);var e=function(b){b=b||window.event,"function"==typeof d&&d.call(a,b)},f=a[0]||"";this._events[f+b+c+d]||(this._events[f+b+c+d]=e,b.attachEvent?b.attachEvent("on"+c,e):b.addEventListener(c,e,!1))},removeEvent:function(a,b,c,d){3===arguments.length&&(d=c,c=b,b=window.document);var e=a[0]||"",f=this._events[e+b+c+d];f&&(b.detachEvent?b.detachEvent("on"+c,f):b.removeEventListener(c,f,!1),delete this._events[e+b+c+d])},background:function(a){b.stage.elem.style.background=a}})},{"./core.js":10}],16:[function(a){{var b=a("./core.js");window.document}b.c("HTML",{inner:"",init:function(){this.requires("2D, DOM")},replace:function(a){return this.inner=a,this._element.innerHTML=a,this},append:function(a){return this.inner+=a,this._element.innerHTML+=a,this},prepend:function(a){return this.inner=a+this.inner,this._element.innerHTML=a+this.inner,this}})},{"./core.js":10}],17:[function(a){{var b=a("./core.js");window.document}b.extend({isometric:{_tile:{width:0,height:0},_elements:{},_pos:{x:0,y:0},_z:0,size:function(a,b){return this._tile.width=a,this._tile.height=b>0?b:a/2,this},place:function(a,c,d,e){var f=this.pos2px(a,c);return f.top-=d*(this._tile.height/2),e.attr({x:f.left+b.viewport._x,y:f.top+b.viewport._y}).z+=d,this},pos2px:function(a,b){return{left:a*this._tile.width+(1&b)*(this._tile.width/2),top:b*this._tile.height/2}},px2pos:function(a,b){return{x:-Math.ceil(-a/this._tile.width-.5*(1&b)),y:b/this._tile.height*2}},centerAt:function(a,c){if("number"==typeof a&&"number"==typeof c){var d=this.pos2px(a,c);return b.viewport._x=-d.left+b.viewport.width/2-this._tile.width/2,b.viewport._y=-d.top+b.viewport.height/2-this._tile.height/2,this}return{top:-b.viewport._y+b.viewport.height/2-this._tile.height/2,left:-b.viewport._x+b.viewport.width/2-this._tile.width/2}},area:function(){var a=this.centerAt(),c=this.px2pos(-a.left+b.viewport.width/2,-a.top+b.viewport.height/2),d=this.px2pos(-a.left-b.viewport.width/2,-a.top-b.viewport.height/2);return{x:{start:c.x,end:d.x},y:{start:c.y,end:d.y}}}}})},{"./core.js":10}],18:[function(a){{var b=a("./core.js");window.document}b.extend({keys:{BACKSPACE:8,TAB:9,ENTER:13,PAUSE:19,CAPS:20,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT_ARROW:37,UP_ARROW:38,RIGHT_ARROW:39,DOWN_ARROW:40,INSERT:45,DELETE:46,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57,A:65,B:66,C:67,D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80,Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,NUMPAD_0:96,NUMPAD_1:97,NUMPAD_2:98,NUMPAD_3:99,NUMPAD_4:100,NUMPAD_5:101,NUMPAD_6:102,NUMPAD_7:103,NUMPAD_8:104,NUMPAD_9:105,MULTIPLY:106,ADD:107,SUBSTRACT:109,DECIMAL:110,DIVIDE:111,F1:112,F2:113,F3:114,F4:115,F5:116,F6:117,F7:118,F8:119,F9:120,F10:121,F11:122,F12:123,SHIFT:16,CTRL:17,ALT:18,PLUS:187,COMMA:188,MINUS:189,PERIOD:190,PULT_UP:29460,PULT_DOWN:29461,PULT_LEFT:4,PULT_RIGHT:5},mouseButtons:{LEFT:0,MIDDLE:1,RIGHT:2}})},{"./core.js":10}],19:[function(a){{var b=a("./core.js");window.document}b.extend({assets:{},__paths:{audio:"",images:""},paths:function(a){return"undefined"==typeof a?this.__paths:(a.audio&&(this.__paths.audio=a.audio),void(a.images&&(this.__paths.images=a.images))) +},asset:function(a,c){return 1===arguments.length?b.assets[a]:b.assets[a]?void 0:(b.assets[a]=c,this.trigger("NewAsset",{key:a,value:c}),c)},image_whitelist:["jpg","jpeg","gif","png","svg"],load:function(a,c,d,e){function f(){var a=this.src;this.removeEventListener&&this.removeEventListener("canplaythrough",f,!1),++m,d&&d({loaded:m,total:n,percent:m/n*100,src:a}),m===n&&c&&c()}function g(){var a=this.src;e&&e({loaded:m,total:n,percent:m/n*100,src:a}),m++,m===n&&c&&c()}a="string"==typeof a?JSON.parse(a):a;var h,i,j,k,l,m=0,n=(a.audio?Object.keys(a.audio).length:0)+(a.images?Object.keys(a.images).length:0)+(a.sprites?Object.keys(a.sprites).length:0),o=b.support.audio,p=b.paths(),q=function(a){return a.substr(a.lastIndexOf(".")+1,3).toLowerCase()},r=function(a,b){return-1===b.search("://")?"audio"==a?p.audio+b:p.images+b:b},s=function(a){return b.asset(a)||null},t=function(a){return b.audio.supports(q(a))},u=function(a){return-1!=b.image_whitelist.indexOf(q(a))},v=function(a,c){a.onload=f,"webkit"===b.support.prefix&&(a.src=""),a.src=c};for(k in a)for(l in a[k]){if(h=a[k][l],"audio"===k&&o){if("object"==typeof h){var w=[];for(var x in h)i=r(k,h[x]),!s(i)&&t(h[x])&&w.push(i);j=b.audio.add(l,w).obj}else"string"==typeof h&&t(h)&&(i=r(k,h),s(i)||(j=b.audio.add(l,i).obj));j&&j.addEventListener&&j.addEventListener("canplaythrough",f,!1)}else l="sprites"===k?l:h,i=r(k,l),u(l)&&(j=s(i),j||(j=new Image,"sprites"===k&&b.sprite(h.tile,h.tileh,i,h.map,h.paddingX,h.paddingY,h.paddingAroundBorder),b.asset(i,j)),v(j,i));j?j.onerror=g:--n}0===n&&c()},removeAssets:function(a){a="string"==typeof a?JSON.parse(a):a;var c,d,e,f,g=b.paths(),h=function(a,b){return-1===b.search("://")?"audio"==a?g.audio+b:g.images+b:b};for(e in a)for(f in a[e])if(c=a[e][f],"audio"===e)if("object"==typeof c)for(var i in c)d=h(e,c[i]),b.asset(d)&&b.audio.remove(f);else"string"==typeof c&&(d=h(e,c),b.asset(d)&&b.audio.remove(f));else if(f="sprites"===e?f:c,d=h(e,f),b.asset(d)){if("sprites"===e)for(var j in c.map)delete b.components()[j];delete b.assets[d]}}})},{"./core.js":10}],20:[function(a){{var b=a("./core.js");window.document}b.math={abs:function(a){return 0>a?-a:a},amountOf:function(a,b,c){return c>b?(a-b)/(c-b):(a-c)/(b-c)},clamp:function(a,b,c){return a>c?c:b>a?b:a},degToRad:function(a){return a*Math.PI/180},distance:function(a,c,d,e){var f=b.math.squaredDistance(a,c,d,e);return Math.sqrt(parseFloat(f))},lerp:function(a,b,c){return a+(b-a)*c},negate:function(a){return Math.random()=b&&c>=a}},b.math.Vector2D=function(){function a(b,c){if(b instanceof a)this.x=b.x,this.y=b.y;else if(2===arguments.length)this.x=b,this.y=c;else if(arguments.length>0)throw"Unexpected number of arguments for Vector2D()"}return a.prototype.x=0,a.prototype.y=0,a.prototype.add=function(a){return this.x+=a.x,this.y+=a.y,this},a.prototype.angleBetween=function(a){return Math.atan2(this.x*a.y-this.y*a.x,this.x*a.x+this.y*a.y)},a.prototype.angleTo=function(a){return Math.atan2(a.y-this.y,a.x-this.x)},a.prototype.clone=function(){return new a(this)},a.prototype.distance=function(a){return Math.sqrt((a.x-this.x)*(a.x-this.x)+(a.y-this.y)*(a.y-this.y))},a.prototype.distanceSq=function(a){return(a.x-this.x)*(a.x-this.x)+(a.y-this.y)*(a.y-this.y)},a.prototype.divide=function(a){return this.x/=a.x,this.y/=a.y,this},a.prototype.dotProduct=function(a){return this.x*a.x+this.y*a.y},a.prototype.crossProduct=function(a){return this.x*a.y-this.y*a.x},a.prototype.equals=function(b){return b instanceof a&&this.x==b.x&&this.y==b.y},a.prototype.perpendicular=function(b){return b=b||new a,b.setValues(-this.y,this.x)},a.prototype.getNormal=function(b,c){return c=c||new a,c.setValues(b.y-this.y,this.x-b.x).normalize()},a.prototype.isZero=function(){return 0===this.x&&0===this.y},a.prototype.magnitude=function(){return Math.sqrt(this.x*this.x+this.y*this.y)},a.prototype.magnitudeSq=function(){return this.x*this.x+this.y*this.y},a.prototype.multiply=function(a){return this.x*=a.x,this.y*=a.y,this},a.prototype.negate=function(){return this.x=-this.x,this.y=-this.y,this},a.prototype.normalize=function(){var a=Math.sqrt(this.x*this.x+this.y*this.y);return 0===a?(this.x=1,this.y=0):(this.x/=a,this.y/=a),this},a.prototype.scale=function(a,b){return void 0===b&&(b=a),this.x*=a,this.y*=b,this},a.prototype.scaleToMagnitude=function(a){var b=a/this.magnitude();return this.x*=b,this.y*=b,this},a.prototype.setValues=function(b,c){return b instanceof a?(this.x=b.x,this.y=b.y):(this.x=b,this.y=c),this},a.prototype.subtract=function(a){return this.x-=a.x,this.y-=a.y,this},a.prototype.toString=function(){return"Vector2D("+this.x+", "+this.y+")"},a.prototype.translate=function(a,b){return void 0===b&&(b=a),this.x+=a,this.y+=b,this},a.tripleProduct=function(a,c,d,e){e=e||new b.math.Vector2D;var f=a.dotProduct(d),g=c.dotProduct(d);return e.setValues(c.x*f-a.x*g,c.y*f-a.y*g)},a}(),b.math.Matrix2D=function(){return Matrix2D=function(a,b,c,d,e,f){if(a instanceof Matrix2D)this.a=a.a,this.b=a.b,this.c=a.c,this.d=a.d,this.e=a.e,this.f=a.f;else if(6===arguments.length)this.a=a,this.b=b,this.c=c,this.d=d,this.e=e,this.f=f;else if(arguments.length>0)throw"Unexpected number of arguments for Matrix2D()"},Matrix2D.prototype.a=1,Matrix2D.prototype.b=0,Matrix2D.prototype.c=0,Matrix2D.prototype.d=1,Matrix2D.prototype.e=0,Matrix2D.prototype.f=0,Matrix2D.prototype.apply=function(a){var b=a.x;return a.x=b*this.a+a.y*this.c+this.e,a.y=b*this.b+a.y*this.d+this.f,a},Matrix2D.prototype.clone=function(){return new Matrix2D(this)},Matrix2D.prototype.combine=function(a){var b=this.a;return this.a=b*a.a+this.b*a.c,this.b=b*a.b+this.b*a.d,b=this.c,this.c=b*a.a+this.d*a.c,this.d=b*a.b+this.d*a.d,b=this.e,this.e=b*a.a+this.f*a.c+a.e,this.f=b*a.b+this.f*a.d+a.f,this},Matrix2D.prototype.equals=function(a){return a instanceof Matrix2D&&this.a==a.a&&this.b==a.b&&this.c==a.c&&this.d==a.d&&this.e==a.e&&this.f==a.f},Matrix2D.prototype.determinant=function(){return this.a*this.d-this.b*this.c},Matrix2D.prototype.invert=function(){var a=this.determinant();if(0!==a){var b={a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f};this.a=b.d/a,this.b=-b.b/a,this.c=-b.c/a,this.d=b.a/a,this.e=(b.c*b.f-b.e*b.d)/a,this.f=(b.e*b.b-b.a*b.f)/a}return this},Matrix2D.prototype.isIdentity=function(){return 1===this.a&&0===this.b&&0===this.c&&1===this.d&&0===this.e&&0===this.f},Matrix2D.prototype.isInvertible=function(){return 0!==this.determinant()},Matrix2D.prototype.preRotate=function(a){var b=Math.cos(a),c=Math.sin(a),d=this.a;return this.a=b*d-c*this.b,this.b=c*d+b*this.b,d=this.c,this.c=b*d-c*this.d,this.d=c*d+b*this.d,this},Matrix2D.prototype.preScale=function(a,b){return void 0===b&&(b=a),this.a*=a,this.b*=b,this.c*=a,this.d*=b,this},Matrix2D.prototype.preTranslate=function(a,b){return"number"==typeof a?(this.e+=a,this.f+=b):(this.e+=a.x,this.f+=a.y),this},Matrix2D.prototype.rotate=function(a){var b=Math.cos(a),c=Math.sin(a),d=this.a;return this.a=b*d-c*this.b,this.b=c*d+b*this.b,d=this.c,this.c=b*d-c*this.d,this.d=c*d+b*this.d,d=this.e,this.e=b*d-c*this.f,this.f=c*d+b*this.f,this},Matrix2D.prototype.scale=function(a,b){return void 0===b&&(b=a),this.a*=a,this.b*=b,this.c*=a,this.d*=b,this.e*=a,this.f*=b,this},Matrix2D.prototype.setValues=function(a,b,c,d,e,f){return a instanceof Matrix2D?(this.a=a.a,this.b=a.b,this.c=a.c,this.d=a.d,this.e=a.e,this.f=a.f):(this.a=a,this.b=b,this.c=c,this.d=d,this.e=e,this.f=f),this},Matrix2D.prototype.toString=function(){return"Matrix2D(["+this.a+", "+this.c+", "+this.e+"] ["+this.b+", "+this.d+", "+this.f+"] [0, 0, 1])"},Matrix2D.prototype.translate=function(a,b){return"number"==typeof a?(this.e+=this.a*a+this.c*b,this.f+=this.b*a+this.d*b):(this.e+=this.a*a.x+this.c*a.y,this.f+=this.b*a.x+this.d*a.y),this},Matrix2D}()},{"./core.js":10}],21:[function(a){{var b=a("./core.js");window.document}b.c("Model",{init:function(){this.changed=[],this.bind("Change",this._changed_attributes),this.bind("Change",this._changed_triggers)},_changed_triggers:function(a,c){var d;c=b.extend.call({pre:""},c);for(d in a)this.trigger("Change["+c.pre+d+"]",a[d]),"Object"===a[d].constructor.name&&this._changed_triggers(a[d],{pre:c.pre+d+"."})},_changed_attributes:function(a){var b;for(b in a)this.changed.push(b);return this},is_dirty:function(a){return 0===arguments.length?!!this.changed.length:this.changed.indexOf(a)>-1}})},{"./core.js":10}],22:[function(a){var b=a("./core.js"),c=window.document;b.c("Particles",{init:function(){this._Particles=b.clone(this._Particles),this._Particles.parentEntity=this},particles:function(a){if(!b.support.canvas||b.deactivateParticles)return this;var d,e,f,g,h;d=c.createElement("canvas"),d.width=b.viewport.width,d.height=b.viewport.height,d.style.position="absolute",d.style.left="0px",d.style.top="0px",b.stage.elem.appendChild(d),e=d.getContext("2d"),this._Particles.init(a),this.bind("Remove",function(){b.stage.elem.removeChild(d)}).bind("RemoveComponent",function(a){"particles"===a&&b.stage.elem.removeChild(d)}),f=this.x+b.viewport.x,g=this.y+b.viewport.y,this._Particles.position=this._Particles.vectorHelpers.create(f,g);var i={x:b.viewport.x,y:b.viewport.y};return this.bind("EnterFrame",function(){f=this.x+b.viewport.x,g=this.y+b.viewport.y,this._Particles.viewportDelta={x:b.viewport.x-i.x,y:b.viewport.y-i.y},i={x:b.viewport.x,y:b.viewport.y},this._Particles.position=this._Particles.vectorHelpers.create(f,g),"function"==typeof b.DrawManager.boundingRect?(h=b.DrawManager.boundingRect(this._Particles.register),h&&e.clearRect(h._x,h._y,h._w,h._h)):e.clearRect(0,0,b.viewport.width,b.viewport.height),this._Particles.update(),this._Particles.render(e)}),this},_Particles:{presets:{maxParticles:150,size:18,sizeRandom:4,speed:1,speedRandom:1.2,lifeSpan:29,lifeSpanRandom:7,angle:65,angleRandom:34,startColour:[255,131,0,1],startColourRandom:[48,50,45,0],endColour:[245,35,0,0],endColourRandom:[60,60,60,0],sharpness:20,sharpnessRandom:10,spread:10,duration:-1,fastMode:!1,gravity:{x:0,y:.1},jitter:0,particles:[],active:!0,particleCount:0,elapsedFrames:0,emissionRate:0,emitCounter:0,particleIndex:0},init:function(a){this.position=this.vectorHelpers.create(0,0),"undefined"==typeof a&&(a={});for(var b in this.presets)this[b]="undefined"!=typeof a[b]?a[b]:this.presets[b];this.emissionRate=this.maxParticles/this.lifeSpan,this.positionRandom=this.vectorHelpers.create(this.spread,this.spread)},addParticle:function(){if(this.particleCount==this.maxParticles)return!1;var a=new this.particle(this.vectorHelpers);return this.initParticle(a),this.particles[this.particleCount]=a,this.particleCount++,!0},RANDM1TO1:function(){return 2*Math.random()-1},initParticle:function(a){a.position.x=this.position.x+this.positionRandom.x*this.RANDM1TO1(),a.position.y=this.position.y+this.positionRandom.y*this.RANDM1TO1();var b=(this.angle+this.angleRandom*this.RANDM1TO1())*(Math.PI/180),c=this.vectorHelpers.create(Math.sin(b),-Math.cos(b)),d=this.speed+this.speedRandom*this.RANDM1TO1();a.direction=this.vectorHelpers.multiply(c,d),a.size=this.size+this.sizeRandom*this.RANDM1TO1(),a.size=a.size<0?0:~~a.size,a.timeToLive=this.lifeSpan+this.lifeSpanRandom*this.RANDM1TO1(),a.sharpness=this.sharpness+this.sharpnessRandom*this.RANDM1TO1(),a.sharpness=a.sharpness>100?100:a.sharpness<0?0:a.sharpness,a.sizeSmall=~~(a.size/200*a.sharpness);var e=[this.startColour[0]+this.startColourRandom[0]*this.RANDM1TO1(),this.startColour[1]+this.startColourRandom[1]*this.RANDM1TO1(),this.startColour[2]+this.startColourRandom[2]*this.RANDM1TO1(),this.startColour[3]+this.startColourRandom[3]*this.RANDM1TO1()],f=[this.endColour[0]+this.endColourRandom[0]*this.RANDM1TO1(),this.endColour[1]+this.endColourRandom[1]*this.RANDM1TO1(),this.endColour[2]+this.endColourRandom[2]*this.RANDM1TO1(),this.endColour[3]+this.endColourRandom[3]*this.RANDM1TO1()];a.colour=e,a.deltaColour[0]=(f[0]-e[0])/a.timeToLive,a.deltaColour[1]=(f[1]-e[1])/a.timeToLive,a.deltaColour[2]=(f[2]-e[2])/a.timeToLive,a.deltaColour[3]=(f[3]-e[3])/a.timeToLive},update:function(){if(this.active&&this.emissionRate>0){var a=1/this.emissionRate;for(this.emitCounter++;this.particleCounta;)this.addParticle(),this.emitCounter-=a;this.elapsedFrames++,-1!=this.duration&&this.duration0){c.direction=this.vectorHelpers.add(c.direction,this.gravity),c.position=this.vectorHelpers.add(c.position,c.direction),c.position=this.vectorHelpers.add(c.position,this.viewportDelta),this.jitter&&(c.position.x+=this.jitter*this.RANDM1TO1(),c.position.y+=this.jitter*this.RANDM1TO1()),c.timeToLive--;var d=c.colour[0]+=c.deltaColour[0],e=c.colour[1]+=c.deltaColour[1],f=c.colour[2]+=c.deltaColour[2],g=c.colour[3]+=c.deltaColour[3];b=[],b.push("rgba("+(d>255?255:0>d?0:~~d)),b.push(e>255?255:0>e?0:~~e),b.push(f>255?255:0>f?0:~~f),b.push((g>1?1:0>g?0:g.toFixed(2))+")"),c.drawColour=b.join(","),this.fastMode||(b[3]="0)",c.drawColourEnd=b.join(",")),this.particleIndex++}else this.particleIndex!=this.particleCount-1&&(this.particles[this.particleIndex]=this.particles[this.particleCount-1]),this.particleCount--;var h={};h._x=~~c.position.x,h._y=~~c.position.y,h._w=c.size,h._h=c.size,this.register.push(h)}},stop:function(){this.active=!1,this.elapsedFrames=0,this.emitCounter=0,this.parentEntity.trigger("ParticleEnd")},render:function(a){for(var c=0,d=this.particleCount;d>c;c++){var e=this.particles[c],f=e.size,g=f>>1;if(!(e.position.x+f<0||e.position.y+f<0||e.position.x-f>b.viewport.width||e.position.y-f>b.viewport.height)){var h=~~e.position.x,i=~~e.position.y;if(this.fastMode)a.fillStyle=e.drawColour;else{var j=a.createRadialGradient(h+g,i+g,e.sizeSmall,h+g,i+g,g);j.addColorStop(0,e.drawColour),j.addColorStop(.9,e.drawColourEnd),a.fillStyle=j}a.fillRect(h,i,f,f)}}},particle:function(a){this.position=a.create(0,0),this.direction=a.create(0,0),this.size=0,this.sizeSmall=0,this.timeToLive=0,this.colour=[],this.drawColour="",this.deltaColour=[],this.sharpness=0},vectorHelpers:{create:function(a,b){return{x:a,y:b}},multiply:function(a,b){return a.x*=b,a.y*=b,a},add:function(a,b){return a.x+=b.x,a.y+=b.y,a}}}})},{"./core.js":10}],23:[function(a){{var b=a("./core.js");window.document}b.extend({_scenes:{},_current:null,scene:function(a,c,d){return 1===arguments.length||"function"!=typeof arguments[1]?void b.enterScene(a,arguments[1]):void b.defineScene(a,c,d)},defineScene:function(a,b,c){if("function"!=typeof b)throw"Init function is the wrong type.";this._scenes[a]={},this._scenes[a].initialize=b,"undefined"!=typeof c&&(this._scenes[a].uninitialize=c)},enterScene:function(a,c){if("function"==typeof c)throw"Scene data cannot be a function";b.trigger("SceneDestroy",{newScene:a}),b.viewport.reset(),b("2D").each(function(){this.has("Persist")||this.destroy()}),null!==this._current&&"uninitialize"in this._scenes[this._current]&&this._scenes[this._current].uninitialize.call(this);var d=this._current;this._current=a,b.trigger("SceneChange",{oldScene:d,newScene:a}),this._scenes[a].initialize.call(this,c)}})},{"./core.js":10}],24:[function(a){var b=a("./core.js"),d=window.document;b.extend({audio:{sounds:{},supported:null,codecs:{ogg:'audio/ogg; codecs="vorbis"',wav:'audio/wav; codecs="1"',webma:'audio/webm; codecs="vorbis"',mp3:'audio/mpeg; codecs="mp3"',m4a:'audio/mp4; codecs="mp4a.40.2"'},volume:1,muted:!1,paused:!1,playCheck:null,_canPlay:function(){if(this.supported={},b.support.audio){var a,c=this.audioElement();for(var d in this.codecs)a=c.canPlayType(this.codecs[d]),this.supported[d]=""!==a&&"no"!==a?!0:!1}},supports:function(a){return null===this.supported&&this._canPlay(),this.supported[a]?!0:!1},audioElement:function(){return"undefined"!=typeof Audio?new Audio(""):d.createElement("audio")},create:function(a,c){var d=c.substr(c.lastIndexOf(".")+1).toLowerCase();if(!this.supports(d))return!1;var e=this.audioElement();return e.id=a,e.preload="auto",e.volume=b.audio.volume,e.src=c,b.asset(c,e),this.sounds[a]={obj:e,played:0,volume:b.audio.volume},this.sounds[a]},add:function(a,c){if(b.support.audio){var d,e;if(1===arguments.length&&"object"==typeof a)for(var f in a)for(d in a[f])if(e=b.audio.create(f,a[f][d]))break;if("string"==typeof a&&("string"==typeof c&&(e=b.audio.create(a,c)),"object"==typeof c))for(d in c)if(e=b.audio.create(a,c[d]))break;return e}},play:function(a,c,d){if(0!==c&&b.support.audio&&this.sounds[a]){var e=this.sounds[a],f=this.getOpenChannel();if(!f)return null;f.id=a,f.repeat=c;var g=f.obj;return f.volume=e.volume=e.obj.volume=d||b.audio.volume,g.volume=e.volume,g.src=e.obj.src,this.muted&&(g.volume=0),g.play(),e.played++,f.onEnd=function(){e.played=0)for(;d+f>h;h++)g.frames.push([h,y]);else for(;h>d+f;h--)g.frames.push([h,y]);else{if(3!==arguments.length||"object"!=typeof d)throw"Urecognized arguments. Please see the documentation for 'reel(...)'.";g.frames=d}return this._reels[a]=g,this},animate:function(a,b){"string"==typeof a&&this.reel(a);var c=this._currentReel;if("undefined"==typeof c||null===c)throw"No reel is specified, and there is no currently active reel.";return this.pauseAnimation(),"undefined"==typeof b&&(b="number"==typeof a?a:1),c.easing.reset(),this.loops(b),this._setFrame(0),this.bind("EnterFrame",this._animationTick),this._isPlaying=!0,this.trigger("StartAnimation",c),this},resumeAnimation:function(){return this._isPlaying===!1&&null!==this._currentReel&&(this.bind("EnterFrame",this._animationTick),this._isPlaying=!0,this._currentReel.easing.resume(),this.trigger("StartAnimation",this._currentReel)),this},pauseAnimation:function(){return this._isPlaying===!0&&(this.unbind("EnterFrame",this._animationTick),this._isPlaying=!1,this._reels[this._currentReelId].easing.pause()),this},resetAnimation:function(){var a=this._currentReel;if(null===a)throw"No active reel to reset.";return this.reelPosition(0),a.easing.repeat(a.defaultLoops),this},loops:function(a){return 0===arguments.length?null!==this._currentReel?this._currentReel.easing.loops:0:(null!==this._currentReel&&(0>a&&(a=1/0),this._currentReel.easing.repeat(a),this._currentReel.defaultLoops=a),this)},reelPosition:function(a){if(null===this._currentReel)throw"No active reel.";if(0===arguments.length)return this._currentReel.currentFrame;var b,c=this._currentReel.frames.length;if("end"===a&&(a=c-1),1>a&&a>0)b=a,a=Math.floor(c*b);else{if(a!==Math.floor(a))throw"Position "+a+" is invalid.";0>a&&(a=c-1+a),b=a/c}return a=Math.min(a,c-1),a=Math.max(a,0),this._setProgress(b),this._setFrame(a),this},_animationTick:function(a){var b=this._reels[this._currentReelId];b.easing.tick(a.dt*this.animationSpeed);var c=b.easing.value(),d=Math.min(Math.floor(b.frames.length*c),b.frames.length-1);this._setFrame(d),b.easing.complete===!0&&(this.pauseAnimation(),this.trigger("AnimationEnd",this._currentReel))},_setFrame:function(a){var b=this._currentReel;a!==b.currentFrame&&(b.currentFrame=a,this._updateSprite(),this.trigger("FrameChange",b))},_updateSprite:function(){var a=this._currentReel,b=a.frames[a.currentFrame];this.sprite(b[0],b[1])},_setProgress:function(a,b){this._currentReel.easing.setProgress(a,b)},isPlaying:function(a){return this._isPlaying?a?this._currentReelId===a:!!this._currentReelId:!1},getReel:function(a){if(0===arguments.length){if(!this._currentReelId)return null;a=this._currentReelId}return this._reels[a]}})},{"./animation.js":5,"./core.js":10}],26:[function(a){{var b=a("./core.js");window.document}b.extend({sprite:function(a,c,d,e,f,g,h){var i,j,k;"string"==typeof a&&(g=f,f=e,e=c,d=a,a=1,c=1),"string"==typeof c&&(g=f,f=e,e=d,d=c,c=a),!g&&f&&(g=f),f=parseInt(f||0,10),g=parseInt(g||0,10);var l=function(){this.ready=!0,this.trigger("Invalidate")};k=b.asset(d),k||(k=new Image,k.src=d,b.asset(d,k),k.onload=function(){for(var a in e)b(a).each(l)});var m=function(){this.requires("2D, Sprite"),this.__trim=[0,0,0,0],this.__image=d,this.__coord=[this.__coord[0],this.__coord[1],this.__coord[2],this.__coord[3]],this.__tile=a,this.__tileh=c,this.__padding=[f,g],this.__padBorder=h,this.sprite(this.__coord[0],this.__coord[1],this.__coord[2],this.__coord[3]),this.img=k,this.img.complete&&this.img.width>0&&(this.ready=!0,this.trigger("Invalidate")),this.w=this.__coord[2],this.h=this.__coord[3]};for(i in e)e.hasOwnProperty(i)&&(j=e[i],b.c(i,{ready:!1,__coord:[j[0],j[1],j[2]||1,j[3]||1],init:m}));return this}}),b.c("Sprite",{__image:"",__tile:0,__tileh:0,__padding:null,__trim:null,img:null,ready:!1,init:function(){this.__trim=[0,0,0,0];var a=function(a){var b=a.co,c=a.pos,d=a.ctx;if("canvas"===a.type)d.drawImage(this.img,b.x,b.y,b.w,b.h,c._x,c._y,c._w,c._h);else if("DOM"===a.type){var e=this._h/b.h,f=this._w/b.w,g=this._element.style;g.background=g.backgroundColor+" url('"+this.__image+"') no-repeat",g.backgroundPosition="-"+b.x*f+"px -"+b.y*e+"px",(1!=e||1!=f)&&(g.backgroundSize=this.img.width*f+"px "+this.img.height*e+"px")}};this.bind("Draw",a).bind("RemoveComponent",function(b){"Sprite"===b&&this.unbind("Draw",a)})},sprite:function(a,b,c,d){return this.__coord=this.__coord||[0,0,0,0],this.__coord[0]=a*(this.__tile+this.__padding[0])+(this.__padBorder?this.__padding[0]:0)+this.__trim[0],this.__coord[1]=b*(this.__tileh+this.__padding[1])+(this.__padBorder?this.__padding[1]:0)+this.__trim[1],"undefined"!=typeof c&&"undefined"!=typeof d&&(this.__coord[2]=this.__trim[2]||c*this.__tile||this.__tile,this.__coord[3]=this.__trim[3]||d*this.__tileh||this.__tileh),this.trigger("Invalidate"),this},crop:function(a,b,c,d){var e=this._mbr||this.pos();return this.__trim=[],this.__trim[0]=a,this.__trim[1]=b,this.__trim[2]=c,this.__trim[3]=d,this.__coord[0]+=a,this.__coord[1]+=b,this.__coord[2]=c,this.__coord[3]=d,this._w=c,this._h=d,this.trigger("Invalidate",e),this}})},{"./core.js":10}],27:[function(a){{var b=a("./core.js");window.document}b.storage=function(a,b){var c=window.localStorage,d=b;if(!c)return!1;if(1===arguments.length)try{return JSON.parse(c.getItem(a))}catch(e){return c.getItem(a)}else"object"==typeof b&&(d=JSON.stringify(b)),c.setItem(a,d)},b.storage.remove=function(a){window.localStorage.removeItem(a)}},{"./core.js":10}],28:[function(a){{var b=a("./core.js");window.document}b.c("Text",{_text:"",defaultSize:"10px",defaultFamily:"sans-serif",defaultVariant:"normal",defaultLineHeight:"normal",ready:!0,init:function(){this.requires("2D"),this._textFont={type:"",weight:"",size:this.defaultSize,lineHeight:this.defaultLineHeight,family:this.defaultFamily,variant:this.defaultVariant},this.bind("Draw",function(a){var b=this._fontString();if("DOM"===a.type){var c=this._element,d=c.style;d.color=this._textColor,d.font=b,c.innerHTML=this._text}else if("canvas"===a.type){var e=a.ctx;e.save(),e.textBaseline="top",e.fillStyle=this._textColor||"rgb(0,0,0)",e.font=b,e.fillText(this._text,a.pos._x,a.pos._y),e.restore()}})},_getFontHeight:function(){var a=/([a-zA-Z]+)\b/,b={px:1,pt:4/3,pc:16,cm:96/2.54,mm:96/25.4,"in":96,em:void 0,ex:void 0};return function(c){var d=parseFloat(c),e=a.exec(c),f=e?e[1]:"px";return Math.ceil(void 0!==b[f]?d*b[f]:d)}}(),text:function(a){return"undefined"==typeof a||null===a?this._text:(this._text="function"==typeof a?a.call(this):a,this.has("Canvas")&&this._resizeForCanvas(),this.trigger("Invalidate"),this)},_resizeForCanvas:function(){var a=b.canvas.context;a.font=this._fontString(),this.w=a.measureText(this._text).width;var c=this._textFont.size||this.defaultSize;this.h=1.1*this._getFontHeight(c)},_fontString:function(){return this._textFont.type+" "+this._textFont.variant+" "+this._textFont.weight+" "+this._textFont.size+" / "+this._textFont.lineHeight+" "+this._textFont.family},textColor:function(a){return b.assignColor(a,this),this._textColor="rgba("+this._red+", "+this._green+", "+this._blue+", "+this._strength+")",this.trigger("Invalidate"),this},textFont:function(a,b){if(1===arguments.length){if("string"==typeof a)return this._textFont[a];if("object"==typeof a)for(var c in a)this._textFont[c]="family"==c?"'"+a[c]+"'":a[c]}else this._textFont[a]=b;return this.has("Canvas")&&this._resizeForCanvas(),this.trigger("Invalidate"),this},unselectable:function(){return this.has("DOM")&&(this.css({"-webkit-touch-callout":"none","-webkit-user-select":"none","-khtml-user-select":"none","-moz-user-select":"none","-ms-user-select":"none","user-select":"none",cursor:"default"}),this.trigger("Invalidate")),this}})},{"./core.js":10}],29:[function(a){{var b=a("./core.js");window.document}b.c("Delay",{init:function(){this._delays=[],this.bind("EnterFrame",function(a){for(var b=this._delays.length;--b>=0;){var c=this._delays[b];if(c===!1)this._delays.splice(b,1);else{for(c.accumulator+=a.dt;c.accumulator>=c.delay&&c.repeat>=0;)c.accumulator-=c.delay,c.repeat--,c.callback.call(this);c.repeat<0&&(this._delays.splice(b,1),"function"==typeof c.callbackOff&&c.callbackOff.call(this))}}})},delay:function(a,b,c,d){return this._delays.push({accumulator:0,callback:a,callbackOff:d,delay:b,repeat:(0>c?1/0:c)||0}),this},cancelDelay:function(a){for(var b=this._delays.length;--b>=0;){var c=this._delays[b];c&&c.callback==a&&(this._delays[b]=!1)}return this}})},{"./core.js":10}],30:[function(a,b){b.exports="0.6.3-beta"},{}],31:[function(a){var b=a("./core.js"),c=window.document;b.extend({viewport:{clampToEntities:!0,_width:0,_height:0,_x:0,_y:0,_scale:1,bounds:null,scroll:function(a,c){this[a]=c,b.trigger("ViewportScroll"),b.trigger("InvalidateViewport")},rect_object:{_x:0,_y:0,_w:0,_h:0},rect:function(){return this.rect_object._x=-this._x,this.rect_object._y=-this._y,this.rect_object._w=this._width/this._scale,this.rect_object._h=this._height/this._scale,this.rect_object},pan:function(){function a(a){h.tick(a.dt);var i=h.value();b.viewport.x=(1-i)*f+i*d,b.viewport.y=(1-i)*g+i*e,b.viewport._clamp(),h.complete&&(c(),b.trigger("CameraAnimationDone"))}function c(){b.unbind("EnterFrame",a)}var d,e,f,g,h;return b.bind("StopCamera",c),function(c,i,j){b.trigger("StopCamera"),"reset"!=c&&(f=b.viewport._x,g=b.viewport._y,d=f-c,e=g-i,h=new b.easing(j),b.uniqueBind("EnterFrame",a))}}(),follow:function(){function a(){b.viewport.scroll("_x",-(this.x+this.w/2-b.viewport.width/2-e)),b.viewport.scroll("_y",-(this.y+this.h/2-b.viewport.height/2-f)),b.viewport._clamp()}function c(){d&&d.unbind("Move",a)}var d,e,f;return b.bind("StopCamera",c),function(c,g,h){c&&c.has("2D")&&(b.trigger("StopCamera"),d=c,e="undefined"!=typeof g?g:0,f="undefined"!=typeof h?h:0,c.bind("Move",a),a.call(c))}}(),centerOn:function(a,c){var d=a.x+b.viewport.x,e=a.y+b.viewport.y,f=a.w/2,g=a.h/2,h=b.viewport.width/2,i=b.viewport.height/2,j=d+f-h,k=e+g-i;b.viewport.pan(j,k,c)},zoom:function(){function a(){b.unbind("EnterFrame",c)}function c(c){var e,l;k.tick(c.dt),e=Math.pow(f,k.value()),l=1===f?k.value():(1/e-1)/(1/f-1),b.viewport.scale(e*d),b.viewport.scroll("_x",g*(1-l)+h*l),b.viewport.scroll("_y",i*(1-l)+j*l),b.viewport._clamp(),k.complete&&(a(),b.trigger("CameraAnimationDone"))}b.bind("StopCamera",a);var d,e,f,g,h,i,j,k;return function(a,l,m,n){return a?(arguments.length<=2&&(n=l,l=b.viewport.x-b.viewport.width,m=b.viewport.y-b.viewport.height),b.trigger("StopCamera"),d=b.viewport._scale,f=a,e=d*f,g=b.viewport.x,i=b.viewport.y,h=-(l-b.viewport.width/(2*e)),j=-(m-b.viewport.height/(2*e)),k=new b.easing(n),void b.uniqueBind("EnterFrame",c)):void b.viewport.scale(1)}}(),scale:function(){return function(a){this._scale=a?a:1,b.trigger("InvalidateViewport"),b.trigger("ViewportScale")}}(),mouselook:function(){var a=!1,c=!1,d={};return old={},function(e,f){if("boolean"==typeof e)return a=e,void(a?b.mouseObjs++:b.mouseObjs=Math.max(0,b.mouseObjs-1));if(a)switch(e){case"move":case"drag":if(!c)return;diff={x:f.clientX-d.x,y:f.clientY-d.y},d.x=f.clientX,d.y=f.clientY,b.viewport.x+=diff.x,b.viewport.y+=diff.y,b.viewport._clamp();break;case"start":b.trigger("StopCamera"),d.x=f.clientX,d.y=f.clientY,c=!0;break;case"stop":c=!1}}}(),_clamp:function(){if(this.clampToEntities){var a=this.bounds||b.map.boundaries();a.max.x*=this._scale,a.min.x*=this._scale,a.max.y*=this._scale,a.min.y*=this._scale,a.max.x-a.min.x>b.viewport.width?b.viewport.x<-a.max.x+b.viewport.width?b.viewport.x=-a.max.x+b.viewport.width:b.viewport.x>-a.min.x&&(b.viewport.x=-a.min.x):b.viewport.x=-1*(a.min.x+(a.max.x-a.min.x)/2-b.viewport.width/2),a.max.y-a.min.y>b.viewport.height?b.viewport.y<-a.max.y+b.viewport.height?b.viewport.y=-a.max.y+b.viewport.height:b.viewport.y>-a.min.y&&(b.viewport.y=-a.min.y):b.viewport.y=-1*(a.min.y+(a.max.y-a.min.y)/2-b.viewport.height/2)}},init:function(a,d,e){b.DOM.window.init(),this._defineViewportProperties(),this._width=a?a:b.DOM.window.width,this._height=d?d:b.DOM.window.height,"undefined"==typeof e&&(e="cr-stage");var f;if("string"==typeof e)f=c.getElementById(e);else{if(!("undefined"!=typeof HTMLElement?e instanceof HTMLElement:e instanceof Element))throw new TypeError("stage_elem must be a string or an HTMLElement"); +f=e}b.stage={x:0,y:0,fullscreen:!1,elem:f?f:c.createElement("div"),inner:c.createElement("div")},a||d||(c.body.style.overflow="hidden",b.stage.fullscreen=!0),b.addEvent(this,window,"resize",b.viewport.reload),b.addEvent(this,window,"blur",function(){b.settings.get("autoPause")&&(b._paused||b.pause())}),b.addEvent(this,window,"focus",function(){b._paused&&b.settings.get("autoPause")&&b.pause()}),b.settings.register("stageSelectable",function(a){b.stage.elem.onselectstart=a?function(){return!0}:function(){return!1}}),b.settings.modify("stageSelectable",!1),b.settings.register("stageContextMenu",function(a){b.stage.elem.oncontextmenu=a?function(){return!0}:function(){return!1}}),b.settings.modify("stageContextMenu",!1),b.settings.register("autoPause",function(){}),b.settings.modify("autoPause",!1),f||(c.body.appendChild(b.stage.elem),b.stage.elem.id=e);var g,h=b.stage.elem.style;if(b.stage.elem.appendChild(b.stage.inner),b.stage.inner.style.position="absolute",b.stage.inner.style.zIndex="1",b.stage.inner.style.transformStyle="preserve-3d",h.width=this.width+"px",h.height=this.height+"px",h.overflow="hidden",b.bind("ViewportResize",function(){b.trigger("InvalidateViewport")}),b.mobile){void 0!==typeof h.webkitTapHighlightColor&&(h.webkitTapHighlightColor="rgba(0,0,0,0)");var i=c.createElement("meta"),j=c.getElementsByTagName("head")[0];i=c.createElement("meta"),i.setAttribute("name","apple-mobile-web-app-capable"),i.setAttribute("content","yes"),j.appendChild(i),b.addEvent(this,b.stage.elem,"touchmove",function(a){a.preventDefault()})}else h.position="relative",g=b.DOM.inner(b.stage.elem),b.stage.x=g.x,b.stage.y=g.y},_defineViewportProperties:function(){Object.defineProperty(this,"x",{set:function(a){this.scroll("_x",a)},get:function(){return this._x},configurable:!0}),Object.defineProperty(this,"y",{set:function(a){this.scroll("_y",a)},get:function(){return this._y},configurable:!0}),Object.defineProperty(this,"width",{set:function(a){this._width=a,b.trigger("ViewportResize")},get:function(){return this._width},configurable:!0}),Object.defineProperty(this,"height",{set:function(a){this._height=a,b.trigger("ViewportResize")},get:function(){return this._height},configurable:!0})},reload:function(){b.DOM.window.init();var a,c=b.DOM.window.width,d=b.DOM.window.height;b.stage.fullscreen&&(this._width=c,this._height=d,b.trigger("ViewportResize")),a=b.DOM.inner(b.stage.elem),b.stage.x=a.x,b.stage.y=a.y},reset:function(){b.viewport.mouselook("stop"),b.trigger("StopCamera"),b.viewport.scale(1)}}})},{"./core.js":10}]},{},[11]); \ No newline at end of file diff --git a/dist/crafty.js b/dist/crafty.js index 173674b8..684d0a83 100644 --- a/dist/crafty.js +++ b/dist/crafty.js @@ -1,24 +1,24 @@ /** - * crafty 0.6.0 + * crafty 0.6.3-beta * http://craftyjs.com/ * - * Copyright 2013, Louis Stowasser + * Copyright 2014, Louis Stowasser * Dual licensed under the MIT or GPL licenses. */ -;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) + return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number; + return number.toString(); + } +}); /**@ * #2D * @category 2D * Component for any entity that has a position on the stage. * @trigger Move - when the entity has moved - { _x:Number, _y:Number, _w:Number, _h:Number } - Old position - * @trigger Change - when the entity has moved - { _x:Number, _y:Number, _w:Number, _h:Number } - Old position - * @trigger Rotate - when the entity is rotated - { cos:Number, sin:Number, deg:Number, rad:Number, o: {x:Number, y:Number}, matrix: {M11, M12, M21, M22} } + * @trigger Invalidate - when the entity needs to be redrawn + * @trigger Rotate - when the entity is rotated - { cos:Number, sin:Number, deg:Number, rad:Number, o: {x:Number, y:Number}} */ Crafty.c("2D", { /**@ @@ -187,66 +195,9 @@ Crafty.c("2D", { _parent: null, _changed: false, - _defineGetterSetter_setter: function () { - //create getters and setters using __defineSetter__ and __defineGetter__ - this.__defineSetter__('x', function (v) { - this._attr('_x', v); - }); - this.__defineSetter__('y', function (v) { - this._attr('_y', v); - }); - this.__defineSetter__('w', function (v) { - this._attr('_w', v); - }); - this.__defineSetter__('h', function (v) { - this._attr('_h', v); - }); - this.__defineSetter__('z', function (v) { - this._attr('_z', v); - }); - this.__defineSetter__('rotation', function (v) { - this._attr('_rotation', v); - }); - this.__defineSetter__('alpha', function (v) { - this._attr('_alpha', v); - }); - this.__defineSetter__('visible', function (v) { - this._attr('_visible', v); - }); - - this.__defineGetter__('x', function () { - return this._x; - }); - this.__defineGetter__('y', function () { - return this._y; - }); - this.__defineGetter__('w', function () { - return this._w; - }); - this.__defineGetter__('h', function () { - return this._h; - }); - this.__defineGetter__('z', function () { - return this._z; - }); - this.__defineGetter__('rotation', function () { - return this._rotation; - }); - this.__defineGetter__('alpha', function () { - return this._alpha; - }); - this.__defineGetter__('visible', function () { - return this._visible; - }); - this.__defineGetter__('parent', function () { - return this._parent; - }); - this.__defineGetter__('numChildren', function () { - return this._children.length; - }); - }, + - _defineGetterSetter_defineProperty: function () { + _define2DProperties: function () { Object.defineProperty(this, 'x', { set: function (v) { this._attr('_x', v); @@ -254,9 +205,12 @@ Crafty.c("2D", { get: function () { return this._x; }, - configurable: true + configurable: true, + enumerable: true }); + Object.defineProperty(this, '_x', {enumerable:false}); + Object.defineProperty(this, 'y', { set: function (v) { this._attr('_y', v); @@ -264,8 +218,10 @@ Crafty.c("2D", { get: function () { return this._y; }, - configurable: true + configurable: true, + enumerable: true }); + Object.defineProperty(this, '_y', {enumerable:false}); Object.defineProperty(this, 'w', { set: function (v) { @@ -274,8 +230,10 @@ Crafty.c("2D", { get: function () { return this._w; }, - configurable: true + configurable: true, + enumerable: true }); + Object.defineProperty(this, '_w', {enumerable:false}); Object.defineProperty(this, 'h', { set: function (v) { @@ -284,8 +242,10 @@ Crafty.c("2D", { get: function () { return this._h; }, - configurable: true + configurable: true, + enumerable: true }); + Object.defineProperty(this, '_h', {enumerable:false}); Object.defineProperty(this, 'z', { set: function (v) { @@ -294,8 +254,10 @@ Crafty.c("2D", { get: function () { return this._z; }, - configurable: true + configurable: true, + enumerable: true }); + Object.defineProperty(this, '_z', {enumerable:false}); Object.defineProperty(this, 'rotation', { set: function (v) { @@ -304,8 +266,10 @@ Crafty.c("2D", { get: function () { return this._rotation; }, - configurable: true + configurable: true, + enumerable: true }); + Object.defineProperty(this, '_rotation', {enumerable:false}); Object.defineProperty(this, 'alpha', { set: function (v) { @@ -314,8 +278,10 @@ Crafty.c("2D", { get: function () { return this._alpha; }, - configurable: true + configurable: true, + enumerable: true }); + Object.defineProperty(this, '_alpha', {enumerable:false}); Object.defineProperty(this, 'visible', { set: function (v) { @@ -324,83 +290,10 @@ Crafty.c("2D", { get: function () { return this._visible; }, - configurable: true - }); - }, - - _defineGetterSetter_fallback: function () { - //set the public properties to the current private properties - this.x = this._x; - this.y = this._y; - this.w = this._w; - this.h = this._h; - this.z = this._z; - this.rotation = this._rotation; - this.alpha = this._alpha; - this.visible = this._visible; - - //on every frame check for a difference in any property - this.bind("EnterFrame", function () { - //if there are differences between the public and private properties - if (this.x !== this._x || this.y !== this._y || - this.w !== this._w || this.h !== this._h || - this.z !== this._z || this.rotation !== this._rotation || - this.alpha !== this._alpha || this.visible !== this._visible) { - - //save the old positions - var old = Crafty._rectPool.copy(this); - - //if rotation has changed, use the private rotate method - if (this.rotation !== this._rotation) { - this._rotate(this.rotation); - } else { - //update the MBR - var mbr = this._mbr, - moved = false; - // If the browser doesn't have getters or setters, - // {x, y, w, h, z} and {_x, _y, _w, _h, _z} may be out of sync, - // in which case t checks if they are different on tick and executes the Change event. - if (mbr) { //check each value to see which has changed - if (this.x !== this._x) { - mbr._x -= this.x - this._x; - moved = true; - } else if (this.y !== this._y) { - mbr._y -= this.y - this._y; - moved = true; - } else if (this.w !== this._w) { - mbr._w -= this.w - this._w; - moved = true; - } else if (this.h !== this._h) { - mbr._h -= this.h - this._h; - moved = true; - } else if (this.z !== this._z) { - mbr._z -= this.z - this._z; - moved = true; - } - } - - //if the moved flag is true, trigger a move - if (moved) this.trigger("Move", old); - } - - //set the public properties to the private properties - this._x = this.x; - this._y = this.y; - this._w = this.w; - this._h = this.h; - this._z = this.z; - this._rotation = this.rotation; - this._alpha = this.alpha; - this._visible = this.visible; - - //trigger the changes - this.trigger("Change", old); - //without this entities weren't added correctly to Crafty.map.map in IE8. - //not entirely sure this is the best way to fix it though - this.trigger("Move", old); - Crafty._rectPool.recycle(old); - } + configurable: true, + enumerable: true }); + Object.defineProperty(this, '_visible', {enumerable:false}); }, init: function () { @@ -409,28 +302,27 @@ Crafty.c("2D", { x: 0, y: 0 }; + + // offsets for the basic bounding box + this._bx1 = 0; + this._bx2 = 0; + this._by1 = 0; + this._by2 = 0; + this._children = []; - if (Crafty.support.setter) { - this._defineGetterSetter_setter(); - } else if (Crafty.support.defineProperty) { - //IE9 supports Object.defineProperty - this._defineGetterSetter_defineProperty(); - } else { - /* - If no setters and getters are supported (e.g. IE8) supports, - check on every frame for a difference between this._(x|y|w|h|z...) - and this.(x|y|w|h|z) and update accordingly. - */ - this._defineGetterSetter_fallback(); - } + + + this._define2DProperties(); + //insert self into the HashMap this._entry = Crafty.map.insert(this); //when object changes, update HashMap this.bind("Move", function (e) { - var area = this._mbr || this; + // Choose the largest bounding region that exists + var area = this._cbr || this._mbr || this; this._entry.update(area); // Move children (if any) by the same amount if (this._children.length > 0) { @@ -439,7 +331,8 @@ Crafty.c("2D", { }); this.bind("Rotate", function (e) { - var old = this._mbr || this; + // Choose the largest bounding region that exists + var old = this._cbr || this._mbr || this; this._entry.update(old); // Rotate children (if any) by the same amount if (this._children.length > 0) { @@ -475,34 +368,69 @@ Crafty.c("2D", { }, + /**@ + * #.offsetBoundary + * @comp 2D + * Extends the MBR of the entity by a specified amount. + * + * @trigger BoundaryOffset - when the MBR offset changes + * @sign public this .offsetBoundary(Number dx1, Number dy1, Number dx2, Number dy2) + * @param dx1 - Extends the MBR to the left by this amount + * @param dy1 - Extends the MBR upward by this amount + * @param dx2 - Extends the MBR to the right by this amount + * @param dy2 - Extends the MBR downward by this amount + * + * @sign public this .offsetBoundary(Number offset) + * @param offset - Extend the MBR in all directions by this amount + * + * You would most likely use this function to ensure that custom canvas rendering beyond the extent of the entity's normal bounds is not clipped. + */ + offsetBoundary: function(x1, y1, x2, y2){ + if (arguments.length === 1) + y1 = x2 = y2 = x1; + this._bx1 = x1; + this._bx2 = x2; + this._by1 = y1; + this._by2 = y2; + this.trigger("BoundaryOffset"); + this._calculateMBR(); + return this; + }, + /** * Calculates the MBR when rotated some number of radians about an origin point o. - * Necessary on a rotation, or a resize (when already rotated) + * Necessary on a rotation, or a resize */ - _calculateMBR: function (ox, oy, rad) { - if (rad === 0) { - this._mbr = null; - return; - } + _calculateMBR: function () { + var ox = this._origin.x + this._x, + oy = this._origin.y + this._y, + rad = -this._rotation * DEG_TO_RAD; + // axis-aligned (unrotated) coordinates, relative to the origin point + var dx1 = this._x - this._bx1 - ox, + dx2 = this._x + this._w + this._bx2 - ox, + dy1 = this._y - this._by1 - oy, + dy2 = this._y + this._h + this._by2 - oy; var ct = Math.cos(rad), st = Math.sin(rad); // Special case 90 degree rotations to prevent rounding problems ct = (ct < 1e-10 && ct > -1e-10) ? 0 : ct; st = (st < 1e-10 && st > -1e-10) ? 0 : st; - var x0 = ox + (this._x - ox) * ct + (this._y - oy) * st, - y0 = oy - (this._x - ox) * st + (this._y - oy) * ct, - x1 = ox + (this._x + this._w - ox) * ct + (this._y - oy) * st, - y1 = oy - (this._x + this._w - ox) * st + (this._y - oy) * ct, - x2 = ox + (this._x + this._w - ox) * ct + (this._y + this._h - oy) * st, - y2 = oy - (this._x + this._w - ox) * st + (this._y + this._h - oy) * ct, - x3 = ox + (this._x - ox) * ct + (this._y + this._h - oy) * st, - y3 = oy - (this._x - ox) * st + (this._y + this._h - oy) * ct, - minx = Math.floor(Math.min(x0, x1, x2, x3)), - miny = Math.floor(Math.min(y0, y1, y2, y3)), - maxx = Math.ceil(Math.max(x0, x1, x2, x3)), - maxy = Math.ceil(Math.max(y0, y1, y2, y3)); + + // Calculate the new points relative to the origin, then find the new (absolute) bounding coordinates! + var x0 = dx1 * ct + dy1 * st, + y0 = - dx1 * st + dy1 * ct, + x1 = dx2 * ct + dy1 * st, + y1 = - dx2 * st + dy1 * ct, + x2 = dx2 * ct + dy2 * st, + y2 = - dx2 * st + dy2 * ct, + x3 = dx1 * ct + dy2 * st, + y3 = - dx1 * st + dy2 * ct, + minx = Math.floor(Math.min(x0, x1, x2, x3) + ox), + miny = Math.floor(Math.min(y0, y1, y2, y3) + oy), + maxx = Math.ceil(Math.max(x0, x1, x2, x3) + ox), + maxy = Math.ceil(Math.max(y0, y1, y2, y3) + oy); if (!this._mbr) { this._mbr = { _x: minx, @@ -517,6 +445,23 @@ Crafty.c("2D", { this._mbr._h = maxy - miny; } + // If a collision hitbox exists AND sits outside the entity, find a bounding box for both. + // `_cbr` contains information about a bounding circle of the hitbox. + // The bounds of `_cbr` will be the union of the `_mbr` and the bounding box of that circle. + // This will not be a minimal region, but since it's only used for the broad phase pass it's good enough. + // + // cbr is calculated by the `_checkBounds` method of the "Collision" component + if (this._cbr) { + var cbr = this._cbr; + var cx = cbr.cx, cy = cbr.cy, r = cbr.r; + var cx2 = ox + (cx + this._x - ox) * ct + (cy + this._y - oy) * st; + var cy2 = oy - (cx + this._x - ox) * st + (cy + this._y - oy) * ct; + cbr._x = Math.min(cx2 - r, minx); + cbr._y = Math.min(cy2 - r, miny); + cbr._w = Math.max(cx2 + r, maxx) - cbr._x; + cbr._h = Math.max(cy2 + r, maxy) - cbr._y; + } + }, /** @@ -528,6 +473,8 @@ Crafty.c("2D", { // skip if there's no rotation! if (difference === 0) return; + else + this._rotation = v; //Calculate the new MBR var rad = theta * DEG_TO_RAD, @@ -536,7 +483,7 @@ Crafty.c("2D", { y: this._origin.y + this._y }; - this._calculateMBR(o.x, o.y, rad); + this._calculateMBR(); //trigger "Rotate" event @@ -549,13 +496,7 @@ Crafty.c("2D", { sin: Math.sin(drad), deg: difference, rad: drad, - o: o, - matrix: { - M11: ct, - M12: st, - M21: -st, - M22: ct - } + o: o }); }, @@ -768,7 +709,7 @@ Crafty.c("2D", { l = children.length, obj; //rotation - if (e.cos) { + if (("cos" in e) || ("sin" in e)) { for (; i < l; ++i) { obj = children[i]; if ('rotate' in obj) obj.rotate(e); @@ -898,7 +839,7 @@ Crafty.c("2D", { /**@ * #.flip * @comp 2D - * @trigger Change - when the entity has flipped + * @trigger Invalidate - when the entity has flipped * @sign public this .flip(String dir) * @param dir - Flip direction * @@ -913,7 +854,7 @@ Crafty.c("2D", { dir = dir || "X"; if (!this["_flip" + dir]) { this["_flip" + dir] = true; - this.trigger("Change"); + this.trigger("Invalidate"); } return this; }, @@ -921,7 +862,7 @@ Crafty.c("2D", { /**@ * #.unflip * @comp 2D - * @trigger Change - when the entity has unflipped + * @trigger Invalidate - when the entity has unflipped * @sign public this .unflip(String dir) * @param dir - Unflip direction * @@ -936,7 +877,7 @@ Crafty.c("2D", { dir = dir || "X"; if (this["_flip" + dir]) { this["_flip" + dir] = false; - this.trigger("Change"); + this.trigger("Invalidate"); } return this; }, @@ -950,7 +891,7 @@ Crafty.c("2D", { y2 = (this._y + this._origin.y - e.o.y) * e.cos - (this._x + this._origin.x - e.o.x) * e.sin + (e.o.y - this._origin.y); this._attr('_rotation', this._rotation - e.deg); this._attr('_x', x2 ); - this._attr('_y', y2 ); + this._attr('_y', y2 ); }, /**@ @@ -977,10 +918,15 @@ Crafty.c("2D", { this.trigger("reorder"); //if the rect bounds change, update the MBR and trigger move } else if (name === '_x' || name === '_y') { + // mbr is the minimal bounding rectangle of the entity mbr = this._mbr; - if (mbr) { mbr[name] -= this[name] - value; + // cbr is a non-minmal bounding rectangle that contains both hitbox and mbr + // It will exist only when the collision hitbox sits outside the entity + if (this._cbr){ + this._cbr[name] -= this[name] - value; + } } this[name] = value; @@ -992,7 +938,7 @@ Crafty.c("2D", { var oldValue = this[name]; this[name] = value; if (mbr) { - this._calculateMBR(this._origin.x + this._x, this._origin.y + this._y, -this._rotation * DEG_TO_RAD); + this._calculateMBR(); } if (name === '_w') { this.trigger("Resize", { @@ -1012,8 +958,8 @@ Crafty.c("2D", { //everything will assume the value this[name] = value; - //trigger a change - this.trigger("Change", old); + // flag for redraw + this.trigger("Invalidate"); Crafty._rectPool.recycle(old); } @@ -1022,6 +968,8 @@ Crafty.c("2D", { /**@ * #Gravity * @category 2D + * @trigger Moved - When entity has moved on y-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y} + * * Adds gravitational pull to the entity. */ Crafty.c("Gravity", { @@ -1089,6 +1037,7 @@ Crafty.c("Gravity", { //if falling, move the players Y this._gy += this._gravityConst; this.y += this._gy; + this.trigger('Moved', { x: this._x, y: this._y - this._gy }); } else { this._gy = 0; //reset change in y } @@ -1122,7 +1071,7 @@ Crafty.c("Gravity", { if (hit) { //stop falling if found and player is moving down if (this._falling && ((this._gy > this._jumpSpeed) || !this._up)){ this.stopFalling(hit); - } + } } else { this._falling = true; //keep falling otherwise } @@ -1378,7 +1327,7 @@ Crafty.matrix.prototype = { } }; -},{"./HashMap.js":4,"./core.js":9}],2:[function(require,module,exports){ +},{"./HashMap.js":4,"./core.js":10}],2:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -1421,46 +1370,19 @@ Crafty.c("DOM", { this._element.style.position = "absolute"; this._element.id = "ent" + this[0]; - this.bind("Change", function () { - if (!this._changed) { - this._changed = true; - Crafty.DrawManager.addDom(this); - } - }); - - function updateClass() { - var i = 0, - c = this.__c, - str = ""; - for (i in c) { - str += ' ' + i; - } - str = str.substr(1); - this._element.className = str; - } - - this.bind("NewComponent", updateClass).bind("RemoveComponent", updateClass); + this.bind("Invalidate", this._invalidateDOM); + this.bind("NewComponent", this._updateClass); + this.bind("RemoveComponent", this._removeClass); - if (Crafty.support.prefix === "ms" && Crafty.support.version < 9) { - this._filters = {}; + this._invalidateDOM(); - this.bind("Rotate", function (e) { - var m = e.matrix, - elem = this._element.style, - M11 = m.M11.toFixed(8), - M12 = m.M12.toFixed(8), - M21 = m.M21.toFixed(8), - M22 = m.M22.toFixed(8); - - this._filters.rotation = "progid:DXImageTransform.Microsoft.Matrix(M11=" + M11 + ", M12=" + M12 + ", M21=" + M21 + ", M22=" + M22 + ",sizingMethod='auto expand')"; - }); - } + }, - this.bind("Remove", this.undraw); - this.bind("RemoveComponent", function (compName) { - if (compName === "DOM") - this.undraw(); - }); + remove: function(){ + this.undraw(); + this.unbind("NewComponent", this._updateClass); + this.unbind("RemoveComponent", this._removeClass); + this.unbind("Invalidate", this._invalidateDOM); }, /**@ @@ -1474,6 +1396,39 @@ Crafty.c("DOM", { return this._element.id; }, + // removes a component on RemoveComponent events + _removeClass: function(removedComponent) { + var i = 0, + c = this.__c, + str = ""; + for (i in c) { + if(i != removedComponent) { + str += ' ' + i; + } + } + str = str.substr(1); + this._element.className = str; + }, + + // adds a class on NewComponent events + _updateClass: function() { + var i = 0, + c = this.__c, + str = ""; + for (i in c) { + str += ' ' + i; + } + str = str.substr(1); + this._element.className = str; + }, + + _invalidateDOM: function(){ + if (!this._changed) { + this._changed = true; + Crafty.DrawManager.addDom(this); + } + }, + /**@ * #.DOM * @comp DOM @@ -1553,17 +1508,6 @@ Crafty.c("DOM", { style[prefix + "Opacity"] = this._alpha; } - //if not version 9 of IE - if (prefix === "ms" && Crafty.support.version < 9) { - //for IE version 8, use ImageTransform filter - if (Crafty.support.version === 8) { - this._filters.alpha = "progid:DXImageTransform.Microsoft.Alpha(Opacity=" + (this._alpha * 100) + ")"; // first! - //all other versions use filter - } else { - this._filters.alpha = "alpha(opacity=" + (this._alpha * 100) + ")"; - } - } - if (this._mbr) { var origin = this._origin.x + "px " + this._origin.y + "px"; style.transformOrigin = origin; @@ -1574,21 +1518,10 @@ Crafty.c("DOM", { if (this._flipX) { trans.push("scaleX(-1)"); - if (prefix === "ms" && Crafty.support.version < 9) { - this._filters.flipX = "fliph"; - } } if (this._flipY) { trans.push("scaleY(-1)"); - if (prefix === "ms" && Crafty.support.version < 9) { - this._filters.flipY = "flipv"; - } - } - - //apply the filters if IE - if (prefix === "ms" && Crafty.support.version < 9) { - this.applyFilters(); } if (this._cssStyles.transform != trans.join(" ")) { @@ -1606,18 +1539,6 @@ Crafty.c("DOM", { return this; }, - applyFilters: function () { - this._element.style.filter = ""; - var str = ""; - - for (var filter in this._filters) { - if (!this._filters.hasOwnProperty(filter)) continue; - str += this._filters[filter] + " "; - } - - this._element.style.filter = str; - }, - /**@ * #.undraw * @comp DOM @@ -1626,8 +1547,9 @@ Crafty.c("DOM", { * Removes the element from the stage. */ undraw: function () { - if (this._element) { - Crafty.stage.inner.removeChild(this._element); + var el = this._element; + if (el && el.parentNode !== null) { + el.parentNode.removeChild(el); } return this; }, @@ -1635,10 +1557,11 @@ Crafty.c("DOM", { /**@ * #.css * @comp DOM - * @sign public * css(String property, String value) + * @sign public css(String property, String value) * @param property - CSS property to modify * @param value - Value to give the CSS property - * @sign public * css(Object map) + * + * @sign public css(Object map) * @param map - Object where the key is the CSS property and the value is CSS value * * Apply CSS styles to the element. @@ -1656,7 +1579,7 @@ Crafty.c("DOM", { * * @example * ~~~ - * this.css({'text-align', 'center', 'text-decoration': 'line-through'}); + * this.css({'text-align': 'center', 'text-decoration': 'line-through'}); * this.css("textAlign", "center"); * this.css("text-align"); //returns center * ~~~ @@ -1686,18 +1609,12 @@ Crafty.c("DOM", { } } - this.trigger("Change"); + this.trigger("Invalidate"); return this; } }); -/** - * Fix IE6 background flickering - */ -try { - document.execCommand("BackgroundImageCache", false, true); -} catch (e) {} Crafty.extend({ /**@ @@ -1720,8 +1637,37 @@ Crafty.extend({ this.height = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight); // Bind scene rendering (see drawing.js) - Crafty.unbind("RenderScene", Crafty.DrawManager.renderDOM); - Crafty.bind("RenderScene", Crafty.DrawManager.renderDOM); + Crafty.uniqueBind("RenderScene", Crafty.DrawManager.renderDOM); + // Resize the viewport + Crafty.uniqueBind("ViewportResize", this._resize); + + // Listen for changes in pixel art settings + // Since window is inited before stage, can't set right away, but shouldn't need to! + Crafty.uniqueBind("PixelartSet", this._setPixelArt); + }, + + _resize: function(){ + Crafty.stage.elem.style.width = Crafty.viewport.width + "px"; + Crafty.stage.elem.style.height = Crafty.viewport.height + "px"; + }, + + // Handle whether images should be smoothed or not + _setPixelArt: function(enabled) { + var style = Crafty.stage.inner.style; + if (enabled) { + style[Crafty.DOM.camelize("image-rendering")] = "optimizeSpeed"; /* legacy */ + style[Crafty.DOM.camelize("image-rendering")] = "-moz-crisp-edges"; /* Firefox */ + style[Crafty.DOM.camelize("image-rendering")] = "-o-crisp-edges"; /* Opera */ + style[Crafty.DOM.camelize("image-rendering")] = "-webkit-optimize-contrast"; /* Webkit (Chrome & Safari) */ + style[Crafty.DOM.camelize("-ms-interpolation-mode")] = "nearest-neighbor"; /* IE */ + style[Crafty.DOM.camelize("image-rendering")] = "optimize-contrast"; /* CSS3 proposed */ + style[Crafty.DOM.camelize("image-rendering")] = "pixelated"; /* CSS4 proposed */ + style[Crafty.DOM.camelize("image-rendering")] = "crisp-edges"; /* CSS4 proposed */ + } else { + style[Crafty.DOM.camelize("image-rendering")] = "optimizeQuality"; /* legacy */ + style[Crafty.DOM.camelize("-ms-interpolation-mode")] = "bicubic"; /* IE */ + style[Crafty.DOM.camelize("image-rendering")] = "auto"; /* CSS3 */ + } }, width: 0, @@ -1798,24 +1744,29 @@ Crafty.extend({ /**@ * #Crafty.DOM.translate * @comp Crafty.DOM - * @sign public Object Crafty.DOM.translate(Number x, Number y) - * @param x - x position to translate - * @param y - y position to translate - * @return Object with x and y as keys and translated values - * - * Method will translate x and y positions to positions on the - * stage. Useful for mouse events with `e.clientX` and `e.clientY`. + * @sign public Object Crafty.DOM.translate(Number clientX, Number clientY) + * @param clientX - clientX position in the browser screen + * @param clientY - clientY position in the browser screen + * @return Object `{x: ..., y: ...}` with Crafty coordinates. + * + * The parameters clientX and clientY are pixel coordinates within the visible + * browser window. This function translates those to Crafty coordinates (i.e., + * the coordinates that you might apply to an entity), by taking into account + * where the stage is within the screen, what the current viewport is, etc. */ - translate: function (x, y) { + translate: function (clientX, clientY) { + var doc = document.documentElement; + var body = document.body; + return { - x: (x - Crafty.stage.x + document.body.scrollLeft + document.documentElement.scrollLeft - Crafty.viewport._x) / Crafty.viewport._scale, - y: (y - Crafty.stage.y + document.body.scrollTop + document.documentElement.scrollTop - Crafty.viewport._y) / Crafty.viewport._scale + x: (clientX - Crafty.stage.x + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 )) / Crafty.viewport._scale - Crafty.viewport._x, + y: (clientY - Crafty.stage.y + ( doc && doc.scrollTop || body && body.scrollTop || 0 )) / Crafty.viewport._scale - Crafty.viewport._y }; } } }); -},{"./core.js":9}],3:[function(require,module,exports){ +},{"./core.js":10}],3:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -1874,9 +1825,10 @@ Crafty.c("DebugCanvas", { * @comp DebugCanvas * @sign public .debugFill([String fillStyle]) * @param fillStyle - The color the component will be filled with. Defaults to "red". Pass the boolean false to turn off filling. + * @example * ~~~ * var myEntity = Crafty.e("2D, Collision, SolidHitBox ").debugFill("purple") - *~~~ + * ~~~ */ debugFill: function (fillStyle) { if (typeof fillStyle === 'undefined') @@ -1890,9 +1842,10 @@ Crafty.c("DebugCanvas", { * @comp DebugCanvas * @sign public .debugStroke([String strokeStyle]) * @param strokeStyle - The color the component will be outlined with. Defaults to "red". Pass the boolean false to turn this off. + * @example * ~~~ * var myEntity = Crafty.e("2D, Collision, WiredHitBox ").debugStroke("white") - *~~~ + * ~~~ */ debugStroke: function (strokeStyle) { if (typeof strokeStyle === 'undefined') @@ -2172,7 +2125,7 @@ Crafty.DebugCanvas = { current; var view = Crafty.viewport; - ctx.setTransform(view._scale, 0, 0, view._scale, view._x, view._y); + ctx.setTransform(view._scale, 0, 0, view._scale, Math.round(view._x*view._scale), Math.round(view._y*view._scale)); ctx.clearRect(rect._x, rect._y, rect._w, rect._h); @@ -2188,7 +2141,7 @@ Crafty.DebugCanvas = { }; -},{"./core.js":9}],4:[function(require,module,exports){ +},{"./core.js":10}],4:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -2498,10 +2451,8 @@ var Crafty = require('./core.js'), * @category 2D * Broad-phase collision detection engine. See background information at * - * ~~~ * - [N Tutorial B - Broad-Phase Collision](http://www.metanetsoftware.com/technique/tutorialB.html) * - [Broad-Phase Collision Detection with CUDA](http.developer.nvidia.com/GPUGems3/gpugems3_ch32.html) - * ~~~ * @see Crafty.map */ @@ -2551,7 +2502,7 @@ var Crafty = require('./core.js'), module.exports = HashMap; -},{"./core.js":9}],5:[function(require,module,exports){ +},{"./core.js":10}],5:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -2568,13 +2519,13 @@ Crafty.easing.prototype = { steps: null, complete: false, paused: false, - - // init values - reset: function(){ + + // init values + reset: function(){ this.loops = 1; this.clock = 0; this.complete = false; - this.paused = false; + this.paused = false; }, repeat: function(loopCount){ @@ -2585,7 +2536,7 @@ Crafty.easing.prototype = { this.clock = this.duration * progress; if (typeof loopCount !== "undefined") this.loops = loopCount; - + }, pause: function(){ @@ -2615,7 +2566,7 @@ Crafty.easing.prototype = { // same as value for now; with other time value functions would be more useful time: function(){ return ( Math.min(this.clock/this.duration, 1) ); - + }, // Value is where along the tweening curve we are @@ -2631,1059 +2582,934 @@ Crafty.easing.prototype = { + /**@ -* #SpriteAnimation -* @category Animation -* @trigger StartAnimation - When an animation starts playing, or is resumed from the paused state - {Reel} -* @trigger AnimationEnd - When the animation finishes - { Reel } -* @trigger FrameChange - Each time the frame of the current reel changes - { Reel } -* @trigger ReelChange - When the reel changes - { Reel } -* -* Used to animate sprites by treating a sprite map as a set of animation frames. -* Must be applied to an entity that has a sprite-map component. -* -* To define an animation, see the `reel` method. To play an animation, see the `animate` method. -* -* A reel is an object that contains the animation frames and current state for an animation. The reel object has the following properties: -* @param id: (String) - the name of the reel -* @param frames: (Array) - A list of frames in the format [xpos, ypos] -* @param currentFrame: (Number) - The index of the current frame -* @param easing: (Crafty.easing object) - The object that handles the internal progress of the animation. -* @param duration: (Number) - The duration in milliseconds. -* -* Many animation related events pass a reel object as data. As typical with events, this should be treated as read only data that might be later altered by the entity. If you wish to preserve the data, make a copy of it. -* -* @see crafty.sprite -*/ -Crafty.c("SpriteAnimation", { - /* - * - * A map in which the keys are the names assigned to animations defined using - * the component (also known as reelIDs), and the values are objects describing - * the animation and its state. - */ - _reels: null, + * #Tween + * @category Animation + * @trigger TweenEnd - when a tween finishes - String - property + * + * Component to animate the change in 2D properties over time. + */ +Crafty.c("Tween", { - /* - * The reelID of the currently active reel (which is one of the elements in `this._reels`). - * This value is `null` if no reel is active. Some of the component's actions can be invoked - * without specifying a reel, in which case they will work on the active reel. - */ - _currentReelId: null, + init: function(){ + this.tweenGroup = {}; + this.tweenStart = {}; + this.tweens = []; + this.bind("EnterFrame", this._tweenTick); - /* - * The currently active reel. - * This value is `null` if no reel is active. - */ - _currentReel: null, + }, - /* - * Whether or not an animation is currently playing. - */ - _isPlaying: false, + _tweenTick: function(frameData){ + var tween, v, i; + for ( i = this.tweens.length-1; i>=0; i--){ + tween = this.tweens[i]; + tween.easing.tick(frameData.dt); + v = tween.easing.value(); + this._doTween(tween.props, v); + if (tween.easing.complete) { + this.tweens.splice(i, 1); + this._endTween(tween.props); + } + } + }, + _doTween: function(props, v){ + for (var name in props) + this[name] = (1-v) * this.tweenStart[name] + v * props[name]; - init: function () { - this._reels = {}; }, + + /**@ - * #.reel - * @comp SpriteAnimation - * Used to define reels, to change the active reel, and to fetch the id of the active reel. - * - * @sign public this .reel(String reelId, Duration duration, Number fromX, Number fromY, Number frameCount) - * Defines a reel by starting and ending position on the sprite sheet. - * @param reelId - ID of the animation reel being created - * @param duration - The length of the animation in milliseconds. - * @param fromX - Starting `x` position on the sprite map (x's unit is the horizontal size of the sprite in the sprite map). - * @param fromY - `y` position on the sprite map (y's unit is the horizontal size of the sprite in the sprite map). Remains constant through the animation. - * @param frameCount - The number of sequential frames in the animation. If negative, the animation will play backwards. - * - * @sign public this .reel(String reelId, Duration duration, Array frames) - * Defines a reel by an explicit list of frames - * @param reelId - ID of the animation reel being created - * @param duration - The length of the animation in milliseconds. - * @param frames - An array of arrays containing the `x` and `y` values of successive frames: [[x1,y1],[x2,y2],...] (the values are in the unit of the sprite map's width/height respectively). - * - * @sign public this .reel(String reelId) - * Switches to the specified reel. The sprite will be updated to that reel's current frame - * @param reelID - the ID to switch to - * - * @sign public Reel .reel() - * @return The id of the current reel - * - * - * A method to handle animation reels. Only works for sprites built with the Crafty.sprite methods. - * See the Tween component for animation of 2D properties. + * #.tween + * @comp Tween + * @sign public this .tween(Object properties, Number|String duration) + * @param properties - Object of numeric properties and what they should animate to + * @param duration - Duration to animate the properties over, in milliseconds. * - * To setup an animation reel, pass the name of the reel (used to identify the reel later), and either an - * array of absolute sprite positions or the start x on the sprite map, the y on the sprite map and then the end x on the sprite map. + * This method will animate numeric properties over the specified duration. + * These include `x`, `y`, `w`, `h`, `alpha` and `rotation`. * + * The object passed should have the properties as keys and the value should be the resulting + * values of the properties. The passed object might be modified if later calls to tween animate the same properties. * * @example + * Move an object to 100,100 and fade out over 200 ms. * ~~~ - * // Define a sprite-map component - * Crafty.sprite(16, "images/sprite.png", { - * PlayerSprite: [0,0] - * }); - * - * // Define an animation on the second row of the sprite map (y = 1) - * // from the left most sprite (fromX = 0) to the fourth sprite - * // on that row (toX = 3), with a duration of 1 second - * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite").reel('PlayerRunning', 1000, 0, 1, 3); - * - * // This is the same animation definition, but using the alternative method - * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite").reel('PlayerRunning', 1000, [[0, 1], [1, 1], [2, 1], [3, 1]]); + * Crafty.e("2D, Tween") + * .attr({alpha: 1.0, x: 0, y: 0}) + * .tween({alpha: 0.0, x: 100, y: 100}, 200) * ~~~ - */ - reel: function (reelId, duration, fromX, fromY, frameCount) { - // @sign public this .reel() - if (arguments.length === 0) - return this._currentReelId; - - // @sign public this .reel(String reelID) - if (arguments.length === 1 && typeof reelId === "string"){ - if (typeof this._reels[reelId] === "undefined") - throw("The specified reel " + reelId + " is undefined."); - this.pauseAnimation(); - if (this._currentReelId !== reelId) { - this._currentReelId = reelId; - this._currentReel = this._reels[reelId]; - // Change the visible sprite - this._updateSprite(); - // Trigger event - this.trigger("ReelChange", this._currentReel); - } - return this; - } - - - var reel, i, tile, tileh, pos; - - // Get the dimensions of a single frame, as defind in Sprite component. - tile = this.__tile + parseInt(this.__padding[0] || 0, 10); - tileh = this.__tileh + parseInt(this.__padding[1] || 0, 10); + * @example + * Rotate an object over 2 seconds + * ~~~ + * Crafty.e("2D, Tween") + * .attr({rotation:0}) + * .tween({rotation:180}, 2000) + * ~~~ + * + */ + tween: function (props, duration) { - reel = { - id: reelId, - frames: [], - currentFrame: 0, - easing: new Crafty.easing(duration), - defaultLoops: 1 + var tween = { + props: props, + easing: new Crafty.easing(duration) }; - reel.duration = reel.easing.duration; - - // @sign public this .reel(String reelId, Number duration, Number fromX, Number y, Number toX) - if (typeof fromX === "number") { - i = fromX; - y = fromY; - if (frameCount >= 0) { - for (; i < fromX + frameCount ; i++) { - reel.frames.push([i * tile, y * tileh]); - } - } - else { - for (; i > fromX + frameCount; i--) { - reel.frames.push([i * tile, y * tileh]); - } - } - } - // @sign public this .reel(String reelId, Number duration, Array frames) - else if (arguments.length === 3 && typeof fromX === "object") { - - i = 0; - toX = fromX.length - 1; - - for (; i <= toX; i++) { - pos = fromX[i]; - reel.frames.push([pos[0] * tile, pos[1] * tileh]); - } - } - else { - throw "Urecognized arguments. Please see the documentation for 'reel(...)'."; + // Tweens are grouped together by the original function call. + // Individual properties must belong to only a single group + // When a new tween starts, if it already belongs to a group, move it to the new one + // Record the group it currently belongs to, as well as its starting coordinate. + for (var propname in props){ + if (typeof this.tweenGroup[propname] !== "undefined") + this.cancelTween(propname); + this.tweenStart[propname] = this[propname]; + this.tweenGroup[propname] = props; } - - this._reels[reelId] = reel; + this.tweens.push(tween); return this; + }, /**@ - * #.animate - * @comp SpriteAnimation - * @sign public this .animate([String reelId] [, Number loopCount]) - * @param reelId - ID of the animation reel to play. Defaults to the current reel if none is specified. - * @param loopCount - Number of times to repeat the animation. Use -1 to repeat indefinitely. Defaults to 1. - * - * Play one of the reels previously defined through `.reel(...)`. Simply pass the name of the reel. If you wish the - * animation to play multiple times in succession, pass in the amount of times as an additional parameter. - * To have the animation repeat indefinitely, pass in `-1`. - * - * If another animation is currently playing, it will be paused. - * - * This will always play an animation from the beginning. If you wish to resume from the current state of a reel, use `resumeAnimation()`. - * - * Once an animation ends, it will remain at its last frame. - * + * #.cancelTween + * @comp Tween + * @sign public this .cancelTween(String target) + * @param target - The property to cancel * - * @example - * ~~~ - * // Define a sprite-map component - * Crafty.sprite(16, "images/sprite.png", { - * PlayerSprite: [0,0] - * }); + * @sign public this .cancelTween(Object target) + * @param target - An object containing the properties to cancel. * - * // Play the animation across 20 frames (so each sprite in the 4 sprite animation should be seen for 5 frames) and repeat indefinitely - * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite") - * .reel('PlayerRunning', 20, 0, 0, 3) // setup animation - * .animate('PlayerRunning', -1); // start animation - * ~~~ + * Stops tweening the specified property or properties. + * Passing the object used to start the tween might be a typical use of the second signature. */ - animate: function(reelId, loopCount) { + cancelTween: function(target){ + if (typeof target === "string"){ + if (typeof this.tweenGroup[target] == "object" ) + delete this.tweenGroup[target][target]; + } else if (typeof target === "object") { + for (var propname in target) + this.cancelTween(propname); + } - var pos; + return this; + }, - // switch to the specified reel if necessary - if (typeof reelId === "string") - this.reel(reelId); + /* + * Stops tweening the specified group of properties, and fires the "TweenEnd" event. + */ + _endTween: function(properties){ + for (var propname in properties){ + delete this.tweenGroup[propname]; + } + this.trigger("TweenEnd", properties); + } +}); - var currentReel = this._currentReel; +},{"./core.js":10}],6:[function(require,module,exports){ +var Crafty = require('./core.js'), + document = window.document; - if (typeof currentReel === "undefined" || currentReel === null) - throw("No reel is specified, and there is no currently active reel."); +/**@ + * #Canvas + * @category Graphics + * @trigger Draw - when the entity is ready to be drawn to the stage - {type: "canvas", pos, co, ctx} + * @trigger NoCanvas - if the browser does not support canvas + * + * When this component is added to an entity it will be drawn to the global canvas element. The canvas element (and hence all Canvas entities) is always rendered below any DOM entities. + * + * Crafty.canvas.init() will be automatically called if it is not called already to initialize the canvas element. + * + * Create a canvas entity like this + * ~~~ + * var myEntity = Crafty.e("2D, Canvas, Color") + * .color("green") + * .attr({x: 13, y: 37, w: 42, h: 42}); + *~~~ + */ +Crafty.c("Canvas", { - this.pauseAnimation(); // This will pause the current animation, if one is playing + init: function () { + if (!Crafty.canvas.context) { + Crafty.canvas.init(); + } - // Handle repeats; if loopCount is undefined and reelID is a number, calling with that signature - if (typeof loopCount === "undefined") - if (typeof reelId === "number") - loopCount = reelId; - else - loopCount = 1; + //increment the amount of canvas objs + Crafty.DrawManager.total2D++; + //Allocate an object to hold this components current region + this.currentRect = {}; + this._changed = true; + Crafty.DrawManager.addCanvas(this); - // set the animation to the beginning - currentReel.easing.reset(); - + this.bind("Invalidate", function (e) { + //flag if changed + if (this._changed === false) { + this._changed = true; + Crafty.DrawManager.addCanvas(this); + } - // user provided loop count. - this.loops(loopCount); + }); - // trigger the necessary events and switch to the first frame - this._setFrame(0); - // Start the anim - this.bind("EnterFrame", this._animationTick); - this._isPlaying = true; + this.bind("Remove", function () { + Crafty.DrawManager.total2D--; + this._changed = true; + Crafty.DrawManager.addCanvas(this); + }); + }, - this.trigger("StartAnimation", currentReel); - return this; - }, + /**@ + * #.draw + * @comp Canvas + * @sign public this .draw([[Context ctx, ]Number x, Number y, Number w, Number h]) + * @param ctx - Canvas 2D context if drawing on another canvas is required + * @param x - X offset for drawing a segment + * @param y - Y offset for drawing a segment + * @param w - Width of the segment to draw + * @param h - Height of the segment to draw + * + * Method to draw the entity on the canvas element. Can pass rect values for redrawing a segment of the entity. + */ - /**@ - * #.resumeAnimation - * @comp SpriteAnimation - * @sign public this .resumeAnimation() - * - * This will resume animation of the current reel from its current state. - * If a reel is already playing, or there is no current reel, there will be no effect. - */ - resumeAnimation: function() { - if (this._isPlaying === false && this._currentReel !== null) { - this.bind("EnterFrame", this._animationTick); - this._isPlaying = true; - this._currentReel.easing.resume(); - this.trigger("StartAnimation", this._currentReel); - } - return this; - }, + // Cache the various objects and arrays used in draw: + drawVars: { + type: "canvas", + pos: {}, + ctx: null, + coord: [0, 0, 0, 0], + co: { + x: 0, + y: 0, + w: 0, + h: 0 + } - /**@ - * #.pauseAnimation - * @comp SpriteAnimation - * @sign public this .pauseAnimation(void) - * - * Pauses the currently playing animation, or does nothing if no animation is playing. - */ - pauseAnimation: function () { - if (this._isPlaying === true) { - this.unbind("EnterFrame", this._animationTick); - this._isPlaying = false; - this._reels[this._currentReelId].easing.pause(); - } - return this; - }, - /**@ - * #.resetAnimation - * @comp SpriteAnimation - * @sign public this .resetAnimation() - * - * Resets the current animation to its initial state. Resets the number of loops to the last specified value, which defaults to 1. - * - * Neither pauses nor resumes the current animation. - */ - resetAnimation: function(){ - var currentReel = this._currentReel; - if (currentReel === null) - throw("No active reel to reset."); - this.reelPosition(0); - currentReel.easing.repeat(currentReel.defaultLoops); - return this; - }, + }, + draw: function (ctx, x, y, w, h) { + if (!this.ready) return; + if (arguments.length === 4) { + h = w; + w = y; + y = x; + x = ctx; + ctx = Crafty.canvas.context; + } - /**@ - * #.loops - * @comp SpriteAnimation - * @sign public this .loops(Number loopCount) - * @param loopCount - The number of times to play the animation - * - * Sets the number of times the animation will loop for. - * If called while an animation is in progress, the current state will be considered the first loop. - * - * @sign public Number .loops() - * @returns The number of loops left. Returns 0 if no reel is active. - */ - loops: function(loopCount) { - if (arguments.length === 0){ - if (this._currentReel !== null) - return this._currentReel.easing.loops; - else - return 0; - } + var pos = this.drawVars.pos; + pos._x = (this._x + (x || 0)); + pos._y = (this._y + (y || 0)); + pos._w = (w || this._w); + pos._h = (h || this._h); - if (this._currentReel !== null){ - if (loopCount < 0) - loopCount = Infinity; - this._currentReel.easing.repeat(loopCount); - this._currentReel.defaultLoops = loopCount; - } - return this; - }, + context = ctx || Crafty.canvas.context; + coord = this.__coord || [0, 0, 0, 0]; + var co = this.drawVars.co; + co.x = coord[0] + (x || 0); + co.y = coord[1] + (y || 0); + co.w = w || coord[2]; + co.h = h || coord[3]; - /**@ - * #.reelPosition - * @comp SpriteAnimation - * - * @sign public this .reelPosition(Integer position) - * Sets the position of the current reel by frame number. - * @param position - the frame to jump to. This is zero-indexed. A negative values counts back from the last frame. - * - * @sign public this .reelPosition(Number position) - * Sets the position of the current reel by percent progress. - * @param position - a non-integer number between 0 and 1 - * - * @sign public this .reelPosition(String position) - * Jumps to the specified position. The only currently accepted value is "end", which will jump to the end of the reel. - * - * @sign public Number .reelPosition() - * @returns The current frame number - * - */ - reelPosition: function(position) { - if (this._currentReel === null) - throw("No active reel."); + if (this._rotation !== 0) { + context.save(); - if (arguments.length === 0) - return this._currentReel.currentFrame; + context.translate(this._origin.x + this._x, this._origin.y + this._y); + pos._x = -this._origin.x; + pos._y = -this._origin.y; - var progress, - l = this._currentReel.frames.length; - if (position === "end") - position = l - 1; + context.rotate((this._rotation % 360) * (Math.PI / 180)); + } - if (position < 1 && position > 0) { - progress = position; - position = Math.floor(l * progress); - } else { - if (position !== Math.floor(position)) - throw("Position " + position + " is invalid."); - if (position < 0) - position = l - 1 + position; - progress = position / l; - } - // cap to last frame - position = Math.min(position, l-1); - position = Math.max(position, 0); - this._setProgress(progress); - this._setFrame(position); + if (this._flipX || this._flipY) { + context.save(); + context.scale((this._flipX ? -1 : 1), (this._flipY ? -1 : 1)); + if (this._flipX) { + pos._x = -(pos._x + pos._w); + } + if (this._flipY) { + pos._y = -(pos._y + pos._h); + } + } - return this; - - }, + var globalpha; - - // Bound to "EnterFrame". Progresses the animation by dt, changing the frame if necessary. - _animationTick: function(frameData) { - var currentReel = this._reels[this._currentReelId]; - currentReel.easing.tick(frameData.dt); - var progress = currentReel.easing.value(); - var frameNumber = Math.min( Math.floor(currentReel.frames.length * progress), currentReel.frames.length - 1); + //draw with alpha + if (this._alpha < 1.0) { + globalpha = context.globalAlpha; + context.globalAlpha = this._alpha; + } - this._setFrame(frameNumber); + this.drawVars.ctx = context; + this.trigger("Draw", this.drawVars); - if(currentReel.easing.complete === true){ - this.trigger("AnimationEnd", this._currentReel); - this.pauseAnimation(); - } - }, + if (this._rotation !== 0 || (this._flipX || this._flipY)) { + context.restore(); + } + if (globalpha) { + context.globalAlpha = globalpha; + } + return this; + } +}); +/**@ + * #Crafty.canvas + * @category Graphics + * + * Collection of methods to draw on canvas. + */ +Crafty.extend({ + canvas: { + /**@ + * #Crafty.canvas.context + * @comp Crafty.canvas + * + * This will return the 2D context of the main canvas element. + * The value returned from `Crafty.canvas._canvas.getContext('2d')`. + */ + context: null, + /**@ + * #Crafty.canvas._canvas + * @comp Crafty.canvas + * + * Main Canvas element + */ + /**@ + * #Crafty.canvas.init + * @comp Crafty.canvas + * @sign public void Crafty.canvas.init(void) + * @trigger NoCanvas - triggered if `Crafty.support.canvas` is false + * + * Creates a `canvas` element inside `Crafty.stage.elem`. Must be called + * before any entities with the Canvas component can be drawn. + * + * This method will automatically be called if no `Crafty.canvas.context` is + * found. + */ + init: function () { + //check if canvas is supported + if (!Crafty.support.canvas) { + Crafty.trigger("NoCanvas"); + Crafty.stop(); + return; + } - + //create an empty canvas element + var c; + c = document.createElement("canvas"); + c.width = Crafty.viewport.width; + c.height = Crafty.viewport.height; + c.style.position = 'absolute'; + c.style.left = "0px"; + c.style.top = "0px"; - // Set the current frame and update the displayed sprite - // The actual progress for the animation must be set seperately. - _setFrame: function(frameNumber) { - var currentReel = this._currentReel; - if (frameNumber === currentReel.currentFrame) - return; - currentReel.currentFrame = frameNumber; - this._updateSprite(); - this.trigger("FrameChange", currentReel); - }, + Crafty.stage.elem.appendChild(c); + Crafty.canvas.context = c.getContext('2d'); + Crafty.canvas._canvas = c; - // Update the displayed sprite. - _updateSprite: function() { - var currentReel = this._currentReel; - var pos = currentReel.frames[currentReel.currentFrame]; - this.__coord[0] = pos[0]; - this.__coord[1] = pos[1]; - this.trigger("Change"); // needed to trigger a redraw + //Set any existing transformations + var zoom = Crafty.viewport._scale; + if (zoom != 1) + Crafty.canvas.context.scale(zoom, zoom); - }, + // Set pixelart to current status, and listen for changes + this._setPixelart(Crafty._pixelartEnabled); + Crafty.uniqueBind("PixelartSet", this._setPixelart); + //Bind rendering of canvas context (see drawing.js) + Crafty.uniqueBind("RenderScene", Crafty.DrawManager.renderCanvas); + + Crafty.uniqueBind("ViewportResize", this._resize); + }, - // Sets the internal state of the current reel's easing object - _setProgress: function(progress, repeats) { - this._currentReel.easing.setProgress(progress, repeats); + // Resize the canvas element to the current viewport + _resize: function() { + var c = Crafty.canvas._canvas; + c.width = Crafty.viewport.width; + c.height = Crafty.viewport.height; - }, + }, + _setPixelart: function(enabled){ + var context = Crafty.canvas.context; + context.imageSmoothingEnabled = !enabled; + context.mozImageSmoothingEnabled = !enabled; + context.webkitImageSmoothingEnabled = !enabled; + context.oImageSmoothingEnabled = !enabled; + context.msImageSmoothingEnabled = !enabled; + } - /**@ - * #.isPlaying - * @comp SpriteAnimation - * @sign public Boolean .isPlaying([String reelId]) - * @param reelId - The reelId of the reel we wish to examine - * @returns The current animation state - * - * Determines if the specified animation is currently playing. If no reelId is specified, - * checks if any animation is playing. - * - * @example - * ~~~ - * myEntity.isPlaying() // is any animation playing - * myEntity.isPlaying('PlayerRunning') // is the PlayerRunning animation playing - * ~~~ - */ - isPlaying: function (reelId) { - if (!this._isPlaying) return false; + } +}); - if (!reelId) return !!this._currentReelId; - return this._currentReelId === reelId; - }, - - /**@ - * #.getReel - * @comp SpriteAnimation - * @sign public Reel .getReel() - * @returns The current reel, or null if there is no active reel - * - * @sign public Reel .getReel(reelId) - * @param reelId - The id of the reel to fetch. - * @returns The specified reel, or `undefined` if no such reel exists. - * - */ - getReel: function (reelId) { - if (arguments.length === 0){ - if (!this._currentReelId) return null; - reelId = this._currentReelId; - } - - return this._reels[reelId]; - } -}); +},{"./core.js":10}],7:[function(require,module,exports){ +var Crafty = require('./core.js'), + document = window.document, + DEG_TO_RAD = Math.PI / 180; /**@ - * #Tween - * @category Animation - * @trigger TweenEnd - when a tween finishes - String - property + * #Collision + * @category 2D + * @trigger HitOn - Triggered when collisions occur. Will not trigger again until collisions of this type cease, or an event is requested once more (using `resetHitChecks(component)`). - { hitData } + * @trigger HitOff - Triggered when collision with a specific component type ceases - { componentName } * - * Component to animate the change in 2D properties over time. + * Component to detect collision between any two convex polygons. + * + * If collision checks are registered for multiple component and collisions with + * multiple types occur simultaniously, each collision will cause an individual + * event to fire. + * + * **Note:** All data received from events is only valid for the duration of the event's callback. + * If you wish to preserve the data, make a copy of it. + * + * For a description of collision event data (hitData above), see the documentation for + * `.hit()`. + * + * @see .hit, .checkHits, .onHit */ -Crafty.c("Tween", { +Crafty.c("Collision", { + /**@ + * #.init + * @comp Collision + * Set up collision handling. + * + * By default, the collision hitbox will match the dimensions (x, y, w, h) and rotation of the object. + * + * **Note:** If the entity this component is applied to does not have its + * dimensions set the default hit area will not be set properly. + */ + init: function () { + this.requires("2D"); + this._collisionData = {}; - init: function(){ - this.tweenGroup = {}; - this.tweenStart = {}; - this.tweens = []; - this.bind("EnterFrame", this._tweenTick); + this.collision(); + }, - }, - _tweenTick: function(frameData){ - var tween, v, i; - for ( i = this.tweens.length-1; i>=0; i--){ - tween = this.tweens[i]; - tween.easing.tick(frameData.dt); - v = tween.easing.value(); - this._doTween(tween.props, v); - if (tween.easing.complete) { - this.tweens.splice(i, 1); - this._endTween(tween.props); - } - } - }, + // Run by Crafty when the component is removed + remove: function() { + this._cbr = null; + this.unbind("Resize", this._resizeMap); + this.unbind("Resize", this._checkBounds); + }, - _doTween: function(props, v){ - for (var name in props) - this[name] = (1-v) * this.tweenStart[name] + v * props[name]; + /**@ + * #.collision + * @comp Collision + * + * @trigger NewHitbox - when a new hitbox is assigned - Crafty.polygon + * + * @sign public this .collision([Crafty.polygon polygon]) + * @param polygon - Crafty.polygon object that will act as the hit area. + * + * @sign public this .collision(Array point1, .., Array pointN) + * @param point# - Array of [x, y] coordinate pairs to generate a hit area polygon. + * + * Constructor that takes a polygon or array of points to use as the hit area, + * with points being relative to the object's position in its unrotated state. + * + * The hit area must be a convex shape and not concave for collision detection to work properly. + * + * If no parameter is passed, the x, y, w, h properties of the entity will be used, and the hitbox will be resized when the entity is. + * + * If a hitbox is set that is outside of the bounds of the entity itself, there will be a small performance penalty as it is tracked separately. + * + * @example + * ~~~ + * Crafty.e("2D, Collision").collision( + * new Crafty.polygon([50,0], [100,100], [0,100]) + * ); + * + * Crafty.e("2D, Collision").collision([50,0], [100,100], [0,100]); + * ~~~ + * + * @see Crafty.polygon + */ + collision: function (poly) { + // Unbind anything bound to "Resize" + this.unbind("Resize", this._resizeMap); + this.unbind("Resize", this._checkBounds); - }, + if (!poly) { + // If no polygon is specified, then a polygon is created that matches the bounds of the entity + // It will be adjusted on a "Resize" event + poly = new Crafty.polygon([0, 0], [this._w, 0], [this._w, this._h], [0, this._h]); + this.bind("Resize", this._resizeMap); + this._cbr = null; + } else { + // Otherwise, we set the specified hitbox, converting from a list of arguments to a polygon if necessary + if (arguments.length > 1) { + //convert args to array to create polygon + var args = Array.prototype.slice.call(arguments, 0); + poly = new Crafty.polygon(args); + } + // Check to see if the polygon sits outside the entity, and set _cbr appropriately + // On resize, the new bounds will be checked if necessary + this._findBounds(poly.points); + } - /**@ - * #.tween - * @comp Tween - * @sign public this .tween(Object properties, Number|String duration) - * @param properties - Object of numeric properties and what they should animate to - * @param duration - Duration to animate the properties over, in milliseconds. - * - * This method will animate numeric properties over the specified duration. - * These include `x`, `y`, `w`, `h`, `alpha` and `rotation`. - * - * The object passed should have the properties as keys and the value should be the resulting - * values of the properties. The passed object might be modified if later calls to tween animate the same properties. - * - * @example - * Move an object to 100,100 and fade out over 200 ms. - * ~~~ - * Crafty.e("2D, Tween") - * .attr({alpha: 1.0, x: 0, y: 0}) - * .tween({alpha: 0.0, x: 100, y: 100}, 200) - * ~~~ - * @example - * Rotate an object over 2 seconds - * ~~~ - * Crafty.e("2D, Tween") - * .attr({rotate:0}) - * .tween({rotate:180}, 2000) - * ~~~ - * - */ - tween: function (props, duration) { - - var tween = { - props: props, - easing: new Crafty.easing(duration) - }; + // If the entity is currently rotated, the points in the hitbox must also be rotated + if (this.rotation) { + poly.rotate({ + cos: Math.cos(-this.rotation * DEG_TO_RAD), + sin: Math.sin(-this.rotation * DEG_TO_RAD), + o: { + x: this._origin.x, + y: this._origin.y + } + }); + } - // Tweens are grouped together by the original function call. - // Individual properties must belong to only a single group - // When a new tween starts, if it already belongs to a group, move it to the new one - // Record the group it currently belongs to, as well as its starting coordinate. - for (var propname in props){ - if (typeof this.tweenGroup[propname] !== "undefined") - this.cancelTween(propname); - this.tweenStart[propname] = this[propname]; - this.tweenGroup[propname] = props; - } - this.tweens.push(tween); - - return this; + // Finally, assign the hitbox, and attach it to the "Collision" entity + this.map = poly; + this.attach(this.map); + this.map.shift(this._x, this._y); + this.trigger("NewHitbox", poly); + return this; + }, - }, - /**@ - * #.cancelTween - * @comp Tween - * @sign public this .cancelTween(String target) - * @param target - The property to cancel - * - * @sign public this .cancelTween(Object target) - * @param target - An object containing the properties to cancel. - * - * Stops tweening the specified property or properties. - * Passing the object used to start the tween might be a typical use of the second signature. - */ - cancelTween: function(target){ - if (typeof target === "string"){ - if (typeof this.tweenGroup[target] == "object" ) - delete this.tweenGroup[target][target]; - } else if (typeof target === "object") { - for (var propname in target) - this.cancelTween(propname); - } + // If the hitbox is set by hand, it might extend beyond the entity. + // In such a case, we need to track this separately. + // This function finds a (non-minimal) bounding circle around the hitbox. + // + // It uses a pretty naive algorithm to do so, for more complicated options see [wikipedia](http://en.wikipedia.org/wiki/Bounding_sphere). + _findBounds: function(points) { + var minX = Infinity, maxX = -Infinity, minY=Infinity, maxY=-Infinity; + var p; + + // Calculate the MBR of the points by finding the min/max x and y + for (var i=0; i maxX) + maxX = p[0]; + if (p[1] < minY) + minY = p[1]; + if (p[1] > maxY) + maxY = p[1]; + } + + // This describes a circle centered on the MBR of the points, with a diameter equal to its diagonal + // It will be used to find a rough bounding box round the points, even if they've been rotated + var cbr = { + cx: (minX + maxX) / 2, + cy: (minY + maxY) / 2, + r: Math.sqrt( (maxX - minX)*(maxX - minX) + (maxY - minY)*(maxY - minY))/2, + }; - return this; - - }, + // We need to worry about resizing, but only if resizing could possibly change whether the hitbox is in or out of bounds + // Thus if the upper-left corner is out of bounds, then there's no need to recheck on resize + if (minX >= 0 && minY >= 0) { + this._checkBounds = function() { + if (this._cbr === null && this._w < maxX || this._h < maxY ){ + this._cbr = cbr; + this._calculateMBR(); + } else if (this._cbr) { + this._cbr = null; + this._calculateMBR(); + } + }; + this.bind("Resize", this._checkBounds); + } - /* - * Stops tweening the specified group of properties, and fires the "TweenEnd" event. - */ - _endTween: function(properties){ - for (var propname in properties){ - delete this.tweenGroup[propname]; - } - this.trigger("TweenEnd", properties); - } -}); + // If the hitbox is within the entity, _cbr is null + // Otherwise, set it, and immediately calculate the bounding box. + if (minX >= 0 && minY >= 0 && maxX <= this._w && maxY <= this._h){ + this._cbr = null; + return false; + } else { + this._cbr = cbr; + this._calculateMBR(); + return true; + } -},{"./core.js":9}],6:[function(require,module,exports){ -var Crafty = require('./core.js'), - document = window.document; + }, -/**@ - * #Canvas - * @category Graphics - * @trigger Draw - when the entity is ready to be drawn to the stage - {type: "canvas", pos, co, ctx} - * @trigger NoCanvas - if the browser does not support canvas - * - * When this component is added to an entity it will be drawn to the global canvas element. The canvas element (and hence all Canvas entities) is always rendered below any DOM entities. - * - * Crafty.canvas.init() will be automatically called if it is not called already to initialize the canvas element. - * - * Create a canvas entity like this - * ~~~ - * var myEntity = Crafty.e("2D, Canvas, Color").color("green") - * .attr({x: 13, y: 37, w: 42, h: 42}); - *~~~ - */ -Crafty.c("Canvas", { + // The default behavior is to match the hitbox to the entity. + // This function will change the hitbox when a "Resize" event triggers. + _resizeMap: function (e) { - init: function () { - if (!Crafty.canvas.context) { - Crafty.canvas.init(); - } + var dx, dy, rot = this.rotation * DEG_TO_RAD, + points = this.map.points; - //increment the amount of canvas objs - Crafty.DrawManager.total2D++; - //Allocate an object to hold this components current region - this.currentRect = {}; - this._changed = true; - Crafty.DrawManager.addCanvas(this); + // Depending on the change of axis, move the corners of the rectangle appropriately + if (e.axis === 'w') { + if (rot) { + dx = e.amount * Math.cos(rot); + dy = e.amount * Math.sin(rot); + } else { + dx = e.amount; + dy = 0; + } - this.bind("Change", function (e) { - //flag if changed - if (this._changed === false) { - this._changed = true; - Crafty.DrawManager.addCanvas(this); + // "top right" point shifts on change of w + points[1][0] += dx; + points[1][1] += dy; + } else { + if (rot) { + dy = e.amount * Math.cos(rot); + dx = -e.amount * Math.sin(rot); + } else { + dx = 0; + dy = e.amount; } - }); + // "bottom left" point shifts on change of h + points[3][0] += dx; + points[3][1] += dy; + } - - this.bind("Remove", function () { - Crafty.DrawManager.total2D--; - this._changed = true; - Crafty.DrawManager.addCanvas(this); - }); - }, + // "bottom right" point shifts on either change + points[2][0] += dx; + points[2][1] += dy; + }, /**@ - * #.draw - * @comp Canvas - * @sign public this .draw([[Context ctx, ]Number x, Number y, Number w, Number h]) - * @param ctx - Canvas 2D context if drawing on another canvas is required - * @param x - X offset for drawing a segment - * @param y - Y offset for drawing a segment - * @param w - Width of the segment to draw - * @param h - Height of the segment to draw + * #.hit + * @comp Collision + * @sign public Boolean/Array hit(String component) + * @param component - Check collision with entities that have this component + * applied to them. + * @return `false` if there is no collision. If a collision is detected, + * returns an Array of collision data objects (see below). * - * Method to draw the entity on the canvas element. Can pass rect values for redrawing a segment of the entity. + * Tests for collisions with entities that have the specified component + * applied to them. + * If a collision is detected, data regarding the collision will be present in + * the array returned by this method. + * If no collisions occur, this method returns false. + * + * Following is a description of a collision data object that this method may + * return: The returned collision data will be an Array of Objects with the + * type of collision used, the object collided and if the type used was SAT (a polygon was used as the hitbox) then an amount of overlap. + * ~~~ + * [{ + * obj: [entity], + * type: ["MBR" or "SAT"], + * overlap: [number] + * }] + * ~~~ + * **obj:** The entity with which the collision occured. + * **type:** Collision detection method used. One of: + * + *MBR:* Standard axis aligned rectangle intersection (`.intersect` in the 2D component). + * + *SAT:* Collision between any two convex polygons. Used when both colliding entities have the `Collision` component applied to them. + * **overlap:** If SAT collision was used, this will signify the overlap percentage between the colliding entities. + * + * @see 2D */ + hit: function (comp) { + var area = this._cbr || this._mbr || this, + results = Crafty.map.search(area, false), + i = 0, + l = results.length, + dupes = {}, + id, obj, oarea, key, + hasMap = ('map' in this && 'containsPoint' in this.map), + finalresult = []; - // Cache the various objects and arrays used in draw: - drawVars: { - type: "canvas", - pos: {}, - ctx: null, - coord: [0, 0, 0, 0], - co: { - x: 0, - y: 0, - w: 0, - h: 0 + if (!l) { + return false; } + for (; i < l; ++i) { + obj = results[i]; + oarea = obj._cbr || obj._mbr || obj; //use the mbr - }, + if (!obj) continue; + id = obj[0]; - draw: function (ctx, x, y, w, h) { - if (!this.ready) return; - if (arguments.length === 4) { - h = w; - w = y; - y = x; - x = ctx; - ctx = Crafty.canvas.context; + //check if not added to hash and that actually intersects + if (!dupes[id] && this[0] !== id && obj.__c[comp] && + oarea._x < area._x + area._w && oarea._x + oarea._w > area._x && + oarea._y < area._y + area._h && oarea._h + oarea._y > area._y) + dupes[id] = obj; } - var pos = this.drawVars.pos; - pos._x = (this._x + (x || 0)); - pos._y = (this._y + (y || 0)); - pos._w = (w || this._w); - pos._h = (h || this._h); - - - context = ctx || Crafty.canvas.context; - coord = this.__coord || [0, 0, 0, 0]; - var co = this.drawVars.co; - co.x = coord[0] + (x || 0); - co.y = coord[1] + (y || 0); - co.w = w || coord[2]; - co.h = h || coord[3]; - - if (this._mbr) { - context.save(); + for (key in dupes) { + obj = dupes[key]; - context.translate(this._origin.x + this._x, this._origin.y + this._y); - pos._x = -this._origin.x; - pos._y = -this._origin.y; + if (hasMap && 'map' in obj) { + var SAT = this._SAT(this.map, obj.map); + SAT.obj = obj; + SAT.type = "SAT"; + if (SAT) finalresult.push(SAT); + } else { + finalresult.push({ + obj: obj, + type: "MBR" + }); + } + } - context.rotate((this._rotation % 360) * (Math.PI / 180)); + if (!finalresult.length) { + return false; } - if (this._flipX || this._flipY) { - context.save(); - context.scale((this._flipX ? -1 : 1), (this._flipY ? -1 : 1)); - if (this._flipX) { - pos._x = -(pos._x + pos._w); - } - if (this._flipY) { - pos._y = -(pos._y + pos._h); + return finalresult; + }, + + /**@ + * #.onHit + * @comp Collision + * @sign public this .onHit(String component, Function hit[, Function noHit]) + * @param component - Component to check collisions for. + * @param hit - Callback method to execute upon collision with component. Will be passed the results of the collision check in the same format documented for hit(). + * @param noHit - Callback method executed once as soon as collision stops. + * + * Creates an EnterFrame event calling .hit() each frame. When a collision is detected the callback will be invoked. + * Note that the `hit` callback will be invoked every frame the collision is active, not just the first time the collision occurs. + * If you want more fine-grained control consider using `.checkHits` or `.hit`. + * + * @see .checkHits + * @see .hit + */ + onHit: function (comp, callback, callbackOff) { + var justHit = false; + this.bind("EnterFrame", function () { + var hitdata = this.hit(comp); + if (hitdata) { + justHit = true; + callback.call(this, hitdata); + } else if (justHit) { + if (typeof callbackOff == 'function') { + callbackOff.call(this); + } + justHit = false; } - } + }); + return this; + }, - var globalpha; - //draw with alpha - if (this._alpha < 1.0) { - globalpha = context.globalAlpha; - context.globalAlpha = this._alpha; - } + /**@ + * #._createCollisionHandler + * @comp Collision + * @sign public void .checkHits(String component, Object collisionData) + * @param component - The name of the component for which this handler + * checks for collisions. + * @param collisionData - Collision data object used to track collisions with + * the specified component. + * + * This is a helper method for creating collisions handlers set up by + * `.checkHits(...)`. Do not call this directly. + * + * @see .checkHits + */ + _createCollisionHandler: function(component, collisionData) { + return function() { + var hitData = this.hit(component); - this.drawVars.ctx = context; - this.trigger("Draw", this.drawVars); + if (collisionData.occurring === true) { + if (hitData !== false) { + // The collision is still in progress + return; + } - if (this._mbr || (this._flipX || this._flipY)) { - context.restore(); + collisionData.occurring = false; + this.trigger("HitOff", component); } - if (globalpha) { - context.globalAlpha = globalpha; + else if (hitData !== false) { + collisionData.occurring = true; + this.trigger("HitOn", hitData); } - return this; - } -}); - -/**@ - * #Crafty.canvas - * @category Graphics - * - * Collection of methods to draw on canvas. - */ -Crafty.extend({ - canvas: { - /**@ - * #Crafty.canvas.context - * @comp Crafty.canvas - * - * This will return the 2D context of the main canvas element. - * The value returned from `Crafty.canvas._canvas.getContext('2d')`. - */ - context: null, - /**@ - * #Crafty.canvas._canvas - * @comp Crafty.canvas - * - * Main Canvas element - */ - - /**@ - * #Crafty.canvas.init - * @comp Crafty.canvas - * @sign public void Crafty.canvas.init(void) - * @trigger NoCanvas - triggered if `Crafty.support.canvas` is false - * - * Creates a `canvas` element inside `Crafty.stage.elem`. Must be called - * before any entities with the Canvas component can be drawn. - * - * This method will automatically be called if no `Crafty.canvas.context` is - * found. - */ - init: function () { - //check if canvas is supported - if (!Crafty.support.canvas) { - Crafty.trigger("NoCanvas"); - Crafty.stop(); - return; - } + }; + }, - //create an empty canvas element - var c; - c = document.createElement("canvas"); - c.width = Crafty.viewport.width; - c.height = Crafty.viewport.height; - c.style.position = 'absolute'; - c.style.left = "0px"; - c.style.top = "0px"; + /**@ + * #.checkHits + * @comp Collision + * @sign public this .checkHits(String componentList) + * @param componentList - A comma seperated list of components to check for collisions with. + * @sign public this .checkHits(String component1[, .., String componentN]) + * @param component# - A component to check for collisions with. + * + * Performs collision checks against all entities that have at least one of + * the components specified when calling this method. If collisions occur, + * a "HitOn" event containing the collision information will be fired for the + * entity on which this method was invoked. See the documentation for `.hit()` + * for a description of collision data contained in the event. + * When a collision that was reported ends, a corresponding "HitOff" event + * will be fired. + * + * Calling this method more than once for the same component type will not + * cause redundant hit checks. + * + * **Note:** Hit checks are performed upon entering each new frame (using + * the *EnterFrame* event). It is entirely possible for object to move in + * said frame after the checks were performed (even if the more is the + * result of *EnterFrame*, as handlers run in no particular order). In such + * a case, the hit events will not fire until the next check is performed in + * the following frame. + * + * @example + * ~~~ + * Crafty.e("2D, Collision") + * .checkHits('Solid') // check for collisions with entities that have the Solid component in each frame + * .bind("HitOn", function(hitData) { + * console.log("Collision with Solid entity occurred for the first time."); + * }) + * .bind("HitOff", function(comp) { + * console.log("Collision with Solid entity ended."); + * }); + * ~~~ + * + * @see .hit + */ + checkHits: function () { + var components = arguments; + var i = 0; - Crafty.stage.elem.appendChild(c); - Crafty.canvas.context = c.getContext('2d'); - Crafty.canvas._canvas = c; + if (components.length === 1) { + components = components[0].split(/\s*,\s*/); + } - //Set any existing transformations - var zoom = Crafty.viewport._scale; - if (zoom != 1) - Crafty.canvas.context.scale(zoom, zoom); + for (; i < components.length; ++i) { + var component = components[i]; + var collisionData = this._collisionData[component]; - //Bind rendering of canvas context (see drawing.js) - Crafty.unbind("RenderScene", Crafty.DrawManager.renderCanvas); - Crafty.bind("RenderScene", Crafty.DrawManager.renderCanvas); + if (collisionData !== undefined) { + // There is already a handler for collision with this component + continue; } - } -}); + this._collisionData[component] = collisionData = { occurring: false, handler: null }; + collisionData.handler = this._createCollisionHandler(component, collisionData); -},{"./core.js":9}],7:[function(require,module,exports){ -var Crafty = require('./core.js'), - document = window.document, - DEG_TO_RAD = Math.PI / 180; + this.bind("EnterFrame", collisionData.handler); + } + + return this; + }, -/**@ - * #Collision - * @category 2D - * Component to detect collision between any two convex polygons. - */ -Crafty.c("Collision", { /**@ - * #.init + * #.ignoreHits * @comp Collision - * Create a rectangle polygon based on the x, y, w, h dimensions. + * @sign public this .ignoreHits() + * @sign public this .ignoreHits(String componentList) + * @param componentList - A comma seperated list of components to stop checking + * for collisions with. + * @sign public this .ignoreHits(String component1[, .., String componentN]) + * @param component# - A component to stop checking for collisions with. + * + * Stops checking for collisions with all, or certain, components. If called + * without arguments, this method will cause all collision checks on the + * entity to cease. To disable checks for collisions with specific + * components, specify the components as a comma separated string or as + * a set of arguments. + * + * Calling this method with component names for which there are no collision + * checks has no effect. * - * By default, the collision hitbox will match the dimensions (x, y, w, h) and rotation of the object. + * @example + * ~~~ + * Crafty.e("2D, Collision") + * .checkHits('Solid') + * ... + * .ignoreHits('Solid'); // stop checking for collisions with entities that have the Solid component + * ~~~ */ - init: function () { - this.requires("2D"); - var area = this._mbr || this; + ignoreHits: function () { + var components = arguments; + var i = 0; + var collisionData; - this.collision(); - }, + if (components.length === 0) { + for (collisionData in this._collisionData) { + this.unbind("EnterFrame", collisionData.handler); + } - /**@ - * #.collision - * @comp Collision - * - * @trigger NewHitbox - when a new hitbox is assigned - Crafty.polygon - * - * @sign public this .collision([Crafty.polygon polygon]) - * @param polygon - Crafty.polygon object that will act as the hit area - * - * @sign public this .collision(Array point1, .., Array pointN) - * @param point# - Array with an `x` and `y` position to generate a polygon - * - * Constructor takes a polygon or array of points to use as the hit area. - * - * The hit area (polygon) must be a convex shape and not concave - * for the collision detection to work. - * - * Points are relative to the object's position and its unrotated state. - * - * If no parameter is passed, the x, y, w, h properties of the entity will be used, and the hitbox will be resized when the entity is. - * - * @example - * ~~~ - * Crafty.e("2D, Collision").collision( - * new Crafty.polygon([50,0], [100,100], [0,100]) - * ); - * - * Crafty.e("2D, Collision").collision([50,0], [100,100], [0,100]); - * ~~~ - * - * @see Crafty.polygon - */ - collision: function (poly) { - this.unbind("Resize", this._resizeMap); - if (!poly) { - poly = new Crafty.polygon([0, 0], [this._w, 0], [this._w, this._h], [0, this._h]); - this.bind("Resize", this._resizeMap); - } - - if (arguments.length > 1) { - //convert args to array to create polygon - var args = Array.prototype.slice.call(arguments, 0); - poly = new Crafty.polygon(args); - } - - if (this.rotation) { - poly.rotate({ - cos: Math.cos(-this.rotation * DEG_TO_RAD), - sin: Math.sin(-this.rotation * DEG_TO_RAD), - o: { - x: this._origin.x, - y: this._origin.y - } - }); - } - - this.map = poly; - this.attach(this.map); - this.map.shift(this._x, this._y); - this.trigger("NewHitbox", poly); - return this; - }, - - - // Change the hitbox when a "Resize" event triggers. - _resizeMap: function (e) { - - var dx, dy, rot = this.rotation * DEG_TO_RAD, - points = this.map.points; - - // Depending on the change of axis, move the corners of the rectangle appropriately - if (e.axis === 'w') { - - if (rot) { - dx = e.amount * Math.cos(rot); - dy = e.amount * Math.sin(rot); - } else { - dx = e.amount; - dy = 0; - } + this._collisionData = {}; + } - // "top right" point shifts on change of w - points[1][0] += dx; - points[1][1] += dy; - } else { + if (components.length === 1) { + components = components[0].split(/\s*,\s*/); + } - if (rot) { - dy = e.amount * Math.cos(rot); - dx = -e.amount * Math.sin(rot); - } else { - dx = 0; - dy = e.amount; - } + for (; i < components.length; ++i) { + var component = components[i]; + collisionData = this._collisionData[component]; - // "bottom left" point shifts on change of h - points[3][0] += dx; - points[3][1] += dy; + if (collisionData === undefined) { + continue; } - // "bottom right" point shifts on either change - points[2][0] += dx; - points[2][1] += dy; + this.unbind("EnterFrame", collisionData.handler); + delete this._collisionData[component]; + } + return this; }, /**@ - * #.hit + * #.resetHitChecks * @comp Collision - * @sign public Boolean/Array hit(String component) - * @param component - Check collision with entities that has this component - * @return `false` if no collision. If a collision is detected, returns an Array of objects that are colliding. + * @sign public this .resetHitChecks() + * @sign public this .resetHitChecks(String componentList) + * @param componentList - A comma seperated list of components to re-check + * for collisions with. + * @sign public this .resetHitChecks(String component1[, .., String componentN]) + * @param component# - A component to re-check for collisions with. + * + * Causes collision events to be received for collisions that are already + * taking place (normally, an additional event would not fire before said + * collisions cease and happen another time). + * If called without arguments, this method will cause all collision checks on the + * entity to fire events once more. To re-check for collisions with specific + * components, specify the components as a comma separated string or as + * a set of arguments. + * + * Calling this method with component names for which there are no collision + * checks has no effect. * - * Takes an argument for a component to test collision for. If a collision is found, an array of - * every object in collision along with the amount of overlap is passed. - * - * If no collision, will return false. The return collision data will be an Array of Objects with the - * type of collision used, the object collided and if the type used was SAT (a polygon was used as the hitbox) then an amount of overlap.\ + * @example * ~~~ - * [{ - * obj: [entity], - * type "MBR" or "SAT", - * overlap: [number] - * }] + * // this example fires the HitOn event each frame the collision with the Solid entity is active, instead of just the first time the collision occurs. + * Crafty.e("2D, Collision") + * .checkHits('Solid') + * .bind("HitOn", function(hitData) { + * console.log("Collision with Solid entity was reported in this frame again!"); + * this.resetHitChecks('Solid'); // fire the HitOn event in the next frame also, if the collision is still active. + * }) * ~~~ - * `MBR` is your standard axis aligned rectangle intersection (`.intersect` in the 2D component). - * `SAT` is collision between any convex polygon. - * - * @see .onHit, 2D */ - hit: function (comp) { - var area = this._mbr || this, - results = Crafty.map.search(area, false), - i = 0, - l = results.length, - dupes = {}, - id, obj, oarea, key, - hasMap = ('map' in this && 'containsPoint' in this.map), - finalresult = []; - - if (!l) { - return false; - } - - for (; i < l; ++i) { - obj = results[i]; - oarea = obj._mbr || obj; //use the mbr - - if (!obj) continue; - id = obj[0]; + resetHitChecks: function() { + var components = arguments; + var i = 0; + var collisionData; - //check if not added to hash and that actually intersects - if (!dupes[id] && this[0] !== id && obj.__c[comp] && - oarea._x < area._x + area._w && oarea._x + oarea._w > area._x && - oarea._y < area._y + area._h && oarea._h + oarea._y > area._y) - dupes[id] = obj; + if (components.length === 0) { + for (collisionData in this._collisionData) { + this._collisionData[collisionData].occurring = false; } + } - for (key in dupes) { - obj = dupes[key]; + if (components.length === 1) { + components = components[0].split(/\s*,\s*/); + } - if (hasMap && 'map' in obj) { - var SAT = this._SAT(this.map, obj.map); - SAT.obj = obj; - SAT.type = "SAT"; - if (SAT) finalresult.push(SAT); - } else { - finalresult.push({ - obj: obj, - type: "MBR" - }); - } - } + for (; i < components.length; ++i) { + var component = components[i]; + collisionData = this._collisionData[component]; - if (!finalresult.length) { - return false; + if (collisionData === undefined) { + continue; } - return finalresult; - }, + collisionData.occurring = false; + } - /**@ - * #.onHit - * @comp Collision - * @sign public this .onHit(String component, Function hit[, Function noHit]) - * @param component - Component to check collisions for - * @param hit - Callback method to execute upon collision with component. Will be passed the results of the collision check in the same format documented for hit(). - * @param noHit - Callback method executed once as soon as collision stops - * - * Creates an EnterFrame event calling .hit() each frame. When a collision is detected the callback will be invoked. - * - * @see .hit - */ - onHit: function (comp, callback, callbackOff) { - var justHit = false; - this.bind("EnterFrame", function () { - var hitdata = this.hit(comp); - if (hitdata) { - justHit = true; - callback.call(this, hitdata); - } else if (justHit) { - if (typeof callbackOff == 'function') { - callbackOff.call(this); - } - justHit = false; - } - }); - return this; + return this; }, _SAT: function (poly1, poly2) { @@ -3803,8 +3629,6 @@ Crafty.c("Collision", { normal.y = -normal.y; } else { interval = min1 - max2; - - } //exit early if positive @@ -3829,7 +3653,241 @@ Crafty.c("Collision", { } }); -},{"./core.js":9}],8:[function(require,module,exports){ +},{"./core.js":10}],8:[function(require,module,exports){ +var Crafty = require('./core.js'), + document = window.document; + + + + +/**@ + * #Crafty.assignColor + * @category Graphics + * @sign Crafty.assignColor(color[, assignee]) + * @param color - a string represenation of the color to assign, in any valid HTML format + * @param assignee - an object to use instead of creating one from scratch + * @returns An object with `_red`, `_green`, and `_blue` properties assigned. + * Potentially with `_strength` representing the alpha channel. + * If the assignee parameter is passed, that object will be assigned those values and returned. + */ +Crafty.extend({ + assignColor: (function(){ + + // Create phantom element to assess color + var element = document.createElement("div"); + element.style.display = "none"; + // Can't attach it til later on, so we need a flag! + var element_attached = false; + var dictionary = { + "aqua": "#00ffff", + "black": "#000000", + "blue": "#0000ff", + "fuchsia": "#ff00ff", + "gray": "#808080", + "green": "#00ff00", + "lime": "#00ff00", + "maroon": "#800000", + "navy": "#000080", + "olive": "#808000", + "orange": "#ffa500", + "purple": "#800080", + "red": "#ff0000", + "silver": "#c0c0c0", + "teal": "#008080", + "white": "#ffffff", + "yellow": "#ffff00" + }; + + function default_value(c){ + c._red = c._blue = c._green = 0; + return c; + } + + function hexComponent(component) { + var hex = component.toString(16); + if (hex.length==1) + hex = "0" + hex; + return hex; + } + + function rgbToHex(r, g, b){ + return "#" + hexComponent(r) + hexComponent(g) + hexComponent(b); + } + + function parseHexString(hex, c) { + var l; + if (hex.length === 7){ + l=2; + } else if (hex.length === 4){ + l=1; + } else { + return default_value(c); + } + c._red = parseInt(hex.substr(1, l), 16); + c._green = parseInt(hex.substr(1+l, l), 16); + c._blue = parseInt(hex.substr(1+2*l, l), 16); + return c; + } + + var rgb_regex = /rgba?\s*\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,?\s*([0-9.]+)?\)/; + + function parseRgbString(rgb, c) { + var values = rgb_regex.exec(rgb); + if( values===null || (values.length != 4 && values.length != 5)) { + return default_value(c); // return bad result? + } + c._red = Math.round(parseFloat(values[1])); + c._green = Math.round(parseFloat(values[2])); + c._blue = Math.round(parseFloat(values[3])); + if (values[4]) { + c._strength = parseFloat(values[4]); + } + return c; + } + + function parseColorName(key, c){ + if (typeof dictionary[key] === "undefined"){ + if (element_attached === false){ + window.document.body.appendChild(element); + element_attached = true; + } + element.style.color = key; + var rgb = window.getComputedStyle(element).color; + parseRgbString(rgb, c); + dictionary[key] = rgbToHex(c._red, c._green, c._blue); + //window.document.body.removeChild(element); + } else { + parseHexString(dictionary[key], c); + } + return c; + } + + function rgbaString(c){ + return "rgba(" + c._red + ", " + c._green + ", " + c._blue + ", " + c._strength + ")"; + } + + // The actual assignColor function + return function(color, c){ + c = c || {}; + color = color.trim().toLowerCase(); + var ret = null; + if (color[0] === '#'){ + ret = parseHexString(color, c); + } else if (color[0] === 'r' && color[1] === 'g' && color[2] === 'b'){ + ret = parseRgbString(color, c); + } else { + ret = parseColorName(color, c); + } + c._strength = c._strength || 1.0; + c._color = rgbaString(c); + }; + + })() +}); + + + + + + +/**@ + * #Color + * @category Graphics + * Draw a colored rectangle. + */ +Crafty.c("Color", { + _red: 0, + _green: 0, + _blue: 0, + _strength: 1.0, + _color: "", + ready: true, + + init: function () { + this.bind("Draw", this._drawColor); + this.trigger("Invalidate"); + }, + + remove: function(){ + this.unbind("Draw", this._drawColor); + if (this.has("DOM")){ + this._element.style.backgroundColor = "transparent"; + } + this.trigger("Invalidate"); + }, + + // draw function for "Color" + _drawColor: function(e){ + if (!this._color) { return; } + if (e.type === "DOM") { + e.style.backgroundColor = this._color; + e.style.lineHeight = 0; + } else if (e.type === "canvas") { + e.ctx.fillStyle = this._color; + e.ctx.fillRect(e.pos._x, e.pos._y, e.pos._w, e.pos._h); + } + }, + + /**@ + * #.color + * @comp Color + * @trigger Invalidate - when the color changes + * + * Will assign the color and opacity, either through a string shorthand, or through explicit rgb values. + * @sign public this .color(String color[, Float strength]) + * @param color - Color of the rectangle + * @param strength - the opacity of the rectangle + * + * @sign public this .color(r, g, b[, strength]) + * @param r - value for the red channel + * @param g - value for the green channel + * @param b - value for the blue channel + * @param strength - the opacity of the rectangle + * + * @sign public String .color() + * @return A string representing the current color as a CSS property. + * + * @example + * ``` + * var c = Crafty.e("2D, DOM, Color"); + * c.color("#FF0000"); + * c.color("red"); + * c.color(255, 0, 0); + * c.color("rgb(255, 0, 0") + * ``` + * Three different ways of assign the color red. + * ``` + * var c = Crafty.e("2D, DOM, Color"); + * c.color("#00FF00", 0.5); + * c.color("rgba(0, 255, 0, 0.5)"); + * ``` + * Two ways of assigning a transparent green color. + */ + color: function (color) { + if (arguments.length === 0 ){ + return this._color; + } else if (arguments.length>=3){ + this._red = arguments[0]; + this._green = arguments[1]; + this._blue = arguments[2]; + if (typeof arguments[3] === "number") + this._strength = arguments[3]; + } else { + // First argument is color name + Crafty.assignColor(color, this); + // Second argument, if present, is strength of color + // Note that assignColor will give a default strength of 1.0 if none exists. + if (typeof arguments[1] == "number") + this._strength = arguments[1]; + } + this._color = "rgba(" + this._red + ", " + this._green + ", " + this._blue + ", " + this._strength + ")"; + this.trigger("Invalidate"); + return this; + } +}); + + +},{"./core.js":10}],9:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -3859,18 +3917,34 @@ Crafty.extend({ * ~~~ * @see Keyboard, Crafty.keys */ - detectBlur: function (e) { var selected = ((e.clientX > Crafty.stage.x && e.clientX < Crafty.stage.x + Crafty.viewport.width) && (e.clientY > Crafty.stage.y && e.clientY < Crafty.stage.y + Crafty.viewport.height)); - if (!Crafty.selected && selected) + if (!Crafty.selected && selected) { Crafty.trigger("CraftyFocus"); - if (Crafty.selected && !selected) + } + + if (Crafty.selected && !selected) { Crafty.trigger("CraftyBlur"); - + } + Crafty.selected = selected; }, + + resetKeyDown: function() { + // Tell all the keys they're no longer held down + for (var k in Crafty.keys) { + if (Crafty.keydown[Crafty.keys[k]]) { + this.trigger("KeyUp", { + key: Crafty.keys[k] + }); + } + } + + Crafty.keydown = {}; + }, + /**@ * #Crafty.mouseDispatch * @category Input @@ -3878,6 +3952,9 @@ Crafty.extend({ * Internal method which dispatches mouse events received by Crafty (crafty.stage.elem). * The mouse events get dispatched to the closest entity to the source of the event (if available). * + * You can read more about the MouseEvent, which is the parameter passed to the callback. + * https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent + * * This method also sets a global property Crafty.lastEvent, which holds the most recent event that * occured (useful for determining mouse position in every frame). * ~~~ @@ -3887,9 +3964,12 @@ Crafty.extend({ * * Notable properties of a MouseEvent e: * ~~~ - * e.clientX, e.clientY //(x,y) coordinates of mouse event in web browser screen space - * e.realX, e.realY //(x,y) coordinates of mouse event in world/viewport space - * e.mouseButton // Normalized mouse button according to Crafty.mouseButtons + * //(x,y) coordinates of mouse event in web browser screen space + * e.clientX, e.clientY + * //(x,y) coordinates of mouse event in world/viewport space + * e.realX, e.realY + * // Normalized mouse button according to Crafty.mouseButtons + * e.mouseButton * ~~~ * @see Crafty.touchDispatch */ @@ -4013,7 +4093,7 @@ Crafty.extend({ * * TouchEvents have a different structure then MouseEvents. * The relevant data lives in e.changedTouches[0]. - * To normalize TouchEvents we catch em and dispatch a mock MouseEvent instead. + * To normalize TouchEvents we catch them and dispatch a mock MouseEvent instead. * * @see Crafty.mouseDispatch */ @@ -4060,8 +4140,14 @@ Crafty.extend({ first.target.dispatchEvent(simulatedEvent); } - if (e.preventDefault) e.preventDefault(); - else e.returnValue = false; + //Don't prevent default actions if target node is input or textarea. + if (e.target && e.target.nodeName !== 'INPUT' && e.target.nodeName !== 'TEXTAREA') { + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + } }, @@ -4078,14 +4164,14 @@ Crafty.extend({ * .attr({x: 100, y: 100, w: 50, h: 50}) * .color("red") * .bind('KeyDown', function(e) { - * if(e.key == Crafty.keys['LEFT_ARROW']) { - * this.x=this.x-1; - * } else if (e.key == Crafty.keys['RIGHT_ARROW']) { - * this.x=this.x+1; - * } else if (e.key == Crafty.keys['UP_ARROW']) { - * this.y=this.y-1; - * } else if (e.key == Crafty.keys['DOWN_ARROW']) { - * this.y=this.y+1; + * if(e.key == Crafty.keys.LEFT_ARROW) { + * this.x = this.x-1; + * } else if (e.key == Crafty.keys.RIGHT_ARROW) { + * this.x = this.x+1; + * } else if (e.key == Crafty.keys.UP_ARROW) { + * this.y = this.y-1; + * } else if (e.key == Crafty.keys.DOWN_ARROW) { + * this.y = this.y+1; * } * }); * ~~~ @@ -4159,6 +4245,7 @@ Crafty.bind("Load", function () { Crafty.addEvent(this, Crafty.stage.elem, "mousedown", Crafty.mouseDispatch); Crafty.addEvent(this, Crafty.stage.elem, "mouseup", Crafty.mouseDispatch); Crafty.addEvent(this, document.body, "mouseup", Crafty.detectBlur); + Crafty.addEvent(this, window, "blur", Crafty.resetKeyDown); Crafty.addEvent(this, Crafty.stage.elem, "mousemove", Crafty.mouseDispatch); Crafty.addEvent(this, Crafty.stage.elem, "click", Crafty.mouseDispatch); Crafty.addEvent(this, Crafty.stage.elem, "dblclick", Crafty.mouseDispatch); @@ -4189,32 +4276,41 @@ Crafty.bind("CraftyStop", function () { } Crafty.removeEvent(this, document.body, "mouseup", Crafty.detectBlur); + Crafty.removeEvent(this, window, "blur", Crafty.resetKeyDown); }); /**@ * #Mouse * @category Input * Provides the entity with mouse related events - * @trigger MouseOver - when the mouse enters the entity - MouseEvent - * @trigger MouseOut - when the mouse leaves the entity - MouseEvent - * @trigger MouseDown - when the mouse button is pressed on the entity - MouseEvent - * @trigger MouseUp - when the mouse button is released on the entity - MouseEvent - * @trigger Click - when the user clicks the entity. [See documentation](http://www.quirksmode.org/dom/events/click.html) - MouseEvent - * @trigger DoubleClick - when the user double clicks the entity - MouseEvent - * @trigger MouseMove - when the mouse is over the entity and moves - MouseEvent - * Crafty adds the mouseButton property to MouseEvents that match one of + * @trigger MouseOver - when the mouse enters - MouseEvent + * @trigger MouseOut - when the mouse leaves - MouseEvent + * @trigger MouseDown - when the mouse button is pressed on - MouseEvent + * @trigger MouseUp - when the mouse button is released on - MouseEvent + * @trigger Click - when the user clicks - MouseEvent + * @trigger DoubleClick - when the user double clicks - MouseEvent + * @trigger MouseMove - when the mouse is over and moves - MouseEvent + * + * To be able to use the events on a entity, you have to remember to include the Mouse component, else the events will not get triggered. + * + * You can read more about the MouseEvent, which is the parameter passed to the callback. + * https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent + * + * Crafty adds the mouseButton property to MouseEvents that match one of * - * ~~~ * - Crafty.mouseButtons.LEFT * - Crafty.mouseButtons.RIGHT * - Crafty.mouseButtons.MIDDLE - * ~~~ + * * * @example * ~~~ - * myEntity.bind('Click', function() { - * console.log("Clicked!!"); - * }) + * var myEntity = Crafty.e('2D, Canvas, Color, Mouse') + * .attr({x: 10, y: 10, w: 40, h: 40}) + * .color('red') + * .bind('Click', function(MouseEvent){ + * alert('clicked', MouseEvent); + * }); * * myEntity.bind('MouseUp', function(e) { * if( e.mouseButton == Crafty.mouseButtons.RIGHT ) @@ -4320,7 +4416,7 @@ Crafty.c("Draggable", { _onup: function (e) { // While a drag is occurring, this method is bound to mouseup DOM event - if (this._dragging === true) { + if (e.mouseButton === Crafty.mouseButtons.LEFT && this._dragging === true) { Crafty.removeEvent(this, Crafty.stage.elem, "mousemove", this._ondrag); Crafty.removeEvent(this, Crafty.stage.elem, "mouseup", this._onup); this._dragging = false; @@ -4637,6 +4733,19 @@ Crafty.c("Multiway", { return this; }, + /**@ + * #.speed + * @comp Multiway + * @sign public this .speed(Number speed) + * @param speed - The speed the entity has. + * + * Change the speed that the entity moves with. + * + * @example + * ~~~ + * this.speed(2); + * ~~~ + */ speed: function (speed) { for (var k in this._keyDirection) { var keyCode = Crafty.keys[k] || k; @@ -4697,17 +4806,17 @@ Crafty.c("Fourway", { /**@ * #Twoway * @category Input + * @trigger NewDirection - When direction changes a NewDirection event is triggered with an object detailing the new direction: {x: x_movement, y: y_movement}. This is consistent with Fourway and Multiway components. + * @trigger Moved - When entity has moved on x-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y} + * * Move an entity left or right using the arrow keys or `D` and `A` and jump using up arrow or `W`. - * - * When direction changes a NewDirection event is triggered with an object detailing the new direction: {x: x_movement, y: y_movement}. This is consistent with Fourway and Multiway components. - * When entity has moved on x-axis a Moved event is triggered with an object specifying the old position {x: old_x, y: old_y} */ Crafty.c("Twoway", { _speed: 3, _up: false, init: function () { - this.requires("Fourway, Keyboard"); + this.requires("Fourway, Keyboard, Gravity"); }, /**@ @@ -4719,10 +4828,8 @@ Crafty.c("Twoway", { * * Constructor to initialize the speed and power of jump. Component will * listen for key events and move the entity appropriately. This includes - * ~~~ - * `Up Arrow`, `Right Arrow`, `Left Arrow` as well as W, A, D. Used with the + * `Up Arrow`, `Right Arrow`, `Left Arrow` as well as `W`, `A`, `D`. Used with the * `gravity` component to simulate jumping. - * ~~~ * * The key presses will move the entity in that direction by the speed passed in * the argument. Pressing the `Up Arrow` or `W` will cause the entity to jump. @@ -4751,9 +4858,10 @@ Crafty.c("Twoway", { if (this._up) { this.y -= this._jumpSpeed; this._falling = true; + this.trigger('Moved', { x: this._x, y: this._y + this._jumpSpeed }); } }).bind("KeyDown", function (e) { - if (e.key === Crafty.keys.UP_ARROW || e.key === Crafty.keys.W || e.key === Crafty.keys.Z) + if (!this._falling && (e.key === Crafty.keys.UP_ARROW || e.key === Crafty.keys.W || e.key === Crafty.keys.Z)) this._up = true; }); @@ -4761,7 +4869,7 @@ Crafty.c("Twoway", { } }); -},{"./core.js":9}],9:[function(require,module,exports){ +},{"./core.js":10}],10:[function(require,module,exports){ var version = require('./version'); /**@ @@ -4791,18 +4899,27 @@ var version = require('./version'); * Crafty(1) * ~~~ * Passing an integer will select the entity with that `ID`. + * + * To work directly with an array of entities, use the `get()` method on a selection. + * To call a function in the context of each entity, use the `.each()` method. + * + * The event related methods such as `bind` and `trigger` will work on selections of entities. + * + * @see .get + * @see .each */ - + var Crafty = function (selector) { return new Crafty.fn.init(selector); }, // Internal variables GUID, frame, components, entities, handlers, onloads, - noSetter, slice, rlist, rspace, milliSecPerFrame; + slice, rlist, rspace, milliSecPerFrame; initState = function () { GUID = 1; //GUID for entity IDs + frame = 0; components = {}; //map of components and their functions entities = {}; //map of entities and their data @@ -4975,45 +5092,35 @@ Crafty.fn = Crafty.prototype = { * ~~~ */ addComponent: function (id) { - var uninit = [], - c = 0, - ul, //array of components to init - i = 0, - l, comps, comp; + var comps = [], + c = 0; //add multiple arguments if (arguments.length > 1) { - l = arguments.length; - for (; i < l; i++) { - uninit.push(arguments[i]); + var i = 0; + for (; i < arguments.length; i++) { + comps.push(arguments[i]); } //split components if contains comma } else if (id.indexOf(',') !== -1) { comps = id.split(rlist); - l = comps.length; - for (; i < l; i++) { - uninit.push(comps[i]); - } - //single component passed } else { - uninit.push(id); + comps.push(id); } //extend the components - ul = uninit.length; - for (; c < ul; c++) { - if (this.__c[uninit[c]] === true) + for (; c < comps.length; c++) { + if (this.__c[comps[c]] === true) continue; - this.__c[uninit[c]] = true; - comp = components[uninit[c]]; - this.extend(comp); + this.__c[comps[c]] = true; + this.extend(components[comps[c]]); //if constructor, call it - if (comp && "init" in comp) { - comp.init.call(this); + if (components[comps[c]] && "init" in components[comps[c]]) { + components[comps[c]].init.call(this); } } - this.trigger("NewComponent", uninit); + this.trigger("NewComponent", comps); return this; }, @@ -5171,52 +5278,166 @@ Crafty.fn = Crafty.prototype = { /**@ * #.attr * @comp Crafty Core - * @sign public this .attr(String property, * value) - * @param property - Property of the entity to modify - * @param value - Value to set the property to - * @sign public this .attr(Object map) - * @param map - Object where the key is the property to modify and the value as the property value * @trigger Change - when properties change - {key: value} * + * @sign public this .attr(String property, Any value[, Boolean silent[, Boolean recursive]]) + * @param property - Property of the entity to modify + * @param value - Value to set the property to + * @param silent - If you would like to supress events + * @param recursive - If you would like merge recursively * Use this method to set any property of the entity. * + * @sign public this .attr(Object map[, Boolean silent[, Boolean recursive]]) + * @param map - Object where each key is the property to modify and the value as the property value + * @param silent - If you would like to supress events + * @param recursive - If you would like merge recursively + * Use this method to set multiple properties of the entity. + * + * Setter options: + * `silent`: If you want to prevent it from firing events. + * `recursive`: If you pass in an object you could overwrite sibling keys, this recursively merges instead of just merging it. This is `false` by default, unless you are using dot notation `name.first`. + * + * @sign public Any .attr(String property) + * @param property - Property of the entity to modify + * @returns Value - the value of the property + * Use this method to get any property of the entity. You can also retrieve the property using `this.property`. + * + * * @example * ~~~ * this.attr({key: "value", prop: 5}); - * this.key; //value - * this.prop; //5 + * this.attr("key"); // returns "value" + * this.attr("prop"); // returns 5 + * this.key; // "value" + * this.prop; // 5 * * this.attr("key", "newvalue"); - * this.key; //newvalue + * this.attr("key"); // returns "newvalue" + * this.key; // "newvalue" + * + * this.attr("parent.child", "newvalue"); + * this.parent; // {child: "newvalue"}; + * this.attr('parent.child'); // "newvalue" * ~~~ */ - attr: function (key, value) { - if (arguments.length === 1) { - //if just the key, return the value - if (typeof key === "string") { - return this[key]; - } + attr: function (key, value, silent, recursive) { + if (arguments.length === 1 && typeof arguments[0] === 'string') { + return this._attr_get(key); + } else { + return this._attr_set(key, value, silent, recursive); + } + }, - //extend if object - this.extend(key); - this.trigger("Change", key); //trigger change event - return this; + /** + * Internal getter method for data on the entity. Called by `.attr`. + * + * example + * ~~~ + * person._attr_get('name'); // Foxxy + * person._attr_get('contact'); // {email: 'fox_at_example.com'} + * person._attr_get('contact.email'); // fox_at_example.com + * ~~~ + */ + _attr_get: function(key, context) { + var first, keys, subkey; + if (typeof context === "undefined" || context === null) { + context = this; + } + if (key.indexOf('.') > -1) { + keys = key.split('.'); + first = keys.shift(); + subkey = keys.join('.'); + return this._attr_get(keys.join('.'), context[first]); + } else { + return context[key]; + } + }, + + /** + * Internal setter method for attributes on the component. Called by `.attr`. + * + * Options: + * + * `silent`: If you want to prevent it from firing events. + * + * `recursive`: If you pass in an object you could overwrite + * sibling keys, this recursively merges instead of just + * merging it. This is `false` by default, unless you are + * using dot notation `name.first`. + * + * example + * ~~~ + * person._attr_set('name', 'Foxxy', true); + * person._attr_set('name', 'Foxxy'); + * person._attr_set({name: 'Foxxy'}, true); + * person._attr_set({name: 'Foxxy'}); + * person._attr_set('name.first', 'Foxxy'); + * ~~~ + */ + _attr_set: function() { + var data, silent, recursive; + if (typeof arguments[0] === 'string') { + data = this._set_create_object(arguments[0], arguments[1]); + silent = !!arguments[2]; + recursive = arguments[3] || arguments[0].indexOf('.') > -1; + } else { + data = arguments[0]; + silent = !!arguments[1]; + recursive = !!arguments[2]; } - //if key value pair - this[key] = value; - var change = {}; - change[key] = value; - this.trigger("Change", change); //trigger change event + if (!silent) { + this.trigger('Change', data); + } + + if (recursive) { + this._recursive_extend(data, this); + } else { + this.extend.call(this, data); + } return this; }, + /** + * If you are setting a key of 'foo.bar' or 'bar', this creates + * the appropriate object for you to recursively merge with the + * current attributes. + */ + _set_create_object: function(key, value) { + var data = {}, keys, first, subkey; + if (key.indexOf('.') > -1) { + keys = key.split('.'); + first = keys.shift(); + subkey = keys.join('.'); + data[first] = this._set_create_object(subkey, value); + } else { + data[key] = value; + } + return data; + }, + + /** + * Recursively puts `new_data` into `original_data`. + */ + _recursive_extend: function(new_data, original_data) { + var key; + for (key in new_data) { + if (new_data[key].constructor.name === 'Object') { + original_data[key] = this._recursive_extend(new_data[key], original_data[key]); + } else { + original_data[key] = new_data[key]; + } + } + return original_data; + }, + /**@ * #.toArray * @comp Crafty Core * @sign public this .toArray(void) * - * This method will simply return the found entities as an array. + * This method will simply return the found entities as an array of ids. To get an array of the actual entities, use `get()`. + * @see .get */ toArray: function () { return slice.call(this, 0); @@ -5228,7 +5449,7 @@ Crafty.fn = Crafty.prototype = { * @sign public this .timeout(Function callback, Number delay) * @param callback - Method to execute after given amount of milliseconds * @param delay - Amount of milliseconds to execute the method - * + * * The delay method will execute a function after a given amount of time in milliseconds. * * Essentially a wrapper for `setTimeout`. @@ -5475,6 +5696,53 @@ Crafty.fn = Crafty.prototype = { return this; }, + /**@ + * #.get + * @comp Crafty Core + * @sign public Array .get() + * @returns An array of entities corresponding to the active selector + * + * @sign public Entity .get(Number index) + * @returns an entity belonging to the current selection + * @param index - The index of the entity to return. If negative, counts back from the end of the array. + * + * + * @example + * Get an array containing every "2D" entity + * ~~~ + * var arr = Crafty("2D").get() + * ~~~ + * Get the first entity matching the selector + * ~~~ + * // equivalent to Crafty("2D").get()[0], but doesn't create a new array + * var e = Crafty("2D").get(0) + * ~~~ + * Get the last "2D" entity matching the selector + * ~~~ + * var e = Crafty("2D").get(-1) + * ~~~ + * + */ + get: function(index) { + var l = this.length; + if (typeof index !== "undefined") { + if (index >= l || index+l < 0) + return undefined; + if (index>=0) + return entities[this[index]]; + else + return entities[this[index+l]]; + } else { + var i=0, result = []; + for (; i < l; i++) { + //skip if not exists + if (!entities[this[i]]) continue; + result.push( entities[this[i]] ); + } + return result; + } + }, + /**@ * #.clone * @comp Crafty Core @@ -5511,8 +5779,6 @@ Crafty.fn = Crafty.prototype = { * Will watch a property waiting for modification and will then invoke the * given callback when attempting to modify. * - * *Note: Support in IE<9 is slightly different. The method will be executed - * after the property has been set* */ setter: function (prop, callback) { if (Crafty.support.setter) { @@ -5522,12 +5788,6 @@ Crafty.fn = Crafty.prototype = { set: callback, configurable: true }); - } else { - noSetter.push({ - prop: prop, - obj: this, - fn: callback - }); } return this; }, @@ -5559,9 +5819,12 @@ Crafty.fn = Crafty.prototype = { //give the init instances the Crafty prototype Crafty.fn.init.prototype = Crafty.fn; -/** - * Extension method to extend the namespace and - * selector instances + +/**@ + * #Crafty.extend + * @category Core + * Used to extend the Crafty namespace. + * */ Crafty.extend = Crafty.fn.extend = function (obj) { var target = this, @@ -5578,11 +5841,7 @@ Crafty.extend = Crafty.fn.extend = function (obj) { return target; }; -/**@ - * #Crafty.extend - * @category Core - * Used to extend the Crafty namespace. - */ + Crafty.extend({ /**@ * #Crafty.init @@ -5669,7 +5928,7 @@ Crafty.extend({ * @sign public this Crafty.pause(void) * * Pauses the game by stopping the EnterFrame event from firing. If the game is already paused it is unpaused. - * You can pass a boolean parameter if you want to pause or unpause mo matter what the current state is. + * You can pass a boolean parameter if you want to pause or unpause no matter what the current state is. * Modern browsers pauses the game when the page is not visible to the user. If you want the Pause event * to be triggered when that happens you can enable autoPause in `Crafty.settings`. * @@ -5736,8 +5995,7 @@ Crafty.extend({ // variables used by the game loop to track state var endTime = 0, timeSlip = 0, - gameTime, - frame = 0; + gameTime; // Controls the target rate of fixed mode loop. Set these with the Crafty.timer.FPS function var FPS = 50, @@ -5778,7 +6036,8 @@ Crafty.extend({ if (typeof tick === "number") clearInterval(tick); - var onFrame = window.cancelRequestAnimationFrame || + var onFrame = window.cancelAnimationFrame || + window.cancelRequestAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || @@ -5793,8 +6052,8 @@ Crafty.extend({ /**@ * #Crafty.timer.steptype * @comp Crafty.timer - * Can be called to set the type of timestep the game loop uses * @sign public void Crafty.timer.steptype(mode [, maxTimeStep]) + * Can be called to set the type of timestep the game loop uses * @param mode - the type of time loop. Allowed values are "fixed", "semifixed", and "variable". Crafty defaults to "fixed". * @param mode - For "fixed", sets the max number of frames per step. For "variable" and "semifixed", sets the maximum time step allowed. * @@ -5826,11 +6085,18 @@ Crafty.extend({ * @comp Crafty.timer * @sign public void Crafty.timer.step() * @trigger EnterFrame - Triggered on each frame. Passes the frame number, and the amount of time since the last frame. If the time is greater than maxTimestep, that will be used instead. (The default value of maxTimestep is 50 ms.) - { frame: Number, dt:Number } + * @trigger ExitFrame - Triggered after each frame. Passes the frame number, and the amount of time since the last frame. If the time is greater than maxTimestep, that will be used instead. (The default value of maxTimestep is 50 ms.) - { frame: Number, dt:Number } + * @trigger PreRender - Triggered every time immediately before a scene should be rendered * @trigger RenderScene - Triggered every time a scene should be rendered + * @trigger PostRender - Triggered every time immediately after a scene should be rendered * @trigger MeasureWaitTime - Triggered at the beginning of each step after the first. Passes the time the game loop waited between steps. - Number - * @trigger MeasureFrameTime - Triggered after each step. Passes the time it took to advance one frame. - Number + * @trigger MeasureFrameTime - Triggered after each frame. Passes the time it took to advance one frame. - Number * @trigger MeasureRenderTime - Triggered after each render. Passes the time it took to render the scene - Number - * Advances the game by triggering `EnterFrame` and `RenderScene` + * + * Advances the game by performing a step. A step consists of one/multiple frames followed by a render. The amount of frames depends on the timer's steptype. + * Specifically it triggers `EnterFrame` & `ExitFrame` events for each frame and `PreRender`, `RenderScene` & `PostRender` events for each render. + * + * @see Crafty.timer.steptype */ step: function () { var drawTimeStart, dt, lastFrameTime, loops = 0; @@ -5874,13 +6140,18 @@ Crafty.extend({ // dt is determined by the mode for (var i = 0; i < loops; i++) { lastFrameTime = currentTime; - // Everything that changes over time hooks into this event - Crafty.trigger("EnterFrame", { + + var frameData = { frame: frame++, dt: dt, gameTime: gameTime - }); + }; + // Everything that changes over time hooks into this event + Crafty.trigger("EnterFrame", frameData); + // Event that happens after "EnterFrame", e.g. for resolivng collisions applied through movement during "EnterFrame" events + Crafty.trigger("ExitFrame", frameData); gameTime += dt; + currentTime = new Date().getTime(); Crafty.trigger("MeasureFrameTime", currentTime - lastFrameTime); } @@ -5888,9 +6159,9 @@ Crafty.extend({ //If any frames were processed, render the results if (loops > 0) { drawTimeStart = currentTime; + Crafty.trigger("PreRender"); // Pre-render setup opportunity Crafty.trigger("RenderScene"); - // Post-render cleanup opportunity - Crafty.trigger("PostRender"); + Crafty.trigger("PostRender"); // Post-render cleanup opportunity currentTime = new Date().getTime(); Crafty.trigger("MeasureRenderTime", currentTime - drawTimeStart); } @@ -5919,8 +6190,8 @@ Crafty.extend({ /**@ * #Crafty.timer.simulateFrames * @comp Crafty.timer - * Advances the game state by a number of frames and draws the resulting stage at the end. Useful for tests and debugging. * @sign public this Crafty.timer.simulateFrames(Number frames[, Number timestep]) + * Advances the game state by a number of frames and draws the resulting stage at the end. Useful for tests and debugging. * @param frames - number of frames to simulate * @param timestep - the duration to pass each frame. Defaults to milliSecPerFrame (20 ms) if not specified. */ @@ -5928,12 +6199,16 @@ Crafty.extend({ if (typeof timestep === "undefined") timestep = milliSecPerFrame; while (frames-- > 0) { - Crafty.trigger("EnterFrame", { + var frameData = { frame: frame++, dt: timestep - }); + }; + Crafty.trigger("EnterFrame", frameData); + Crafty.trigger("ExitFrame", frameData); } + Crafty.trigger("PreRender"); Crafty.trigger("RenderScene"); + Crafty.trigger("PostRender"); } }; })(), @@ -5962,23 +6237,21 @@ Crafty.extend({ * @see Crafty.c */ e: function () { - var id = UID(), - craft; - - entities[id] = null; //register the space - entities[id] = craft = Crafty(id); + var id = UID(); + entities[id] = null; + entities[id] = Crafty(id); if (arguments.length > 0) { - craft.addComponent.apply(craft, arguments); + entities[id].addComponent.apply(entities[id], arguments); } - craft.setName('Entity #' + id); //set default entity human readable name - craft.addComponent("obj"); //every entity automatically assumes obj + entities[id].setName('Entity #' + id); //set default entity human readable name + entities[id].addComponent("obj"); //every entity automatically assumes obj Crafty.trigger("NewEntity", { id: id }); - return craft; + return entities[id]; }, /**@ @@ -5986,10 +6259,16 @@ Crafty.extend({ * @category Core * @sign public void Crafty.c(String name, Object component) * @param name - Name of the component - * @param component - Object with the components properties and methods + * @param component - Object with the component's properties and methods * Creates a component where the first argument is the ID and the second * is the object that will be inherited by entities. * + * Specifically, each time a component is added to an entity, the component properties are copied over to the entity. + * * In the case of primitive datatypes (booleans, numbers, strings) the property is copied by value. + * * In the case of complex datatypes (objects, arrays, functions) the property is copied by reference and will thus reference the components' original property. + * * (See the two examples below for further explanation) + * Note that when a component method gets called, the `this` keyword will refer to the current entity the component was added to. + * * A couple of methods are treated specially. They are invoked in partiular contexts, and (in those contexts) cannot be overridden by other components. * * - `init` will be called when the component is added to an entity @@ -6083,7 +6362,7 @@ Crafty.extend({ * @sign public Number bind(String eventName, Function callback) * @param eventName - Name of the event to bind to * @param callback - Method to execute upon event triggered - * @returns ID of the current callback used to unbind + * @returns callback function which can be used for unbind * * Binds to a global event. Method will be executed when `Crafty.trigger` is used * with the event name. @@ -6103,7 +6382,7 @@ Crafty.extend({ // entity6.trigger('Move')), it causes the execution of fnB() and fnC(). When // the Move event is triggered globally (i.e. Crafty.trigger('Move')), it // will execute fnA, fnB, fnC, fnD. - // + // // In this example, "this" is bound to entity #6 whenever fnB() is executed, and // "this" is bound to Crafty whenever fnD() is executed. // @@ -6115,7 +6394,8 @@ Crafty.extend({ var hdl = handlers[event]; if (!hdl.global) hdl.global = []; - return hdl.global.push(callback) - 1; + hdl.global.push(callback); + return callback; }, @@ -6125,7 +6405,7 @@ Crafty.extend({ * @sign public Number uniqueBind(String eventName, Function callback) * @param eventName - Name of the event to bind to * @param callback - Method to execute upon event triggered - * @returns ID of the current callback used to unbind + * @returns callback function which can be used for unbind * * Works like Crafty.bind, but prevents a callback from being bound multiple times. * @@ -6133,8 +6413,7 @@ Crafty.extend({ */ uniqueBind: function (event, callback) { this.unbind(event, callback); - this.bind(event, callback); - + return this.bind(event, callback); }, /**@ @@ -6143,7 +6422,7 @@ Crafty.extend({ * @sign public Number one(String eventName, Function callback) * @param eventName - Name of the event to bind to * @param callback - Method to execute upon event triggered - * @returns ID of the current callback used to unbind + * @returns callback function which can be used for unbind * * Works like Crafty.bind, but will be unbound once the event triggers. * @@ -6156,7 +6435,6 @@ Crafty.extend({ self.unbind(event, oneHandler); }; return self.bind(event, oneHandler); - }, /**@ @@ -6337,37 +6615,49 @@ function clone(obj) { return temp; } -Crafty.bind("Load", function () { - if (!Crafty.support.setter && Crafty.support.defineProperty) { - noSetter = []; - Crafty.bind("EnterFrame", function () { - var i = 0, - l = noSetter.length, - current; - for (; i < l; ++i) { - current = noSetter[i]; - if (current.obj[current.prop] !== current.obj['_' + current.prop]) { - current.fn.call(current.obj, current.obj[current.prop]); - } - } - }); - } -}); - - // export Crafty if (typeof define === 'function') { // AMD define('crafty', [], function () { return Crafty; }); -} else if (typeof exports === 'object') { // CommonJS - module.exports = Crafty; } -window.Crafty = Crafty; - +module.exports = Crafty; + +},{"./version":30}],11:[function(require,module,exports){ +var Crafty = require('./core'); +require('./2D'); +require('./animation'); +require('./canvas'); +require('./collision'); +require('./color'); +require('./controls'); +require('./DebugLayer'); +require('./device'); +require('./diamondiso'); +require('./DOM'); +require('./drawing'); +require('./extensions'); +require('./HashMap'); +require('./html'); +require('./isometric'); +require('./keycodes'); +require('./loader'); +require('./math'); +require('./model'); +require('./particles'); +require('./scenes'); +require('./sound'); +require('./sprite-animation'); +require('./sprite'); +require('./storage'); +require('./text'); +require('./time'); +require('./version'); +require('./viewport'); -},{"./version":25}],10:[function(require,module,exports){ +window.Crafty = Crafty; +},{"./2D":1,"./DOM":2,"./DebugLayer":3,"./HashMap":4,"./animation":5,"./canvas":6,"./collision":7,"./color":8,"./controls":9,"./core":10,"./device":12,"./diamondiso":13,"./drawing":14,"./extensions":15,"./html":16,"./isometric":17,"./keycodes":18,"./loader":19,"./math":20,"./model":21,"./particles":22,"./scenes":23,"./sound":24,"./sprite":26,"./sprite-animation":25,"./storage":27,"./text":28,"./time":29,"./version":30,"./viewport":31}],12:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -6526,7 +6816,7 @@ Crafty.extend({ } }); -},{"./core.js":9}],11:[function(require,module,exports){ +},{"./core.js":10}],13:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -6628,21 +6918,25 @@ Crafty.extend({ area: function (offset) { if (!offset) offset = 0; //calculate the corners - var vp = Crafty.viewport.rect(); + var vp = Crafty.viewport.rect(), + x = vp._x, + y = vp._y, + w = vp._w, + h = vp._h; var ow = offset * this._tile.width; var oh = offset * this._tile.height; - vp._x -= (this._tile.width / 2 + ow); - vp._y -= (this._tile.height / 2 + oh); - vp._w += (this._tile.width / 2 + ow); - vp._h += (this._tile.height / 2 + oh); - /* Crafty.viewport.x = -vp._x; - Crafty.viewport.y = -vp._y; - Crafty.viewport.width = vp._w; - Crafty.viewport.height = vp._h; */ + x -= (this._tile.width / 2 + ow); + y -= (this._tile.height / 2 + oh); + w += (this._tile.width / 2 + ow); + h += (this._tile.height / 2 + oh); + /* Crafty.viewport.x = -x; + Crafty.viewport.y = -y; + Crafty.viewport.width = w; + Crafty.viewport.height = h; */ var grid = []; - for (var y = vp._y, yl = (vp._y + vp._h); y < yl; y += this._tile.height / 2) { - for (var x = vp._x, xl = (vp._x + vp._w); x < xl; x += this._tile.width / 2) { + for (yl = (y + h); y < yl; y += this._tile.height / 2) { + for (xl = (x + w); x < xl; x += this._tile.width / 2) { var row = this.px2pos(x, y); grid.push([~~row.x, ~~row.y]); } @@ -6685,105 +6979,10 @@ Crafty.extend({ } }); -},{"./core.js":9}],12:[function(require,module,exports){ +},{"./core.js":10}],14:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; -/**@ - * #Color - * @category Graphics - * Draw a solid color for the entity - */ -Crafty.c("Color", { - _color: "", - ready: true, - - init: function () { - this.bind("Draw", function (e) { - if (e.type === "DOM") { - e.style.backgroundColor = this._color; - e.style.lineHeight = 0; - } else if (e.type === "canvas") { - if (this._color) e.ctx.fillStyle = this._color; - e.ctx.fillRect(e.pos._x, e.pos._y, e.pos._w, e.pos._h); - } - }); - }, - - /**@ - * #.color - * @comp Color - * @trigger Change - when the color changes - * @sign public this .color(String color) - * @sign public String .color() - * @param color - Color of the rectangle - * Will create a rectangle of solid color for the entity, or return the color if no argument is given. - * - * The argument must be a color readable depending on which browser you - * choose to support. IE 8 and below doesn't support the rgb() syntax. - * - * @example - * ~~~ - * Crafty.e("2D, DOM, Color") - * .color("#969696"); - * ~~~ - */ - color: function (color) { - if (!color) return this._color; - this._color = color; - this.trigger("Change"); - return this; - } -}); - -/**@ - * #Tint - * @category Graphics - * Similar to Color by adding an overlay of semi-transparent color. - * - * *Note: Currently only works for Canvas* - */ -Crafty.c("Tint", { - _color: null, - _strength: 1.0, - - init: function () { - var draw = function d(e) { - var context = e.ctx || Crafty.canvas.context; - - context.fillStyle = this._color || "rgba(0,0,0, 0)"; - context.fillRect(e.pos._x, e.pos._y, e.pos._w, e.pos._h); - }; - - this.bind("Draw", draw).bind("RemoveComponent", function (id) { - if (id === "Tint") this.unbind("Draw", draw); - }); - }, - - /**@ - * #.tint - * @comp Tint - * @trigger Change - when the tint is applied - * @sign public this .tint(String color, Number strength) - * @param color - The color in hexadecimal - * @param strength - Level of opacity - * - * Modify the color and level opacity to give a tint on the entity. - * - * @example - * ~~~ - * Crafty.e("2D, Canvas, Tint") - * .tint("#969696", 0.3); - * ~~~ - */ - tint: function (color, strength) { - this._strength = strength; - this._color = Crafty.toRGB(color, this._strength); - - this.trigger("Change"); - return this; - } -}); /**@ * #Image @@ -6809,8 +7008,10 @@ Crafty.c("Image", { context.fillRect(0, 0, this._w, this._h); context.restore(); } else if (e.type === "DOM") { - if (this.__image) - e.style.background = "url(" + this.__image + ") " + this._repeat; + if (this.__image) { + e.style.backgroundImage = "url(" + this.__image + ")"; + e.style.backgroundRepeat = this._repeat; + } } }; @@ -6822,7 +7023,7 @@ Crafty.c("Image", { /**@ * #.image * @comp Image - * @trigger Change - when the image is loaded + * @trigger Invalidate - when the image is loaded * @sign public this .image(String url[, String repeat]) * @param url - URL of the image * @param repeat - If the image should be repeated to fill the entity. @@ -6869,7 +7070,7 @@ Crafty.c("Image", { self.h = self.img.height; } - self.trigger("Change"); + self.trigger("Invalidate"); }; return this; @@ -6883,142 +7084,12 @@ Crafty.c("Image", { } - this.trigger("Change"); + this.trigger("Invalidate"); return this; } }); -Crafty.extend({ - _scenes: {}, - _current: null, - - /**@ - * #Crafty.scene - * @category Scenes, Stage - * @trigger SceneChange - just before a new scene is initialized - { oldScene:String, newScene:String } - * @trigger SceneDestroy - just before the current scene is destroyed - { newScene:String } - * @sign public void Crafty.scene(String sceneName, Function init[, Function uninit]) - * @param sceneName - Name of the scene to add - * @param init - Function to execute when scene is played - * @param uninit - Function to execute before next scene is played, after entities with `2D` are destroyed - * @sign public void Crafty.scene(String sceneName) - * @param sceneName - Name of scene to play - * - * Method to create scenes on the stage. Pass an ID and function to register a scene. - * - * To play a scene, just pass the ID. When a scene is played, all - * previously-created entities with the `2D` component are destroyed. The - * viewport is also reset. - * - * If you want some entities to persist over scenes (as in, not be destroyed) - * simply add the component `Persist`. - * - * @example - * ~~~ - * Crafty.scene("loading", function() { - * Crafty.background("#000"); - * Crafty.e("2D, DOM, Text") - * .attr({ w: 100, h: 20, x: 150, y: 120 }) - * .text("Loading") - * .css({ "text-align": "center"}) - * .textColor("#FFFFFF"); - * }); - * - * Crafty.scene("UFO_dance", - * function() {Crafty.background("#444"); Crafty.e("UFO");}, - * function() {...send message to server...}); - * ~~~ - * This defines (but does not play) two scenes as discussed below. - * ~~~ - * Crafty.scene("loading"); - * ~~~ - * This command will clear the stage by destroying all `2D` entities (except - * those with the `Persist` component). Then it will set the background to - * black and display the text "Loading". - * ~~~ - * Crafty.scene("UFO_dance"); - * ~~~ - * This command will clear the stage by destroying all `2D` entities (except - * those with the `Persist` component). Then it will set the background to - * gray and create a UFO entity. Finally, the next time the game encounters - * another command of the form `Crafty.scene(scene_name)` (if ever), then the - * game will send a message to the server. - */ - scene: function (name, intro, outro) { - // ---FYI--- - // this._current is the name (ID) of the scene in progress. - // this._scenes is an object like the following: - // {'Opening scene': {'initialize': fnA, 'uninitialize': fnB}, - // 'Another scene': {'initialize': fnC, 'uninitialize': fnD}} - - // If there's one argument, play the scene - if (arguments.length === 1) { - Crafty.trigger("SceneDestroy", { - newScene: name - }); - Crafty.viewport.reset(); - - Crafty("2D").each(function () { - if (!this.has("Persist")) this.destroy(); - }); - // uninitialize previous scene - if (this._current !== null && 'uninitialize' in this._scenes[this._current]) { - this._scenes[this._current].uninitialize.call(this); - } - // initialize next scene - var oldScene = this._current; - this._current = name; - Crafty.trigger("SceneChange", { - oldScene: oldScene, - newScene: name - }); - this._scenes[name].initialize.call(this); - - return; - } - - // If there is more than one argument, add the scene information to _scenes - this._scenes[name] = {}; - this._scenes[name].initialize = intro; - if (typeof outro !== 'undefined') { - this._scenes[name].uninitialize = outro; - } - return; - }, - - /**@ - * #Crafty.toRGB - * @category Graphics - * @sign public String Crafty.scene(String hex[, Number alpha]) - * @param hex - a 6 character hex number string representing RGB color - * @param alpha - The alpha value. - * - * Get a rgb string or rgba string (if `alpha` presents). - * - * @example - * ~~~ - * Crafty.toRGB("ffffff"); // rgb(255,255,255) - * Crafty.toRGB("#ffffff"); // rgb(255,255,255) - * Crafty.toRGB("ffffff", .5); // rgba(255,255,255,0.5) - * ~~~ - * - * @see Text.textColor - */ - toRGB: function (hex, alpha) { - hex = (hex.charAt(0) === '#') ? hex.substr(1) : hex; - var c = [], - result; - - c[0] = parseInt(hex.substr(0, 2), 16); - c[1] = parseInt(hex.substr(2, 2), 16); - c[2] = parseInt(hex.substr(4, 2), 16); - - result = alpha === undefined ? 'rgb(' + c.join(',') + ')' : 'rgba(' + c.join(',') + ',' + alpha + ')'; - - return result; - } -}); /**@ * #Crafty.DrawManager @@ -7056,7 +7127,7 @@ Crafty.DrawManager = (function () { target._y = Math.min(a._y, b._y); target._w -= target._x; target._h -= target._y; - + return target; }, @@ -7209,10 +7280,9 @@ Crafty.DrawManager = (function () { * @comp Crafty.DrawManager * @sign public Crafty.DrawManager.drawAll([Object rect]) * @param rect - a rectangular region {_x: x_val, _y: y_val, _w: w_val, _h: h_val} - * ~~~ + * * - If rect is omitted, redraw within the viewport * - If rect is provided, redraw within the rect - * ~~~ */ drawAll: function (rect) { rect = rect || Crafty.viewport.rect(); @@ -7240,10 +7310,9 @@ Crafty.DrawManager = (function () { * @comp Crafty.DrawManager * @sign public Crafty.DrawManager.boundingRect(set) * @param set - Undocumented - * ~~~ + * * - Calculate the common bounding rect of multiple canvas entities. * - Returns coords - * ~~~ */ boundingRect: function (set) { if (!set || !set.length) return; @@ -7279,12 +7348,11 @@ Crafty.DrawManager = (function () { * #Crafty.DrawManager.renderCanvas * @comp Crafty.DrawManager * @sign public Crafty.DrawManager.renderCanvas() - * ~~~ + * * - Triggered by the "RenderScene" event * - If the number of rects is over 60% of the total number of objects * do the naive method redrawing `Crafty.DrawManager.drawAll` * - Otherwise, clear the dirty regions, and redraw entities overlapping the dirty regions. - * ~~~ * * @see Canvas.draw */ @@ -7303,7 +7371,7 @@ Crafty.DrawManager = (function () { if (dirtyViewport) { var view = Crafty.viewport; - ctx.setTransform(view._scale, 0, 0, view._scale, view.x, view.y); + ctx.setTransform(view._scale, 0, 0, view._scale, Math.round(view._x*view._scale), Math.round(view._y*view._scale) ); } //if the amount of changed objects is over 60% of the total objects @@ -7399,9 +7467,8 @@ Crafty.DrawManager = (function () { * #Crafty.DrawManager.renderDOM * @comp Crafty.DrawManager * @sign public Crafty.DrawManager.renderDOM() - * ~~~ + * * When "RenderScene" is triggered, draws all DOM entities that have been flagged - * ~~~ * * @see DOM.draw */ @@ -7412,8 +7479,8 @@ Crafty.DrawManager = (function () { view = Crafty.viewport; style.transform = style[Crafty.support.prefix + "Transform"] = "scale(" + view._scale + ", " + view._scale + ")"; - style.left = view.x + "px"; - style.top = view.y + "px"; + style.left = Math.round(view._x * view._scale) + "px"; + style.top = Math.round(view._y * view._scale) + "px"; style.zIndex = 10; } @@ -7436,7 +7503,48 @@ Crafty.DrawManager = (function () { }; })(); -},{"./core.js":9}],13:[function(require,module,exports){ +Crafty.extend({ + /**@ + * #Crafty.pixelart + * @category Graphics + * @sign public void Crafty.pixelart(Boolean enabled) + * + * Sets the image smoothing for drawing images (for both DOM and Canvas). + * Setting this to true disables smoothing for images, which is the preferred + * way for drawing pixel art. Defaults to false. + * + * This feature is experimental and you should be careful with cross-browser compatibility. + * The best way to disable image smoothing is to use the Canvas render method and the Sprite component for drawing your entities. + * + * If you want to switch modes in the middle of a scene, + * be aware that canvas entities won't be drawn in the new style until something else invalidates them. + * (You can manually invalidate all canvas entities with `Crafty("Canvas").trigger("Invalidate");`) + * + * Note that Firefox_26 currently has a [bug](https://bugzilla.mozilla.org/show_bug.cgi?id=696630) + * which prevents disabling image smoothing for Canvas entities that use the Image component. Use the Sprite + * component instead. + * Note that Webkit (Chrome & Safari) currently has a bug [link1](http://code.google.com/p/chromium/issues/detail?id=134040) + * [link2](http://code.google.com/p/chromium/issues/detail?id=106662) that prevents disabling image smoothing + * for DOM entities. + * + * @example + * This is the preferred way to draw pixel art with the best cross-browser compatibility. + * ~~~ + * Crafty.canvas.init(); + * Crafty.pixelart(true); + * + * Crafty.sprite(imgWidth, imgHeight, "spriteMap.png", {sprite1:[0,0]}); + * Crafty.e("2D, Canvas, sprite1"); + * ~~~ + */ + _pixelartEnabled: false, + pixelart: function(enabled) { + Crafty._pixelartEnabled = enabled; + Crafty.trigger("PixelartSet", enabled); + } +}); + +},{"./core.js":10}],15:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -7471,13 +7579,6 @@ var Crafty = require('./core.js'), */ if (mobile) Crafty.mobile = mobile[0]; - /**@ - * #Crafty.support.setter - * @comp Crafty.support - * Is `__defineSetter__` supported? - */ - support.setter = ('__defineSetter__' in this && '__defineGetter__' in this); - /**@ * #Crafty.support.defineProperty * @comp Crafty.support @@ -7573,152 +7674,8 @@ var Crafty = require('./core.js'), support.devicemotion = (typeof window.DeviceMotionEvent !== "undefined"); })(); -Crafty.extend({ - - zeroFill: function (number, width) { - width -= number.toString().length; - if (width > 0) - return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') + number; - return number.toString(); - }, - - /**@ - * #Crafty.sprite - * @category Graphics - * @sign public this Crafty.sprite([Number tile, [Number tileh]], String url, Object map[, Number paddingX[, Number paddingY]]) - * @param tile - Tile size of the sprite map, defaults to 1 - * @param tileh - Height of the tile; if provided, tile is interpreted as the width - * @param url - URL of the sprite image - * @param map - Object where the key is what becomes a new component and the value points to a position on the sprite map - * @param paddingX - Horizontal space in between tiles. Defaults to 0. - * @param paddingY - Vertical space in between tiles. Defaults to paddingX. - * Generates components based on positions in a sprite image to be applied to entities. - * - * Accepts a tile size, URL and map for the name of the sprite and its position. - * - * The position must be an array containing the position of the sprite where index `0` - * is the `x` position, `1` is the `y` position and optionally `2` is the width and `3` - * is the height. If the sprite map has padding, pass the values for the `x` padding - * or `y` padding. If they are the same, just add one value. - * - * If the sprite image has no consistent tile size, `1` or no argument need be - * passed for tile size. - * - * Entities that add the generated components are also given the `2D` component, and - * a component called `Sprite`. - * - * @example - * ~~~ - * Crafty.sprite("imgs/spritemap6.png", {flower:[0,0,20,30]}); - * var flower_entity = Crafty.e("2D, DOM, flower"); - * ~~~ - * The first line creates a component called `flower` associated with the sub-image of - * spritemap6.png with top-left corner (0,0), width 20 pixels, and height 30 pixels. - * The second line creates an entity with that image. (Note: The `2D` is not really - * necessary here, because adding the `flower` component automatically also adds the - * `2D` component.) - * ~~~ - * Crafty.sprite(50, "imgs/spritemap6.png", {flower:[0,0], grass:[0,1,3,1]}); - * ~~~ - * In this case, the `flower` component is pixels 0 <= x < 50, 0 <= y < 50, and the - * `grass` component is pixels 0 <= x < 150, 50 <= y < 100. (The `3` means grass has a - * width of 3 tiles, i.e. 150 pixels.) - * ~~~ - * Crafty.sprite(50, 100, "imgs/spritemap6.png", {flower:[0,0], grass:[0,1]}, 10); - * ~~~ - * In this case, each tile is 50x100, and there is a spacing of 10 pixels between - * consecutive tiles. So `flower` is pixels 0 <= x < 50, 0 <= y < 100, and `grass` is - * pixels 0 <= x < 50, 110 <= y < 210. - * - * @see Sprite - */ - sprite: function (tile, tileh, url, map, paddingX, paddingY) { - var spriteName, temp, x, y, w, h, img; - - //if no tile value, default to 1. - //(if the first passed argument is a string, it must be the url.) - if (typeof tile === "string") { - paddingY = paddingX; - paddingX = map; - map = tileh; - url = tile; - tile = 1; - tileh = 1; - } - - if (typeof tileh == "string") { - paddingY = paddingX; - paddingX = map; - map = url; - url = tileh; - tileh = tile; - } - - //if no paddingY, use paddingX - if (!paddingY && paddingX) paddingY = paddingX; - paddingX = parseInt(paddingX || 0, 10); //just incase - paddingY = parseInt(paddingY || 0, 10); - - var markSpritesReady = function() { - this.ready = true; - this.trigger("Change"); - }; - - img = Crafty.asset(url); - if (!img) { - img = new Image(); - img.src = url; - Crafty.asset(url, img); - img.onload = function () { - //all components with this img are now ready - for (var spriteName in map) { - Crafty(spriteName).each(markSpritesReady); - } - }; - } - - var sharedSpriteInit = function() { - this.requires("2D, Sprite"); - this.__trim = [0, 0, 0, 0]; - this.__image = url; - this.__coord = [this.__coord[0], this.__coord[1], this.__coord[2], this.__coord[3]]; - this.__tile = tile; - this.__tileh = tileh; - this.__padding = [paddingX, paddingY]; - this.img = img; - - //draw now - if (this.img.complete && this.img.width > 0) { - this.ready = true; - this.trigger("Change"); - } - - //set the width and height to the sprite size - this.w = this.__coord[2]; - this.h = this.__coord[3]; - }; - - for (spriteName in map) { - if (!map.hasOwnProperty(spriteName)) continue; - - temp = map[spriteName]; - x = temp[0] * (tile + paddingX); - y = temp[1] * (tileh + paddingY); - w = temp[2] * tile || tile; - h = temp[3] * tileh || tileh; - - //generates sprite components for each tile in the map - Crafty.c(spriteName, { - ready: false, - __coord: [x, y, w, h], - - init: sharedSpriteInit - }); - } - - return this; - }, +Crafty.extend({ _events: {}, /**@ @@ -7818,257 +7775,51 @@ Crafty.extend({ * @sign public void Crafty.background(String value) * @param style - Modify the background with a color or image * - * This method is essentially a shortcut for adding a background - * style to the stage element. + * This method is a shortcut for adding a background + * style to the stage element, i.e. + * `Crafty.stage.elem.style.background = ...` + * + * For example, if you want the background to be white, + * with an image in the center, you might use: + * ~~~ + * Crafty.background('#FFFFFF url(landscape.png) no-repeat center center'); + * ~~~ + * */ background: function (style) { Crafty.stage.elem.style.background = style; - }, + } +}); +},{"./core.js":10}],16:[function(require,module,exports){ +var Crafty = require('./core.js'), + document = window.document; +/**@ + * #HTML + * @category Graphics + * Component allow for insertion of arbitrary HTML into an entity + */ +Crafty.c("HTML", { + inner: '', + init: function () { + this.requires('2D, DOM'); + }, /**@ - * #Crafty.keys - * @category Input - * Object of key names and the corresponding key code. + * #.replace + * @comp HTML + * @sign public this .replace(String html) + * @param html - arbitrary html + * + * This method will replace the content of this entity with the supplied html * - * ~~~ - * BACKSPACE: 8, - * TAB: 9, - * ENTER: 13, - * PAUSE: 19, - * CAPS: 20, - * ESC: 27, - * SPACE: 32, - * PAGE_UP: 33, - * PAGE_DOWN: 34, - * END: 35, - * HOME: 36, - * LEFT_ARROW: 37, - * UP_ARROW: 38, - * RIGHT_ARROW: 39, - * DOWN_ARROW: 40, - * INSERT: 45, - * DELETE: 46, - * 0: 48, - * 1: 49, - * 2: 50, - * 3: 51, - * 4: 52, - * 5: 53, - * 6: 54, - * 7: 55, - * 8: 56, - * 9: 57, - * A: 65, - * B: 66, - * C: 67, - * D: 68, - * E: 69, - * F: 70, - * G: 71, - * H: 72, - * I: 73, - * J: 74, - * K: 75, - * L: 76, - * M: 77, - * N: 78, - * O: 79, - * P: 80, - * Q: 81, - * R: 82, - * S: 83, - * T: 84, - * U: 85, - * V: 86, - * W: 87, - * X: 88, - * Y: 89, - * Z: 90, - * NUMPAD_0: 96, - * NUMPAD_1: 97, - * NUMPAD_2: 98, - * NUMPAD_3: 99, - * NUMPAD_4: 100, - * NUMPAD_5: 101, - * NUMPAD_6: 102, - * NUMPAD_7: 103, - * NUMPAD_8: 104, - * NUMPAD_9: 105, - * MULTIPLY: 106, - * ADD: 107, - * SUBSTRACT: 109, - * DECIMAL: 110, - * DIVIDE: 111, - * F1: 112, - * F2: 113, - * F3: 114, - * F4: 115, - * F5: 116, - * F6: 117, - * F7: 118, - * F8: 119, - * F9: 120, - * F10: 121, - * F11: 122, - * F12: 123, - * SHIFT: 16, - * CTRL: 17, - * ALT: 18, - * PLUS: 187, - * COMMA: 188, - * MINUS: 189, - * PERIOD: 190, - * PULT_UP: 29460, - * PULT_DOWN: 29461, - * PULT_LEFT: 4, - * PULT_RIGHT': 5 - * ~~~ - */ - keys: { - 'BACKSPACE': 8, - 'TAB': 9, - 'ENTER': 13, - 'PAUSE': 19, - 'CAPS': 20, - 'ESC': 27, - 'SPACE': 32, - 'PAGE_UP': 33, - 'PAGE_DOWN': 34, - 'END': 35, - 'HOME': 36, - 'LEFT_ARROW': 37, - 'UP_ARROW': 38, - 'RIGHT_ARROW': 39, - 'DOWN_ARROW': 40, - 'INSERT': 45, - 'DELETE': 46, - '0': 48, - '1': 49, - '2': 50, - '3': 51, - '4': 52, - '5': 53, - '6': 54, - '7': 55, - '8': 56, - '9': 57, - 'A': 65, - 'B': 66, - 'C': 67, - 'D': 68, - 'E': 69, - 'F': 70, - 'G': 71, - 'H': 72, - 'I': 73, - 'J': 74, - 'K': 75, - 'L': 76, - 'M': 77, - 'N': 78, - 'O': 79, - 'P': 80, - 'Q': 81, - 'R': 82, - 'S': 83, - 'T': 84, - 'U': 85, - 'V': 86, - 'W': 87, - 'X': 88, - 'Y': 89, - 'Z': 90, - 'NUMPAD_0': 96, - 'NUMPAD_1': 97, - 'NUMPAD_2': 98, - 'NUMPAD_3': 99, - 'NUMPAD_4': 100, - 'NUMPAD_5': 101, - 'NUMPAD_6': 102, - 'NUMPAD_7': 103, - 'NUMPAD_8': 104, - 'NUMPAD_9': 105, - 'MULTIPLY': 106, - 'ADD': 107, - 'SUBSTRACT': 109, - 'DECIMAL': 110, - 'DIVIDE': 111, - 'F1': 112, - 'F2': 113, - 'F3': 114, - 'F4': 115, - 'F5': 116, - 'F6': 117, - 'F7': 118, - 'F8': 119, - 'F9': 120, - 'F10': 121, - 'F11': 122, - 'F12': 123, - 'SHIFT': 16, - 'CTRL': 17, - 'ALT': 18, - 'PLUS': 187, - 'COMMA': 188, - 'MINUS': 189, - 'PERIOD': 190, - 'PULT_UP': 29460, - 'PULT_DOWN': 29461, - 'PULT_LEFT': 4, - 'PULT_RIGHT': 5 - - }, - - /**@ - * #Crafty.mouseButtons - * @category Input - * Object of mouseButton names and the corresponding button ID. - * In all mouseEvents we add the e.mouseButton property with a value normalized to match e.button of modern webkit - * - * ~~~ - * LEFT: 0, - * MIDDLE: 1, - * RIGHT: 2 - * ~~~ - */ - mouseButtons: { - LEFT: 0, - MIDDLE: 1, - RIGHT: 2 - } -}); -},{"./core.js":9}],14:[function(require,module,exports){ -var Crafty = require('./core.js'), - document = window.document; - -/**@ - * #HTML - * @category Graphics - * Component allow for insertion of arbitrary HTML into an entity - */ -Crafty.c("HTML", { - inner: '', - - init: function () { - this.requires('2D, DOM'); - }, - - /**@ - * #.replace - * @comp HTML - * @sign public this .replace(String html) - * @param html - arbitrary html - * - * This method will replace the content of this entity with the supplied html - * - * @example - * Create a link + * @example + * Create a link * ~~~ * Crafty.e("HTML") * .attr({x:20, y:20, w:100, h:100}) - * .replace("Crafty.js"); + * .replace("Index"); * ~~~ */ replace: function (new_html) { @@ -8090,7 +7841,7 @@ Crafty.c("HTML", { * ~~~ * Crafty.e("HTML") * .attr({x:20, y:20, w:100, h:100}) - * .append("Crafty.js"); + * .append("Index"); * ~~~ */ append: function (new_html) { @@ -8112,7 +7863,7 @@ Crafty.c("HTML", { * ~~~ * Crafty.e("HTML") * .attr({x:20, y:20, w:100, h:100}) - * .prepend("Crafty.js"); + * .prepend("Index"); * ~~~ */ prepend: function (new_html) { @@ -8121,71 +7872,7 @@ Crafty.c("HTML", { return this; } }); -},{"./core.js":9}],15:[function(require,module,exports){ -var Crafty = require('./core.js'), - document = window.document; - -/**@ - * #Crafty.import - * @sign public void Crafty.import(String url[, String scene]) - * @param url - Path to the saved file - * @param scene - Name of the scene to load if saved multiple scenes - * @sign public void Crafty.import(Object sceneData) - * @param sceneData - Scene data generated from builder - * This method will load in scene data generated by the Crafty Builder. - * - * @example - * ~~~ - * Crafty.import({ - * '0': {props: value}, - * 'n': [ - * {c: "comp, list", image: ''} - * ] - * }); - * ~~~ - */ -Crafty['import'] = function (obj, scene) { - //if its a string, load the script file - if (typeof obj === "string") { - if (levelData) { - if (scene) Crafty.import(levelData[scene]); - else Crafty.import(levelData); - } else { - var elem; - elem = document.createElement("script"); - elem.onload = function () { - if (scene) Crafty.import(levelData[scene]); - else Crafty.import(levelData); - }; - elem.src = obj; - } - return; - } - - var key, i = 0, - l, current, ent; - - //loop over new entities to create - if (obj.n && typeof obj.n === "object") { - for (l = obj.n.length; i < l; ++i) { - current = obj.n[i]; - - //create entity with components - ent = Crafty.e(current.c); - delete current.c; //remove the components - - //apply the other properties - ent.attr(current); - } - } - - //loop over modified entities - for (key in obj) { - ent = Crafty(key); - ent.attr(obj[key]); - } -}; -},{"./core.js":9}],16:[function(require,module,exports){ +},{"./core.js":10}],17:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -8299,7 +7986,7 @@ Crafty.extend({ return { x: -Math.ceil(-left / this._tile.width - (top & 1) * 0.5), y: top / this._tile.height * 2 - }; + }; }, /**@ * #Crafty.isometric.centerAt @@ -8368,150 +8055,483 @@ Crafty.extend({ } }); -},{"./core.js":9}],17:[function(require,module,exports){ +},{"./core.js":10}],18:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; Crafty.extend({ /**@ - * #Crafty.assets - * @category Assets - * An object containing every asset used in the current Crafty game. - * The key is the URL and the value is the `Audio` or `Image` object. - * - * If loading an asset, check that it is in this object first to avoid loading twice. - * - * @example - * ~~~ - * var isLoaded = !!Crafty.assets["images/sprite.png"]; - * ~~~ - * @see Crafty.loader - */ - assets: {}, - - /**@ - * #Crafty.asset - * @category Assets - * - * @trigger NewAsset - After setting new asset - Object - key and value of new added asset. - * @sign public void Crafty.asset(String key, Object asset) - * @param key - asset url. - * @param asset - Audio` or `Image` object. - * Add new asset to assets object. - * - * @sign public void Crafty.asset(String key) - * @param key - asset url. - * Get asset from assets object. + * #Crafty.keys + * @category Input + * Object of key names and the corresponding key code. * - * @example * ~~~ - * Crafty.asset(key, value); - * var asset = Crafty.asset(key); //object with key and value fields - * ~~~ - * - * @see Crafty.assets + * BACKSPACE: 8, + * TAB: 9, + * ENTER: 13, + * PAUSE: 19, + * CAPS: 20, + * ESC: 27, + * SPACE: 32, + * PAGE_UP: 33, + * PAGE_DOWN: 34, + * END: 35, + * HOME: 36, + * LEFT_ARROW: 37, + * UP_ARROW: 38, + * RIGHT_ARROW: 39, + * DOWN_ARROW: 40, + * INSERT: 45, + * DELETE: 46, + * 0: 48, + * 1: 49, + * 2: 50, + * 3: 51, + * 4: 52, + * 5: 53, + * 6: 54, + * 7: 55, + * 8: 56, + * 9: 57, + * A: 65, + * B: 66, + * C: 67, + * D: 68, + * E: 69, + * F: 70, + * G: 71, + * H: 72, + * I: 73, + * J: 74, + * K: 75, + * L: 76, + * M: 77, + * N: 78, + * O: 79, + * P: 80, + * Q: 81, + * R: 82, + * S: 83, + * T: 84, + * U: 85, + * V: 86, + * W: 87, + * X: 88, + * Y: 89, + * Z: 90, + * NUMPAD_0: 96, + * NUMPAD_1: 97, + * NUMPAD_2: 98, + * NUMPAD_3: 99, + * NUMPAD_4: 100, + * NUMPAD_5: 101, + * NUMPAD_6: 102, + * NUMPAD_7: 103, + * NUMPAD_8: 104, + * NUMPAD_9: 105, + * MULTIPLY: 106, + * ADD: 107, + * SUBSTRACT: 109, + * DECIMAL: 110, + * DIVIDE: 111, + * F1: 112, + * F2: 113, + * F3: 114, + * F4: 115, + * F5: 116, + * F6: 117, + * F7: 118, + * F8: 119, + * F9: 120, + * F10: 121, + * F11: 122, + * F12: 123, + * SHIFT: 16, + * CTRL: 17, + * ALT: 18, + * PLUS: 187, + * COMMA: 188, + * MINUS: 189, + * PERIOD: 190, + * PULT_UP: 29460, + * PULT_DOWN: 29461, + * PULT_LEFT: 4, + * PULT_RIGHT': 5 + * ~~~ */ - asset: function (key, value) { - if (arguments.length === 1) { - return Crafty.assets[key]; - } + keys: { + 'BACKSPACE': 8, + 'TAB': 9, + 'ENTER': 13, + 'PAUSE': 19, + 'CAPS': 20, + 'ESC': 27, + 'SPACE': 32, + 'PAGE_UP': 33, + 'PAGE_DOWN': 34, + 'END': 35, + 'HOME': 36, + 'LEFT_ARROW': 37, + 'UP_ARROW': 38, + 'RIGHT_ARROW': 39, + 'DOWN_ARROW': 40, + 'INSERT': 45, + 'DELETE': 46, + '0': 48, + '1': 49, + '2': 50, + '3': 51, + '4': 52, + '5': 53, + '6': 54, + '7': 55, + '8': 56, + '9': 57, + 'A': 65, + 'B': 66, + 'C': 67, + 'D': 68, + 'E': 69, + 'F': 70, + 'G': 71, + 'H': 72, + 'I': 73, + 'J': 74, + 'K': 75, + 'L': 76, + 'M': 77, + 'N': 78, + 'O': 79, + 'P': 80, + 'Q': 81, + 'R': 82, + 'S': 83, + 'T': 84, + 'U': 85, + 'V': 86, + 'W': 87, + 'X': 88, + 'Y': 89, + 'Z': 90, + 'NUMPAD_0': 96, + 'NUMPAD_1': 97, + 'NUMPAD_2': 98, + 'NUMPAD_3': 99, + 'NUMPAD_4': 100, + 'NUMPAD_5': 101, + 'NUMPAD_6': 102, + 'NUMPAD_7': 103, + 'NUMPAD_8': 104, + 'NUMPAD_9': 105, + 'MULTIPLY': 106, + 'ADD': 107, + 'SUBSTRACT': 109, + 'DECIMAL': 110, + 'DIVIDE': 111, + 'F1': 112, + 'F2': 113, + 'F3': 114, + 'F4': 115, + 'F5': 116, + 'F6': 117, + 'F7': 118, + 'F8': 119, + 'F9': 120, + 'F10': 121, + 'F11': 122, + 'F12': 123, + 'SHIFT': 16, + 'CTRL': 17, + 'ALT': 18, + 'PLUS': 187, + 'COMMA': 188, + 'MINUS': 189, + 'PERIOD': 190, + 'PULT_UP': 29460, + 'PULT_DOWN': 29461, + 'PULT_LEFT': 4, + 'PULT_RIGHT': 5 - if (!Crafty.assets[key]) { - Crafty.assets[key] = value; - this.trigger("NewAsset", { - key: key, - value: value - }); - return value; - } }, + /**@ - * #Crafty.image_whitelist - * @category Assets + * #Crafty.mouseButtons + * @category Input + * An object mapping mouseButton names to the corresponding button ID. + * In all mouseEvents, we add the `e.mouseButton` property with a value normalized to match e.button of modern webkit browsers: * + * ~~~ + * LEFT: 0, + * MIDDLE: 1, + * RIGHT: 2 + * ~~~ + */ + mouseButtons: { + LEFT: 0, + MIDDLE: 1, + RIGHT: 2 + } +}); +},{"./core.js":10}],19:[function(require,module,exports){ +var Crafty = require('./core.js'), + document = window.document; + +Crafty.extend({ + /**@ + * #Crafty.assets + * @category Assets + * An object containing every asset used in the current Crafty game. + * The key is the URL and the value is the `Audio` or `Image` object. * - * A list of file extensions that can be loaded as images by Crafty.load + * If loading an asset, check that it is in this object first to avoid loading twice. * * @example * ~~~ - * Crafty.image_whitelist.push("tif") - * Crafty.load(["images/sprite.tif", "sounds/jump.mp3"], - * function() { - * //when loaded - * Crafty.scene("main"); //go to main scene - * Crafty.audio.play("jump.mp3"); //Play the audio file - * }, - * - * function(e) { - * //progress - * }, - * - * function(e) { - * //uh oh, error loading - * } - * ); + * var isLoaded = !!Crafty.assets["images/sprite.png"]; * ~~~ - * - * @see Crafty.asset - * @see Crafty.load + * @see Crafty.loader */ - image_whitelist: ["jpg", "jpeg", "gif", "png", "svg"], + assets: {}, + __paths: { audio: "", images: "" }, /**@ - * #Crafty.loader + * #Crafty.paths * @category Assets - * @sign public void Crafty.load(Array assets, Function onLoad[, Function onProgress, Function onError]) - * @param assets - Array of assets to load (accepts sounds and images) - * @param onLoad - Callback when the assets are loaded - * @param onProgress - Callback when an asset is loaded. Contains information about assets loaded - * @param onError - Callback when an asset fails to load - * - * Preloader for all assets. Takes an array of URLs and - * adds them to the `Crafty.assets` object. + * @sign public void Crafty.paths([Object paths]) + * @param paths - Object containing paths for audio and images folders * - * Files with suffixes in `image_whitelist` (case insensitive) will be loaded. + * Function to define custom folder for audio and images. You should use + * this function to avoid typing the same paths again and again when + * loading assets with the Crafty.load() function. * - * If `Crafty.support.audio` is `true`, files with the following suffixes `mp3`, `wav`, `ogg` and `mp4` (case insensitive) can be loaded. + * If you do not give a object you get the current paths for both audio + * and images back. * - * The `onProgress` function will be passed on object with information about - * the progress including how many assets loaded, total of all the assets to - * load and a percentage of the progress. - * ~~~ - * { loaded: j, total: total, percent: (j / total * 100) ,src:src}) - * ~~~ + * You do not have to define paths. * - * `onError` will be passed with the asset that couldn't load. + * @example * - * When `onError` is not provided, the onLoad is loaded even some assets are not successfully loaded. Otherwise, onLoad will be called no matter whether there are errors or not. * - * @example + * Setting folders: * ~~~ - * Crafty.load(["images/sprite.png", "sounds/jump.mp3"], - * function() { - * //when loaded - * Crafty.scene("main"); //go to main scene - * Crafty.audio.play("jump.mp3"); //Play the audio file - * }, + * Crafty.paths({ audio: "custom/audio/path/", images: "custom/images/path/" }); * - * function(e) { - * //progress - * }, + * Crafty.load({ + * "audio": { + * "ray": ['ray.mp3'] // This loads ray.mp3 from custom/audio/path/ray.mp3 + * } + * }, function() { + * console.log('loaded'); + * }); + * ~~~ + * + * @see Crafty.load + */ + paths: function(p) { + if (typeof p === "undefined") { + return this.__paths; + } else { + if(p.audio) + this.__paths.audio = p.audio; + if(p.images) + this.__paths.images = p.images; + } + }, + + /**@ + * #Crafty.asset + * @category Assets + * @trigger NewAsset - After setting new asset - Object - key and value of new added asset. + * @sign public void Crafty.asset(String key, Object asset) + * @param key - asset url. + * @param asset - `Audio` or `Image` object. + * Add new asset to assets object. + * + * @sign public void Crafty.asset(String key) + * @param key - asset url. + * Get asset from assets object. + * + * @example + * ~~~ + * Crafty.asset(key, value); + * var asset = Crafty.asset(key); //object with key and value fields + * ~~~ + * + * @see Crafty.assets + */ + asset: function (key, value) { + if (arguments.length === 1) { + return Crafty.assets[key]; + } + + if (!Crafty.assets[key]) { + Crafty.assets[key] = value; + this.trigger("NewAsset", { + key: key, + value: value + }); + return value; + } + }, + /**@ + * #Crafty.image_whitelist + * @category Assets + * + * A list of file extensions that can be loaded as images by Crafty.load + * + * @example + * ~~~ + * // add tif extension to list of supported image files + * Crafty.image_whitelist.push("tif"); + * + * var assets = { + * "sprites": { + * "sprite.tif": { //set a tif sprite + * "tile": 64, + * "tileh": 32, + * "map": { "sprite_car": [0, 0] } + * } + * }, + * "audio": { + * "jump": "jump.mp3"; + * } + * }; + * + * Crafty.load( assets, // preload the assets + * function() { //when loaded + * Crafty.audio.play("jump"); //Play the audio file + * Crafty.e('2D, DOM, sprite_car'); // create entity with sprite + * }, + * + * function(e) { //progress + * }, + * + * function(e) { //uh oh, error loading + * } + * ); + * ~~~ + * + * @see Crafty.asset + * @see Crafty.load + */ + image_whitelist: ["jpg", "jpeg", "gif", "png", "svg"], + /**@ + * #Crafty.loader + * @category Assets + * @sign public void Crafty.load(Object assets, Function onLoad[, Function onProgress[, Function onError]]) + * @param assets - Object JSON formatted (or JSON string), with assets to load (accepts sounds, images and sprites) + * @param onLoad - Callback when the assets are loaded + * @param onProgress - Callback when an asset is loaded. Contains information about assets loaded + * @param onError - Callback when an asset fails to load + * + * Preloader for all assets. Takes a JSON formatted object (or JSON string) of files and adds them to the + * `Crafty.assets` object, as well as setting sprites accordingly. + * + * Format must follow the pattern shown in the example below, but it's not required to pass all "audio", + * "images" and "sprites" properties, only those you'll need. For example, if you don't need to preload + * sprites, you can omit that property. + * + * Default folders for storing and locating assets are 'assets/audio' and 'assets/images'. For changing these, + * use the function Crafty.paths. + * + * Files with suffixes in `image_whitelist` (case insensitive) will be loaded. + * + * It's possible to pass the full file path(including protocol), instead of just the filename.ext, in case + * you want some asset to be loaded from another domain. + * + * If `Crafty.support.audio` is `true`, files with the following suffixes `mp3`, `wav`, `ogg` and + * `mp4` (case insensitive) can be loaded. + * + * The `onProgress` function will be passed on object with information about + * the progress including how many assets loaded, total of all the assets to + * load and a percentage of the progress. + * ~~~ + * { loaded: j, total: total, percent: (j / total * 100), src:src } + * ~~~ + * + * `onError` will be passed with the asset that couldn't load. + * + * When `onError` is not provided, the onLoad is loaded even when some assets are not successfully loaded. + * Otherwise, onLoad will be called no matter whether there are errors or not. + * + * @example + * ~~~ + * var assetsObj = { + * "audio": { + * "beep": ["beep.wav", "beep.mp3", "beep.ogg"], + * "boop": "boop.wav", + * "slash": "slash.wav" + * }, + * "images": ["badguy.bmp", "goodguy.png"], + * "sprites": { + * "animals.png": { + * "tile": 50, + * "tileh": 40, + * "map": { "ladybug": [0,0], "lazycat": [0,1], "ferociousdog": [0,2] } + * "paddingX": 5, + * "paddingY": 5, + * "paddingAroundBorder": 10 + * }, + * "vehicles.png": { + * "tile": 150, + * "tileh": 75, + * "map": { "car": [0,0], "truck": [0,1] } + * } + * }, + * }; + * + * Crafty.load(assetsObj, // preload assets + * function() { //when loaded + * Crafty.scene("main"); //go to main scene + * Crafty.audio.play("boop"); //Play the audio file + * Crafty.e('2D, DOM, lazycat'); // create entity with sprite + * }, + * + * function(e) { //progress + * }, * - * function(e) { - * //uh oh, error loading + * function(e) { //uh oh, error loading * } * ); * ~~~ * + * @see Crafty.paths * @see Crafty.assets * @see Crafty.image_whitelist + * @see Crafty.removeAssets */ load: function (data, oncomplete, onprogress, onerror) { - - var i = 0, - l = data.length, - current, obj, total = l, - j = 0, - ext = ""; + + data = typeof data === "string"?JSON.parse(data):data; + + var j = 0, + total = (data.audio? Object.keys(data.audio).length : 0) + + (data.images? Object.keys(data.images).length : 0) + + (data.sprites? Object.keys(data.sprites).length : 0), + current, fileUrl, obj, type, asset, + audSupport = Crafty.support.audio, + paths = Crafty.paths(), + getExt = function(f) { + return f.substr(f.lastIndexOf('.') + 1, 3).toLowerCase(); + }, + getFilePath = function(type,f) { + return f.search("://") === -1? (type=="audio"? paths.audio + f : paths.images + f) : f; + }, + // returns null if 'a' is not already a loaded asset, obj otherwise + isAsset = function(a) { + return Crafty.asset(a) || null; + }, + isSupportedAudio = function(f) { + return Crafty.audio.supports(getExt(f)); + }, + isValidImage = function(f) { + return Crafty.image_whitelist.indexOf(getExt(f)) != -1; + }, + onImgLoad = function(obj,url) { + obj.onload = pro; + if (Crafty.support.prefix === 'webkit') + obj.src = ""; // workaround for webkit bug + obj.src = url; + }; //Progress function @@ -8519,9 +8539,8 @@ Crafty.extend({ var src = this.src; //Remove events cause audio trigger this event more than once(depends on browser) - if (this.removeEventListener) { + if (this.removeEventListener) this.removeEventListener('canplaythrough', pro, false); - } ++j; //if progress callback, give information of assets loaded, total and percent @@ -8551,41 +8570,50 @@ Crafty.extend({ if (j === total && oncomplete) oncomplete(); } - for (; i < l; ++i) { - current = data[i]; - ext = current.substr(current.lastIndexOf('.') + 1, 3).toLowerCase(); - - obj = Crafty.asset(current) || null; + for (type in data) { + for(asset in data[type]) { - if (Crafty.audio.supports(ext)) { - //Create a new asset if necessary, using the file name as an id - if (!obj) { - var name = current.substr(current.lastIndexOf('/') + 1).toLowerCase(); - obj = Crafty.audio.create(name, current).obj; - } - - //addEventListener is supported on IE9 , Audio as well - if (obj.addEventListener) { - obj.addEventListener('canplaythrough', pro, false); - } + current = data[type][asset]; + if (type === "audio" && audSupport) { + if (typeof current === "object") { + var files = []; + for (var i in current) { + fileUrl = getFilePath(type, current[i]); + if (!isAsset(fileUrl) && isSupportedAudio(current[i])) + files.push(fileUrl); + } + obj = Crafty.audio.add(asset, files).obj; + } + else if (typeof current === "string" && isSupportedAudio(current)) { + fileUrl = getFilePath(type, current); + if (!isAsset(fileUrl)) + obj = Crafty.audio.add(asset, fileUrl).obj; + } - } else if (Crafty.image_whitelist.indexOf(ext) >= 0) { - if (!obj) { - obj = new Image(); - Crafty.asset(current, obj); - } - obj.onload = pro; - if (Crafty.support.prefix === 'webkit') { - obj.src = ""; // workaround for webkit bug + //addEventListener is supported on IE9 , Audio as well + if (obj && obj.addEventListener) + obj.addEventListener('canplaythrough', pro, false); + } else { + asset = type === "sprites"? asset : current; + fileUrl = getFilePath(type, asset); + if (isValidImage(asset)) { + obj = isAsset(fileUrl); + if (!obj) { + obj = new Image(); + if (type === "sprites") + Crafty.sprite(current.tile, current.tileh, fileUrl, current.map, + current.paddingX, current.paddingY, current.paddingAroundBorder); + Crafty.asset(fileUrl, obj); + } + onImgLoad(obj, fileUrl); + } } - obj.src = current; //setup src after onload function Opera/IE Bug - - } else { - total--; - continue; //skip if not applicable + if (obj) + obj.onerror = err; + else + --total; } - obj.onerror = err; } // If we aren't trying to handle *any* of the files, that's as complete as it gets! @@ -8594,206 +8622,96 @@ Crafty.extend({ }, /**@ - * #Crafty.modules + * #Crafty.removeAssets * @category Assets - * @sign public void Crafty.modules([String repoLocation,] Object moduleMap[, Function onLoad]) - * @param modules - Map of name:version pairs for modules to load - * @param onLoad - Callback when the modules are loaded - * - * Browse the selection of community modules on http://craftycomponents.com - * - * It is possible to create your own repository. * + * @sign public void Crafty.removeAssets(Object assets) + * @param data - Object JSON formatted (or JSON string), with assets to remove (accepts sounds, images and sprites) + * Removes assets (audio, images, sprites - and related sprite components) in order to allow the browser + * to free memory. + * + * Recieves a JSON fomatted object (or JSON string) containing 'audio', 'images' and/or 'sprites' + * properties with assets to be deleted. Follows a similar format as Crafty.load 'data' argument. If + * you pass the exact same object passed to Crafty.load, that will delete everything loaded that way. + * For sprites, if you want to keep some specific component, just don't pass that component's name in + * the sprite 'map'. + * + * Note that in order to remove the sprite components related to a given sprite, it's required to + * pass the 'map' property of that sprite, and although its own properties's values (the properties refer + * to sprite components) are not used in the removing process, omitting them will cause an error (since + * 'map' is an object, thus it's properties can NOT omitted - however, they can be null, or undefined). + * It will work as long as the 'map' objects' properties have any value. Or if you define 'map' itself + * as an array, like: + * "map": [ "car", "truck" ] instead of "map": { "car": [0,0], "truck": [0,1] }. + * This is examplified below ("animals.png" VS. "vehicles.png" sprites). * * @example * ~~~ - * // Loading from default repository - * Crafty.modules({ moveto: 'DEV' }, function () { - * //module is ready - * Crafty.e("MoveTo, 2D, DOM"); - * }); - * - * // Loading from your own server - * Crafty.modules({ 'http://mydomain.com/js/mystuff.js': 'DEV' }, function () { - * //module is ready - * Crafty.e("MoveTo, 2D, DOM"); - * }); - * - * // Loading from alternative repository - * Crafty.modules('http://cdn.crafty-modules.com', { moveto: 'DEV' }, function () { - * //module is ready - * Crafty.e("MoveTo, 2D, DOM"); - * }); - * - * // Loading from the latest component website - * Crafty.modules( - * 'http://cdn.craftycomponents.com' - * , { MoveTo: 'release' } - * , function () { - * Crafty.e("2D, DOM, Color, MoveTo") - * .attr({x: 0, y: 0, w: 50, h: 50}) - * .color("green"); - * }); - * }); + * var assetsToRemoveObj = { + * "audio": { + * "beep": ["beep.wav", "beep.mp3", "beep.ogg"], + * "boop": "boop.wav" + * }, + * "images": ["badguy.bmp", "goodguy.png"], + * "sprites": { + * "animals.png": { + * "map": { "ladybug": [0,0], "lazycat": [0,1] }, + * }, + * "vehicles.png": { + * "map": [ "car", "truck" ] + * } + * } + * } + * + * Crafty.removeAssets(assetsToRemoveObj); * ~~~ * + * @see Crafty.load */ - modules: function (modulesRepository, moduleMap, oncomplete) { - - if (arguments.length === 2 && typeof modulesRepository === "object") { - oncomplete = moduleMap; - moduleMap = modulesRepository; - modulesRepository = 'http://cdn.craftycomponents.com'; - } - - /*! - * $script.js Async loader & dependency manager - * https://github.com/ded/script.js - * (c) Dustin Diaz, Jacob Thornton 2011 - * License: MIT - */ - var $script = (function () { - var win = this, - doc = document, - head = doc.getElementsByTagName('head')[0], - validBase = /^https?:\/\//, - old = win.$script, - list = {}, ids = {}, delay = {}, scriptpath, scripts = {}, s = 'string', - f = false, - push = 'push', - domContentLoaded = 'DOMContentLoaded', - readyState = 'readyState', - addEventListener = 'addEventListener', - onreadystatechange = 'onreadystatechange'; - - function every(ar, fn, i) { - for (i = 0, j = ar.length; i < j; ++i) - if (!fn(ar[i])) return f; - return 1; - } - - function each(ar, fn) { - every(ar, function (el) { - return !fn(el); - }); - } - - if (!doc[readyState] && doc[addEventListener]) { - doc[addEventListener](domContentLoaded, function fn() { - doc.removeEventListener(domContentLoaded, fn, f); - doc[readyState] = 'complete'; - }, f); - doc[readyState] = 'loading'; - } - - function $script(paths, idOrDone, optDone) { - paths = paths[push] ? paths : [paths]; - var idOrDoneIsDone = idOrDone && idOrDone.call, - done = idOrDoneIsDone ? idOrDone : optDone, - id = idOrDoneIsDone ? paths.join('') : idOrDone, - queue = paths.length; - - function loopFn(item) { - return item.call ? item() : list[item]; - } - - function callback() { - if (!--queue) { - list[id] = 1; - if (done) - done(); - for (var dset in delay) { - if (every(dset.split('|'), loopFn) && !each(delay[dset], loopFn)) - delay[dset] = []; - } + removeAssets: function(data) { + + data = typeof data === "string"?JSON.parse(data):data; + + var current, fileUrl, type, asset, + paths = Crafty.paths(), + getFilePath = function(type,f) { + return f.search("://") === -1? (type=="audio"? paths.audio + f : paths.images + f) : f; + }; + + for (type in data) { + for (asset in data[type]) { + + current = data[type][asset]; + + if (type === "audio") { + if (typeof current === "object") { + for (var i in current) { + fileUrl = getFilePath(type, current[i]); + if (Crafty.asset(fileUrl)) + Crafty.audio.remove(asset); } } - setTimeout(function () { - each(paths, function (path) { - if (scripts[path]) { - if (id) - ids[id] = 1; - return scripts[path] == 2 && callback(); - } - scripts[path] = 1; - if (id) - ids[id] = 1; - create(!validBase.test(path) && scriptpath ? scriptpath + path + '.js' : path, callback); - }); - }, 0); - return $script; - } - - function create(path, fn) { - var el = doc.createElement('script'), - loaded = f; - el.onload = el.onerror = el[onreadystatechange] = function () { - if ((el[readyState] && !(/^c|loade/.test(el[readyState]))) || loaded) return; - el.onload = el[onreadystatechange] = null; - loaded = 1; - scripts[path] = 2; - fn(); - }; - el.async = 1; - el.src = path; - head.insertBefore(el, head.firstChild); + else if (typeof current === "string") { + fileUrl = getFilePath(type, current); + if (Crafty.asset(fileUrl)) + Crafty.audio.remove(asset); + } + } else { + asset = type === "sprites"? asset : current; + fileUrl = getFilePath(type, asset); + if (Crafty.asset(fileUrl)) { + if (type === "sprites") + for (var comp in current.map) + delete Crafty.components()[comp]; + delete Crafty.assets[fileUrl]; + } + } } - - $script.get = create; - - $script.order = function (scripts, id, done) { - (function callback(s) { - s = scripts.shift(); - if (!scripts.length) $script(s, id, done); - else $script(s, callback); - }()); - }; - - $script.path = function (p) { - scriptpath = p; - }; - // This function is a tangled mess of conciseness, so suppress warnings here - /* jshint -W030 */ - $script.ready = function (deps, ready, req) { - deps = deps[push] ? deps : [deps]; - var missing = []; - !each(deps, function (dep) { - list[dep] || missing[push](dep); - }) && every(deps, function (dep) { - return list[dep]; - }) ? - ready() : ! function (key) { - delay[key] = delay[key] || []; - delay[key][push](ready); - req && req(missing); - }(deps.join('|')); - return $script; - }; - /* jshint +W030 */ - $script.noConflict = function () { - win.$script = old; - return this; - }; - - return $script; - })(); - - var modules = []; - var validBase = /^(https?|file):\/\//; - for (var i in moduleMap) { - if (validBase.test(i)) - modules.push(i); - else - modules.push(modulesRepository + '/' + i.toLowerCase() + '-' + moduleMap[i].toLowerCase() + '.js'); } - - $script(modules, function () { - if (oncomplete) oncomplete(); - }); } }); -},{"./core.js":9}],18:[function(require,module,exports){ +},{"./core.js":10}],20:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -9159,6 +9077,21 @@ Crafty.math.Vector2D = (function () { return this.x * vecRH.x + this.y * vecRH.y; }; // dotProduct + /**@ + * #.crossProduct + * @comp Crafty.math.Vector2D + * + * Calculates the z component of the cross product of the two vectors augmented to 3D. + * + * @public + * @sign public {Number} crossProduct(Vector2D); + * @param {Vector2D} vecRH + * @returns {Number} the resultant cross product + */ + Vector2D.prototype.crossProduct = function (vecRH) { + return this.x * vecRH.y - this.y * vecRH.x; + }; // crossProduct + /**@ * #.equals * @comp Crafty.math.Vector2D @@ -9175,21 +9108,38 @@ Crafty.math.Vector2D = (function () { this.x == vecRH.x && this.y == vecRH.y; }; // equals + /**@ + * #.perpendicular + * @comp Crafty.math.Vector2D + * + * Calculates a new vector that is perpendicular to this vector. + * The perpendicular vector has the same magnitude as this vector and is obtained by a counter-clockwise rotation of 90° of this vector. + * + * @public + * @sign public {Vector2D} perpendicular([Vector2D]); + * @param {Vector2D} [result] - An optional parameter to save the result in + * @returns {Vector2D} the perpendicular vector + */ + Vector2D.prototype.perpendicular = function (result) { + result = result || new Vector2D(); + return result.setValues(-this.y, this.x); + }; // perpendicular + /**@ * #.getNormal * @comp Crafty.math.Vector2D * - * Calculates a new right-handed normal vector for the line created by this and the passed vectors. + * Calculates a new right-handed unit vector that is perpendicular to the line created by this and the passed vector. * * @public - * @sign public {Vector2D} getNormal([Vector2D]); - * @param {Vector2D=<0,0>} [vecRH] + * @sign public {Vector2D} getNormal(Vector2D[, Vector2D]); + * @param {Vector2D} vecRH + * @param {Vector2D} [result] - An optional parameter to save the result in * @returns {Vector2D} the new normal vector */ - Vector2D.prototype.getNormal = function (vecRH) { - if (vecRH === undefined) - return new Vector2D(-this.y, this.x); // assume vecRH is <0, 0> - return new Vector2D(vecRH.y - this.y, this.x - vecRH.x).normalize(); + Vector2D.prototype.getNormal = function (vecRH, result) { + result = result || new Vector2D(); + return result.setValues(vecRH.y - this.y, this.x - vecRH.x).normalize(); }; // getNormal /**@ @@ -9425,16 +9375,18 @@ Crafty.math.Vector2D = (function () { * * @public * @static - * @sign public {Vector2D} tripleProduct(Vector2D, Vector2D, Vector2D); + * @sign public {Vector2D} tripleProduct(Vector2D, Vector2D, Vector2D, [Vector2D]); * @param {Vector2D} a * @param {Vector2D} b * @param {Vector2D} c + * @param {Vector2D} [result] - An optional parameter to save the result in * @return {Vector2D} the triple product as a new vector */ - Vector2D.tripleProduct = function (a, b, c) { + Vector2D.tripleProduct = function (a, b, c, result) { + result = result || new Crafty.math.Vector2D(); var ac = a.dotProduct(c); var bc = b.dotProduct(c); - return new Crafty.math.Vector2D(b.x * ac - a.x * bc, b.y * ac - a.y * bc); + return result.setValues(b.x * ac - a.x * bc, b.y * ac - a.y * bc); }; return Vector2D; @@ -9856,7 +9808,110 @@ Crafty.math.Matrix2D = (function () { return Matrix2D; })(); -},{"./core.js":9}],19:[function(require,module,exports){ +},{"./core.js":10}],21:[function(require,module,exports){ +var Crafty = require('./core.js'), + document = window.document; + +/**@ + * #Model + * @category Model + * Model is a component that offers new features for isolating business + * logic in your application. It offers default values, dirty values, + * and deep events on your data. + * + * All data should be accessed via the appropriate methods `.get`, `.set`, + * and `.data` for the proper events to be triggered. It is not encouraged + * to access them directly. + * + * Dirty values make it simple to inspect a model and see what values have changed. + * + * Deep events allow you to bind to specific fields, like `name` or even deep fields + * like `contact.email` and get notified when those specific fields are updated. + * + * @trigger Change - When any data on the model has changed. + * @trigger Change[key] - When the specific key on the model has changed. + * @trigger Change[key.key] - The nested key value has changed. + * @example + * ~~~ + * Crafty.c('Person', { + * name: 'Fox', + * init: function() { this.requires('Model'); } + * }); + * person = Crafty.e('Person').attr({name: 'blaine'}); + * person.bind('Change[name]', function() { + * console.log('name changed!'); + * }); + * person.attr('name', 'blainesch'); // Triggers event + * person.is_dirty('name'); // true + * person.changed // name + * ~~~ + */ +Crafty.c('Model', { + init: function() { + this.changed = []; + this.bind('Change', this._changed_attributes); + this.bind('Change', this._changed_triggers); + }, + + /** + * Fires more specific `Change` events. + * + * For instance a `Change[name]` may get fired when you + * update the name data attribute on the model. + */ + _changed_triggers: function(data, options) { + var key, trigger_data; + options = Crafty.extend.call({pre: ''}, options); + for (key in data) { + this.trigger('Change[' + options.pre + key + ']', data[key]); + if (data[key].constructor.name === 'Object') { + this._changed_triggers(data[key], { + pre: options.pre + key + '.' + }); + } + } + }, + + /** + * Pushes all top-levle changed attribute names to the + * changed array. + */ + _changed_attributes: function(data) { + var key; + for (key in data) { + this.changed.push(key); + } + return this; + }, + + /**@ + * #.is_dirty + * @comp Model + * Helps determine when data or the entire component is "dirty" or has changed attributes. + * + * @example + * ~~~ + * person = Crafty.e('Person').attr({name: 'Fox', age: 24}) + * person.is_dirty() // false + * person.is_dirty('name') // false + * + * person.attr('name', 'Lucky'); + * person.is_dirty(); // true + * person.is_dirty('name'); // true + * person.is_dirty('age'); // false + * person.changed; // ['name'] + * ~~~ + */ + is_dirty: function(key) { + if (arguments.length === 0) { + return !!this.changed.length; + } else { + return this.changed.indexOf(key) > -1; + } + } +}); + +},{"./core.js":10}],22:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -10235,51 +10290,214 @@ Crafty.c("Particles", { } }); -},{"./core.js":9}],20:[function(require,module,exports){ +},{"./core.js":10}],23:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; Crafty.extend({ + _scenes: {}, + _current: null, + /**@ - * #Crafty.audio - * @category Audio + * #Crafty.scene + * @category Scenes, Stage + * @trigger SceneChange - just before a new scene is initialized - { oldScene:String, newScene:String } + * @trigger SceneDestroy - just before the current scene is destroyed - { newScene:String } * - * Add sound files and play them. Chooses best format for browser support. - * Due to the nature of HTML5 audio, three types of audio files will be - * required for cross-browser capabilities. These formats are MP3, Ogg and WAV. - * When sound was not muted on before pause, sound will be unmuted after unpause. - * When sound is muted Crafty.pause() does not have any effect on sound + * @sign public void Crafty.scene(String sceneName, Function init[, Function uninit]) + * @param sceneName - Name of the scene to add + * @param init - Function to execute when scene is played + * @param uninit - Function to execute before next scene is played, after entities with `2D` are destroyed + * This is equivalent to calling `Crafty.defineScene`. * - * The maximum number of sounds that can be played simultaneously is defined by Crafty.audio.maxChannels. The default value is 7. - */ - audio: { - - sounds: {}, - supported: null, - codecs: { // Chart from jPlayer - ogg: 'audio/ogg; codecs="vorbis"', //OGG - wav: 'audio/wav; codecs="1"', // PCM - webma: 'audio/webm; codecs="vorbis"', // WEBM - mp3: 'audio/mpeg; codecs="mp3"', //MP3 - m4a: 'audio/mp4; codecs="mp4a.40.2"' // AAC / MP4 - }, - volume: 1, //Global Volume - muted: false, - paused: false, - playCheck: null, - /** - * Function to setup supported formats - **/ - _canPlay: function () { - this.supported = {}; - // Without support, no formats are supported - if (!Crafty.support.audio) - return; - var audio = this.audioElement(), - canplay; - for (var i in this.codecs) { - canplay = audio.canPlayType(this.codecs[i]); - if (canplay !== "" && canplay !== "no") { + * @sign public void Crafty.scene(String sceneName[, Data]) + * @param sceneName - Name of scene to play + * @param Data - The init function of the scene will be called with this data as its parameter. Can be of any type other than a function. + * This is equivalent to calling `Crafty.enterScene`. + * + * Method to create scenes on the stage. Pass an ID and function to register a scene. + * + * To play a scene, just pass the ID. When a scene is played, all + * previously-created entities with the `2D` component are destroyed. The + * viewport is also reset. + * + * You can optionally specify an arugment that will be passed to the scene's init function. + * + * If you want some entities to persist over scenes (as in, not be destroyed) + * simply add the component `Persist`. + * + * @example + * ~~~ + * Crafty.defineScene("loading", function() { + * Crafty.background("#000"); + * Crafty.e("2D, DOM, Text") + * .attr({ w: 100, h: 20, x: 150, y: 120 }) + * .text("Loading") + * .css({ "text-align": "center"}) + * .textColor("#FFFFFF"); + * }); + * + * Crafty.defineScene("UFO_dance", + * function() {Crafty.background("#444"); Crafty.e("UFO");}, + * function() {...send message to server...}); + * + * // An example of an init function which accepts arguments, in this case an object. + * Crafty.defineScene("square", function(attributes) { + * Crafty.background("#000"); + * Crafty.e("2D, DOM, Color") + * .attr(attributes) + * .color("red"); + * + * }); + * + * ~~~ + * This defines (but does not play) two scenes as discussed below. + * ~~~ + * Crafty.enterScene("loading"); + * ~~~ + * This command will clear the stage by destroying all `2D` entities (except + * those with the `Persist` component). Then it will set the background to + * black and display the text "Loading". + * ~~~ + * Crafty.enterScene("UFO_dance"); + * ~~~ + * This command will clear the stage by destroying all `2D` entities (except + * those with the `Persist` component). Then it will set the background to + * gray and create a UFO entity. Finally, the next time the game encounters + * another command of the form `Crafty.scene(scene_name)` (if ever), then the + * game will send a message to the server. + * ~~~ + * Crafty.enterScene("square", {x:10, y:10, w:20, h:20}); + * ~~~ + * This will clear the stage, set the background black, and create a red square with the specified position and dimensions. + * ~~~ + */ + scene: function (name, intro, outro) { + // If there's one argument, or the second argument isn't a function, play the scene + if (arguments.length === 1 || typeof(arguments[1]) !== "function") { + Crafty.enterScene(name, arguments[1]); + return; + } + // Otherwise, this is a call to create a scene + Crafty.defineScene(name, intro, outro); + }, + + /* + * #Crafty.defineScene + * @category Scenes, Stage + * + * @sign public void Crafty.enterScene(String name[, Data]) + * @param name - Name of the scene to run. + * @param Data - The init function of the scene will be called with this data as its parameter. Can be of any type other than a function. + * + * @see Crafty.enterScene + * @see Crafty.scene + */ + defineScene: function(name, init, uninit){ + if (typeof init !== "function") + throw("Init function is the wrong type."); + this._scenes[name] = {}; + this._scenes[name].initialize = init; + if (typeof uninit !== 'undefined') { + this._scenes[name].uninitialize = uninit; + } + return; + + }, + + /* + * #Crafty.enterScene + * @category Scenes, Stage + * @trigger SceneChange - just before a new scene is initialized - { oldScene:String, newScene:String } + * @trigger SceneDestroy - just before the current scene is destroyed - { newScene:String } + * + * @sign public void Crafty.enterScene(String name[, Data]) + * @param name - Name of the scene to run. + * @param Data - The init function of the scene will be called with this data as its parameter. Can be of any type other than a function. + * + * @see Crafty.defineScene + * @see Crafty.scene + */ + enterScene: function(name, data){ + if (typeof data === "function") + throw("Scene data cannot be a function"); + + // ---FYI--- + // this._current is the name (ID) of the scene in progress. + // this._scenes is an object like the following: + // {'Opening scene': {'initialize': fnA, 'uninitialize': fnB}, + // 'Another scene': {'initialize': fnC, 'uninitialize': fnD}} + + Crafty.trigger("SceneDestroy", { + newScene: name + }); + Crafty.viewport.reset(); + + Crafty("2D").each(function () { + if (!this.has("Persist")) this.destroy(); + }); + // uninitialize previous scene + if (this._current !== null && 'uninitialize' in this._scenes[this._current]) { + this._scenes[this._current].uninitialize.call(this); + } + // initialize next scene + var oldScene = this._current; + this._current = name; + Crafty.trigger("SceneChange", { + oldScene: oldScene, + newScene: name + }); + this._scenes[name].initialize.call(this, data); + + return; + + } +}); +},{"./core.js":10}],24:[function(require,module,exports){ +var Crafty = require('./core.js'), + document = window.document; + +Crafty.extend({ + /**@ + * #Crafty.audio + * @category Audio + * + * Add sound files and play them. Chooses best format for browser support. + * Due to the nature of HTML5 audio, three types of audio files will be + * required for cross-browser capabilities. These formats are MP3, Ogg and WAV. + * When sound was not muted on before pause, sound will be unmuted after unpause. + * When sound is muted Crafty.pause() does not have any effect on sound + * + * The maximum number of sounds that can be played simultaneously is defined by Crafty.audio.maxChannels. The default value is 7. + */ + audio: { + + sounds: {}, + supported: null, + codecs: { // Chart from jPlayer + ogg: 'audio/ogg; codecs="vorbis"', //OGG + wav: 'audio/wav; codecs="1"', // PCM + webma: 'audio/webm; codecs="vorbis"', // WEBM + mp3: 'audio/mpeg; codecs="mp3"', //MP3 + m4a: 'audio/mp4; codecs="mp4a.40.2"' // AAC / MP4 + }, + volume: 1, //Global Volume + muted: false, + paused: false, + playCheck: null, + /** + * Function to setup supported formats + **/ + _canPlay: function () { + this.supported = {}; + // Without support, no formats are supported + if (!Crafty.support.audio) + return; + var audio = this.audioElement(), + canplay; + for (var i in this.codecs) { + canplay = audio.canPlayType(this.codecs[i]); + if (canplay !== "" && canplay !== "no") { this.supported[i] = true; } else { this.supported[i] = false; @@ -10376,11 +10594,9 @@ Crafty.extend({ * ~~~ * //adding audio from an object * Crafty.audio.add({ - * shoot: ["sounds/shoot.wav", - * "sounds/shoot.mp3", - * "sounds/shoot.ogg"], - * - * coin: "sounds/coin.mp3" + * shoot: ["sounds/shoot.wav", + * "sounds/shoot.mp3", + * "sounds/shoot.ogg"] * }); * * //adding a single sound @@ -10398,29 +10614,34 @@ Crafty.extend({ if (!Crafty.support.audio) return; - var src; + var src, + a; if (arguments.length === 1 && typeof id === "object") { for (var i in id) { for (src in id[i]) { - if (Crafty.audio.create(i, id[i][src])) + a = Crafty.audio.create(i, id[i][src]); + if (a){ break; + } } } } if (typeof id === "string") { if (typeof url === "string") { - Crafty.audio.create(id, url); + a = Crafty.audio.create(id, url); } if (typeof url === "object") { for (src in url) { - if (Crafty.audio.create(id, url[src])) + a = Crafty.audio.create(id, url[src]); + if (a) break; } } } + return a; }, /**@ * #Crafty.audio.play @@ -10545,6 +10766,8 @@ Crafty.extend({ * * Will stop the sound and remove all references to the audio object allowing the browser to free the memory. * If no id is given, all sounds will be removed. + * + * This function uses audio path set in Crafty.path in order to remove sound from the assets object. * * @example * ~~~ @@ -10555,12 +10778,14 @@ Crafty.extend({ if (!Crafty.support.audio) return; - var s; + var s, filename, audioFolder = Crafty.paths().audio; if (!id) { for (var i in this.sounds) { s = this.sounds[i]; + filename = s.obj.src.split('/').pop(); Crafty.audio.stop(id); + delete Crafty.assets[audioFolder + filename]; delete Crafty.assets[s.obj.src]; delete Crafty.audio.sounds[id]; } @@ -10570,7 +10795,9 @@ Crafty.extend({ return; s = this.sounds[id]; + filename = s.obj.src.split('/').pop(); Crafty.audio.stop(id); + delete Crafty.assets[audioFolder + filename]; delete Crafty.assets[s.obj.src]; delete Crafty.audio.sounds[id]; }, @@ -10674,6 +10901,7 @@ Crafty.extend({ * #Crafty.audio.pause * @comp Crafty.audio * @sign public this Crafty.audio.pause(string ID) + * @param {string} id - The id of the audio object to pause * * Pause the Audio instance specified by id param. * @@ -10682,7 +10910,6 @@ Crafty.extend({ * Crafty.audio.pause('music'); * ~~~ * - * @param {string} id The id of the audio object to pause */ pause: function (id) { if (!Crafty.support.audio || !id || !this.sounds[id]) @@ -10700,6 +10927,7 @@ Crafty.extend({ * #Crafty.audio.unpause * @comp Crafty.audio * @sign public this Crafty.audio.unpause(string ID) + * @param {string} id - The id of the audio object to unpause * * Resume playing the Audio instance specified by id param. * @@ -10708,7 +10936,6 @@ Crafty.extend({ * Crafty.audio.unpause('music'); * ~~~ * - * @param {string} id The id of the audio object to unpause */ unpause: function (id) { if (!Crafty.support.audio || !id || !this.sounds[id]) @@ -10725,6 +10952,7 @@ Crafty.extend({ * #Crafty.audio.togglePause * @comp Crafty.audio * @sign public this Crafty.audio.togglePause(string ID) + * @param {string} id - The id of the audio object to pause/ * * Toggle the pause status of the Audio instance specified by id param. * @@ -10733,7 +10961,6 @@ Crafty.extend({ * Crafty.audio.togglePause('music'); * ~~~ * - * @param {string} id The id of the audio object to pause/unpause */ togglePause: function (id) { if (!Crafty.support.audio || !id || !this.sounds[id]) @@ -10749,18 +10976,663 @@ Crafty.extend({ } } } + }, + + /**@ + * #Crafty.audio.isPlaying + * @comp Crafty.audio + * @sign public Boolean Crafty.audio.isPlaying(string ID) + * @param {string} id - The id of the audio object + * @return a Boolean indicating whether the audio is playing or not + * + * Check if audio with the given ID is playing or not (on at least one channel). + * + * @example + * ~~~ + * var isPlaying = Crafty.audio.isPlaying('music'); + * ~~~ + * + */ + isPlaying: function(id) { + if (!Crafty.support.audio) + return false; + + for (var i in this.channels) { + if (this.channels[i]._is(id)) + return true; + } + + return false; + } + } +}); + +},{"./core.js":10}],25:[function(require,module,exports){ +var Crafty = require('./core.js'), + Animation = require('./animation.js'), + document = window.document; + +/**@ +* #SpriteAnimation +* @category Animation +* @trigger StartAnimation - When an animation starts playing, or is resumed from the paused state - {Reel} +* @trigger AnimationEnd - When the animation finishes - { Reel } +* @trigger FrameChange - Each time the frame of the current reel changes - { Reel } +* @trigger ReelChange - When the reel changes - { Reel } +* +* Used to animate sprites by treating a sprite map as a set of animation frames. +* Must be applied to an entity that has a sprite-map component. +* +* To define an animation, see the `reel` method. To play an animation, see the `animate` method. +* +* A reel is an object that contains the animation frames and current state for an animation. The reel object has the following properties: +* @param id: (String) - the name of the reel +* @param frames: (Array) - A list of frames in the format [xpos, ypos] +* @param currentFrame: (Number) - The index of the current frame +* @param easing: (Crafty.easing object) - The object that handles the internal progress of the animation. +* @param duration: (Number) - The duration in milliseconds. +* +* Many animation related events pass a reel object as data. As typical with events, this should be treated as read only data that might be later altered by the entity. If you wish to preserve the data, make a copy of it. +* +* @see Crafty.sprite +*/ +Crafty.c("SpriteAnimation", { + /* + * + * A map in which the keys are the names assigned to animations defined using + * the component (also known as reelIDs), and the values are objects describing + * the animation and its state. + */ + _reels: null, + + /* + * The reelID of the currently active reel (which is one of the elements in `this._reels`). + * This value is `null` if no reel is active. Some of the component's actions can be invoked + * without specifying a reel, in which case they will work on the active reel. + */ + _currentReelId: null, + + /* + * The currently active reel. + * This value is `null` if no reel is active. + */ + _currentReel: null, + + /* + * Whether or not an animation is currently playing. + */ + _isPlaying: false, + + /**@ + * #.animationSpeed + * @comp SpriteAnimation + * + * The playback rate of the animation. This property defaults to 1. + */ + animationSpeed: 1, + + + init: function () { + this._reels = {}; + }, + + /**@ + * #.reel + * @comp SpriteAnimation + * Used to define reels, to change the active reel, and to fetch the id of the active reel. + * + * @sign public this .reel(String reelId, Duration duration, Number fromX, Number fromY, Number frameCount) + * Defines a reel by starting and ending position on the sprite sheet. + * @param reelId - ID of the animation reel being created + * @param duration - The length of the animation in milliseconds. + * @param fromX - Starting `x` position on the sprite map (x's unit is the horizontal size of the sprite in the sprite map). + * @param fromY - `y` position on the sprite map (y's unit is the horizontal size of the sprite in the sprite map). Remains constant through the animation. + * @param frameCount - The number of sequential frames in the animation. If negative, the animation will play backwards. + * + * @sign public this .reel(String reelId, Duration duration, Array frames) + * Defines a reel by an explicit list of frames + * @param reelId - ID of the animation reel being created + * @param duration - The length of the animation in milliseconds. + * @param frames - An array of arrays containing the `x` and `y` values of successive frames: [[x1,y1],[x2,y2],...] (the values are in the unit of the sprite map's width/height respectively). + * + * @sign public this .reel(String reelId) + * Switches to the specified reel. The sprite will be updated to that reel's current frame + * @param reelID - the ID to switch to + * + * @sign public Reel .reel() + * @return The id of the current reel + * + * + * A method to handle animation reels. Only works for sprites built with the Crafty.sprite methods. + * See the Tween component for animation of 2D properties. + * + * To setup an animation reel, pass the name of the reel (used to identify the reel later), and either an + * array of absolute sprite positions or the start x on the sprite map, the y on the sprite map and then the end x on the sprite map. + * + * + * @example + * ~~~ + * // Define a sprite-map component + * Crafty.sprite(16, "images/sprite.png", { + * PlayerSprite: [0,0] + * }); + * + * // Define an animation on the second row of the sprite map (fromY = 1) + * // from the left most sprite (fromX = 0) to the fourth sprite + * // on that row (frameCount = 4), with a duration of 1 second + * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite").reel('PlayerRunning', 1000, 0, 1, 4); + * + * // This is the same animation definition, but using the alternative method + * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite").reel('PlayerRunning', 1000, [[0, 1], [1, 1], [2, 1], [3, 1]]); + * ~~~ + */ + reel: function (reelId, duration, fromX, fromY, frameCount) { + // @sign public this .reel() + if (arguments.length === 0) + return this._currentReelId; + + // @sign public this .reel(String reelID) + if (arguments.length === 1 && typeof reelId === "string"){ + if (typeof this._reels[reelId] === "undefined") + throw("The specified reel " + reelId + " is undefined."); + this.pauseAnimation(); + if (this._currentReelId !== reelId) { + this._currentReelId = reelId; + this._currentReel = this._reels[reelId]; + // Change the visible sprite + this._updateSprite(); + // Trigger event + this.trigger("ReelChange", this._currentReel); + } + return this; + } + + + var reel, i; + + reel = { + id: reelId, + frames: [], + currentFrame: 0, + easing: new Crafty.easing(duration), + defaultLoops: 1 + }; + + reel.duration = reel.easing.duration; + + // @sign public this .reel(String reelId, Number duration, Number fromX, Number fromY, Number frameDuration) + if (typeof fromX === "number") { + i = fromX; + y = fromY; + if (frameCount >= 0) { + for (; i < fromX + frameCount ; i++) { + reel.frames.push([i, y]); + } + } + else { + for (; i > fromX + frameCount; i--) { + reel.frames.push([i, y]); + } + } + } + // @sign public this .reel(String reelId, Number duration, Array frames) + else if (arguments.length === 3 && typeof fromX === "object") { + reel.frames = fromX; + } + else { + throw "Urecognized arguments. Please see the documentation for 'reel(...)'."; + } + + this._reels[reelId] = reel; + + return this; + }, + + /**@ + * #.animate + * @comp SpriteAnimation + * @sign public this .animate([String reelId] [, Number loopCount]) + * @param reelId - ID of the animation reel to play. Defaults to the current reel if none is specified. + * @param loopCount - Number of times to repeat the animation. Use -1 to repeat indefinitely. Defaults to 1. + * + * Play one of the reels previously defined through `.reel(...)`. Simply pass the name of the reel. If you wish the + * animation to play multiple times in succession, pass in the amount of times as an additional parameter. + * To have the animation repeat indefinitely, pass in `-1`. + * + * If another animation is currently playing, it will be paused. + * + * This will always play an animation from the beginning. If you wish to resume from the current state of a reel, use `resumeAnimation()`. + * + * Once an animation ends, it will remain at its last frame. + * + * + * @example + * ~~~ + * // Define a sprite-map component + * Crafty.sprite(16, "images/sprite.png", { + * PlayerSprite: [0,0] + * }); + * + * // Play the animation across 20 frames (so each sprite in the 4 sprite animation should be seen for 5 frames) and repeat indefinitely + * Crafty.e("2D, DOM, SpriteAnimation, PlayerSprite") + * .reel('PlayerRunning', 20, 0, 0, 3) // setup animation + * .animate('PlayerRunning', -1); // start animation + * ~~~ + */ + animate: function(reelId, loopCount) { + + var pos; + + + // switch to the specified reel if necessary + if (typeof reelId === "string") + this.reel(reelId); + + var currentReel = this._currentReel; + + if (typeof currentReel === "undefined" || currentReel === null) + throw("No reel is specified, and there is no currently active reel."); + + this.pauseAnimation(); // This will pause the current animation, if one is playing + + // Handle repeats; if loopCount is undefined and reelID is a number, calling with that signature + if (typeof loopCount === "undefined") + if (typeof reelId === "number") + loopCount = reelId; + else + loopCount = 1; + + // set the animation to the beginning + currentReel.easing.reset(); + + + // user provided loop count. + this.loops(loopCount); + + // trigger the necessary events and switch to the first frame + this._setFrame(0); + + // Start the anim + this.bind("EnterFrame", this._animationTick); + this._isPlaying = true; + + this.trigger("StartAnimation", currentReel); + return this; + }, + + /**@ + * #.resumeAnimation + * @comp SpriteAnimation + * @sign public this .resumeAnimation() + * + * This will resume animation of the current reel from its current state. + * If a reel is already playing, or there is no current reel, there will be no effect. + */ + resumeAnimation: function() { + if (this._isPlaying === false && this._currentReel !== null) { + this.bind("EnterFrame", this._animationTick); + this._isPlaying = true; + this._currentReel.easing.resume(); + this.trigger("StartAnimation", this._currentReel); + } + return this; + }, + + /**@ + * #.pauseAnimation + * @comp SpriteAnimation + * @sign public this .pauseAnimation(void) + * + * Pauses the currently playing animation, or does nothing if no animation is playing. + */ + pauseAnimation: function () { + if (this._isPlaying === true) { + this.unbind("EnterFrame", this._animationTick); + this._isPlaying = false; + this._reels[this._currentReelId].easing.pause(); + } + return this; + }, + + /**@ + * #.resetAnimation + * @comp SpriteAnimation + * @sign public this .resetAnimation() + * + * Resets the current animation to its initial state. Resets the number of loops to the last specified value, which defaults to 1. + * + * Neither pauses nor resumes the current animation. + */ + resetAnimation: function(){ + var currentReel = this._currentReel; + if (currentReel === null) + throw("No active reel to reset."); + this.reelPosition(0); + currentReel.easing.repeat(currentReel.defaultLoops); + return this; + }, + + + /**@ + * #.loops + * @comp SpriteAnimation + * @sign public this .loops(Number loopCount) + * @param loopCount - The number of times to play the animation + * + * Sets the number of times the animation will loop for. + * If called while an animation is in progress, the current state will be considered the first loop. + * + * @sign public Number .loops() + * @returns The number of loops left. Returns 0 if no reel is active. + */ + loops: function(loopCount) { + if (arguments.length === 0){ + if (this._currentReel !== null) + return this._currentReel.easing.loops; + else + return 0; + } + + if (this._currentReel !== null){ + if (loopCount < 0) + loopCount = Infinity; + this._currentReel.easing.repeat(loopCount); + this._currentReel.defaultLoops = loopCount; + } + return this; + + }, + + /**@ + * #.reelPosition + * @comp SpriteAnimation + * + * @sign public this .reelPosition(Integer position) + * Sets the position of the current reel by frame number. + * @param position - the frame to jump to. This is zero-indexed. A negative values counts back from the last frame. + * + * @sign public this .reelPosition(Number position) + * Sets the position of the current reel by percent progress. + * @param position - a non-integer number between 0 and 1 + * + * @sign public this .reelPosition(String position) + * Jumps to the specified position. The only currently accepted value is "end", which will jump to the end of the reel. + * + * @sign public Number .reelPosition() + * @returns The current frame number + * + */ + reelPosition: function(position) { + if (this._currentReel === null) + throw("No active reel."); + + if (arguments.length === 0) + return this._currentReel.currentFrame; + + var progress, + l = this._currentReel.frames.length; + if (position === "end") + position = l - 1; + + if (position < 1 && position > 0) { + progress = position; + position = Math.floor(l * progress); + } else { + if (position !== Math.floor(position)) + throw("Position " + position + " is invalid."); + if (position < 0) + position = l - 1 + position; + progress = position / l; + } + // cap to last frame + position = Math.min(position, l-1); + position = Math.max(position, 0); + this._setProgress(progress); + this._setFrame(position); + + return this; + + }, + + + // Bound to "EnterFrame". Progresses the animation by dt, changing the frame if necessary. + // dt is multiplied by the animationSpeed property + _animationTick: function(frameData) { + var currentReel = this._reels[this._currentReelId]; + currentReel.easing.tick(frameData.dt * this.animationSpeed); + var progress = currentReel.easing.value(); + var frameNumber = Math.min( Math.floor(currentReel.frames.length * progress), currentReel.frames.length - 1); + + this._setFrame(frameNumber); + + if(currentReel.easing.complete === true){ + this.pauseAnimation(); + this.trigger("AnimationEnd", this._currentReel); + } + }, + + + + + + // Set the current frame and update the displayed sprite + // The actual progress for the animation must be set seperately. + _setFrame: function(frameNumber) { + var currentReel = this._currentReel; + if (frameNumber === currentReel.currentFrame) + return; + currentReel.currentFrame = frameNumber; + this._updateSprite(); + this.trigger("FrameChange", currentReel); + }, + + // Update the displayed sprite. + _updateSprite: function() { + var currentReel = this._currentReel; + var pos = currentReel.frames[currentReel.currentFrame]; + this.sprite(pos[0], pos[1]); // .sprite will trigger redraw + + }, + + + // Sets the internal state of the current reel's easing object + _setProgress: function(progress, repeats) { + this._currentReel.easing.setProgress(progress, repeats); + + }, + + + /**@ + * #.isPlaying + * @comp SpriteAnimation + * @sign public Boolean .isPlaying([String reelId]) + * @param reelId - The reelId of the reel we wish to examine + * @returns The current animation state + * + * Determines if the specified animation is currently playing. If no reelId is specified, + * checks if any animation is playing. + * + * @example + * ~~~ + * myEntity.isPlaying() // is any animation playing + * myEntity.isPlaying('PlayerRunning') // is the PlayerRunning animation playing + * ~~~ + */ + isPlaying: function (reelId) { + if (!this._isPlaying) return false; + + if (!reelId) return !!this._currentReelId; + return this._currentReelId === reelId; + }, + + /**@ + * #.getReel + * @comp SpriteAnimation + * @sign public Reel .getReel() + * @returns The current reel, or null if there is no active reel + * + * @sign public Reel .getReel(reelId) + * @param reelId - The id of the reel to fetch. + * @returns The specified reel, or `undefined` if no such reel exists. + * + */ + getReel: function (reelId) { + if (arguments.length === 0){ + if (!this._currentReelId) return null; + reelId = this._currentReelId; + } + + return this._reels[reelId]; + } +}); + +},{"./animation.js":5,"./core.js":10}],26:[function(require,module,exports){ +var Crafty = require('./core.js'), + document = window.document; + +Crafty.extend({ + + /**@ + * #Crafty.sprite + * @category Graphics + * @sign public this Crafty.sprite([Number tile, [Number tileh]], String url, Object map[, Number paddingX[, Number paddingY[, Boolean paddingAroundBorder]]]) + * @param tile - Tile size of the sprite map, defaults to 1 + * @param tileh - Height of the tile; if provided, tile is interpreted as the width + * @param url - URL of the sprite image + * @param map - Object where the key is what becomes a new component and the value points to a position on the sprite map + * @param paddingX - Horizontal space in between tiles. Defaults to 0. + * @param paddingY - Vertical space in between tiles. Defaults to paddingX. + * @param paddingAroundBorder - If padding should be applied around the border of the sprite sheet. If enabled the first tile starts at (paddingX,paddingY) instead of (0,0). Defaults to false. + * Generates components based on positions in a sprite image to be applied to entities. + * + * Accepts a tile size, URL and map for the name of the sprite and its position. + * + * The position must be an array containing the position of the sprite where index `0` + * is the `x` position, `1` is the `y` position and optionally `2` is the width and `3` + * is the height. If the sprite map has padding, pass the values for the `x` padding + * or `y` padding. If they are the same, just add one value. + * + * If the sprite image has no consistent tile size, `1` or no argument need be + * passed for tile size. + * + * Entities that add the generated components are also given the `2D` component, and + * a component called `Sprite`. + * + * @example + * ~~~ + * Crafty.sprite("imgs/spritemap6.png", {flower:[0,0,20,30]}); + * var flower_entity = Crafty.e("2D, DOM, flower"); + * ~~~ + * The first line creates a component called `flower` associated with the sub-image of + * spritemap6.png with top-left corner (0,0), width 20 pixels, and height 30 pixels. + * The second line creates an entity with that image. (Note: The `2D` is not really + * necessary here, because adding the `flower` component automatically also adds the + * `2D` component.) + * ~~~ + * Crafty.sprite(50, "imgs/spritemap6.png", {flower:[0,0], grass:[0,1,3,1]}); + * ~~~ + * In this case, the `flower` component is pixels 0 <= x < 50, 0 <= y < 50, and the + * `grass` component is pixels 0 <= x < 150, 50 <= y < 100. (The `3` means grass has a + * width of 3 tiles, i.e. 150 pixels.) + * ~~~ + * Crafty.sprite(50, 100, "imgs/spritemap6.png", {flower:[0,0], grass:[0,1]}, 10); + * ~~~ + * In this case, each tile is 50x100, and there is a spacing of 10 pixels between + * consecutive tiles. So `flower` is pixels 0 <= x < 50, 0 <= y < 100, and `grass` is + * pixels 0 <= x < 50, 110 <= y < 210. + * + * @see Sprite + */ + sprite: function (tile, tileh, url, map, paddingX, paddingY, paddingAroundBorder) { + var spriteName, temp, x, y, w, h, img; + + //if no tile value, default to 1. + //(if the first passed argument is a string, it must be the url.) + if (typeof tile === "string") { + paddingY = paddingX; + paddingX = map; + map = tileh; + url = tile; + tile = 1; + tileh = 1; + } + + if (typeof tileh == "string") { + paddingY = paddingX; + paddingX = map; + map = url; + url = tileh; + tileh = tile; + } + + //if no paddingY, use paddingX + if (!paddingY && paddingX) paddingY = paddingX; + paddingX = parseInt(paddingX || 0, 10); //just incase + paddingY = parseInt(paddingY || 0, 10); + + var markSpritesReady = function() { + this.ready = true; + this.trigger("Invalidate"); + }; + + img = Crafty.asset(url); + if (!img) { + img = new Image(); + img.src = url; + Crafty.asset(url, img); + img.onload = function () { + //all components with this img are now ready + for (var spriteName in map) { + Crafty(spriteName).each(markSpritesReady); + } + }; + } + + var sharedSpriteInit = function() { + this.requires("2D, Sprite"); + this.__trim = [0, 0, 0, 0]; + this.__image = url; + this.__coord = [this.__coord[0], this.__coord[1], this.__coord[2], this.__coord[3]]; + this.__tile = tile; + this.__tileh = tileh; + this.__padding = [paddingX, paddingY]; + this.__padBorder = paddingAroundBorder; + this.sprite(this.__coord[0], this.__coord[1], this.__coord[2], this.__coord[3]); + + this.img = img; + //draw now + if (this.img.complete && this.img.width > 0) { + this.ready = true; + this.trigger("Invalidate"); + } + + //set the width and height to the sprite size + this.w = this.__coord[2]; + this.h = this.__coord[3]; + }; + + for (spriteName in map) { + if (!map.hasOwnProperty(spriteName)) continue; + + temp = map[spriteName]; + + //generates sprite components for each tile in the map + Crafty.c(spriteName, { + ready: false, + __coord: [temp[0], temp[1], temp[2] || 1, temp[3] || 1], + + init: sharedSpriteInit + }); } + + return this; } }); -},{"./core.js":9}],21:[function(require,module,exports){ -var Crafty = require('./core.js'), - document = window.document; - /**@ * #Sprite * @category Graphics - * @trigger Change - when the sprites change + * @trigger Invalidate - when the sprites change * Component for using tiles in a sprite map. */ Crafty.c("Sprite", { @@ -10812,7 +11684,8 @@ Crafty.c("Sprite", { hscale = this._w / co.w, style = this._element.style; - style.background = style.backgroundColor + " url('" + this.__image + "') no-repeat -" + co.x * hscale + "px -" + co.y * vscale + "px"; + style.background = style.backgroundColor + " url('" + this.__image + "') no-repeat"; + style.backgroundPosition = "-" + co.x * hscale + "px -" + co.y * vscale + "px"; // style.backgroundSize must be set AFTER style.background! if (vscale != 1 || hscale != 1) { style.backgroundSize = (this.img.width * hscale) + "px" + " " + (this.img.height * vscale) + "px"; @@ -10825,705 +11698,188 @@ Crafty.c("Sprite", { }); }, - /**@ - * #.sprite - * @comp Sprite - * @sign public this .sprite(Number x, Number y, Number w, Number h) - * @param x - X cell position - * @param y - Y cell position - * @param w - Width in cells - * @param h - Height in cells - * - * Uses a new location on the sprite map as its sprite. - * - * Values should be in tiles or cells (not pixels). - * - * @example - * ~~~ - * Crafty.e("2D, DOM, Sprite") - * .sprite(0, 0, 2, 2); - * ~~~ - */ - - /**@ - * #.__coord - * @comp Sprite - * - * The coordinate of the slide within the sprite in the format of [x, y, w, h]. - */ - sprite: function (x, y, w, h) { - this.__coord = [x * (this.__tile + this.__padding[0]) + this.__trim[0], - y * (this.__tileh + this.__padding[1]) + this.__trim[1], - this.__trim[2] || w * this.__tile || this.__tile, - this.__trim[3] || h * this.__tileh || this.__tileh - ]; - - this.trigger("Change"); - return this; - }, - - /**@ - * #.crop - * @comp Sprite - * @sign public this .crop(Number x, Number y, Number w, Number h) - * @param x - Offset x position - * @param y - Offset y position - * @param w - New width - * @param h - New height - * - * If the entity needs to be smaller than the tile size, use this method to crop it. - * - * The values should be in pixels rather than tiles. - * - * @example - * ~~~ - * Crafty.e("2D, DOM, Sprite") - * .crop(40, 40, 22, 23); - * ~~~ - */ - crop: function (x, y, w, h) { - var old = this._mbr || this.pos(); - this.__trim = []; - this.__trim[0] = x; - this.__trim[1] = y; - this.__trim[2] = w; - this.__trim[3] = h; - - this.__coord[0] += x; - this.__coord[1] += y; - this.__coord[2] = w; - this.__coord[3] = h; - this._w = w; - this._h = h; - - this.trigger("Change", old); - return this; - } -}); -},{"./core.js":9}],22:[function(require,module,exports){ -var Crafty = require('./core.js'), - document = window.document; - -// Directive for jshint to ignore evals -// eval is used to support IE8, so this can be removed if we decide to drop that -/* jshint evil:true */ - -/**@ - * #Storage - * @category Utilities - * Utility to allow data to be saved to a permanent storage solution: IndexedDB, WebSql, localstorage or cookies - */ -/**@ - * #.open - * @comp Storage - * @sign .open(String gameName) - * @param gameName - a machine readable string to uniquely identify your game - * - * Opens a connection to the database. If the best they have is localstorage or lower, it does nothing - * - * @example - * Open a database - * ~~~ - * Crafty.storage.open('MyGame'); - * ~~~ - */ -/**@ - * #.save - * @comp Storage - * @sign .save(String key, String type, Mixed data) - * @param key - A unique key for identifying this piece of data - * @param type - 'save' or 'cache' - * @param data - Some kind of data. - * - * Saves a piece of data to the database. Can be anything, although entities are preferred. - * For all storage methods but IndexedDB, the data will be serialized as a string - * During serialization, an entity's SaveData event will be triggered. - * Components should implement a SaveData handler and attach the necessary information to the passed object - * - * @example - * Saves an entity to the database - * ~~~ - * var ent = Crafty.e("2D, DOM") - * .attr({x: 20, y: 20, w: 100, h:100}); - * Crafty.storage.open('MyGame'); - * Crafty.storage.save('MyEntity', 'save', ent); - * ~~~ - */ -/**@ - * #.load - * @comp Storage - * @sign .load(String key, String type) - * @param key - A unique key to search for - * @param type - 'save' or 'cache' - * @param callback - Do things with the data you get back - * - * Loads a piece of data from the database. - * Entities will be reconstructed from the serialized string - - * @example - * Loads an entity from the database - * ~~~ - * Crafty.storage.open('MyGame'); - * Crafty.storage.load('MyEntity', 'save', function (data) { // do things }); - * ~~~ - */ -/**@ - * #.getAllKeys - * @comp Storage - * @sign .getAllKeys(String type) - * @param type - 'save' or 'cache' - * Gets all the keys for a given type - - * @example - * Gets all the save games saved - * ~~~ - * Crafty.storage.open('MyGame'); - * var saves = Crafty.storage.getAllKeys('save'); - * ~~~ - */ -/**@ - * #.external - * @comp Storage - * @sign .external(String url) - * @param url - URL to an external to save games too - * - * Enables and sets the url for saving games to an external server - * - * @example - * Save an entity to an external server - * ~~~ - * Crafty.storage.external('http://somewhere.com/server.php'); - * Crafty.storage.open('MyGame'); - * var ent = Crafty.e('2D, DOM') - * .attr({x: 20, y: 20, w: 100, h:100}); - * Crafty.storage.save('save01', 'save', ent); - * ~~~ - */ -/**@ - * #SaveData event - * @comp Storage - * @param data - An object containing all of the data to be serialized - * @param prepare - The function to prepare an entity for serialization - * - * Any data a component wants to save when it's serialized should be added to this object. - * Straight attribute should be set in data.attr. - * Anything that requires a special handler should be set in a unique property. - * - * @example - * Saves the innerHTML of an entity - * ~~~ - * Crafty.e("2D DOM").bind("SaveData", function (data, prepare) { - * data.attr.x = this.x; - * data.attr.y = this.y; - * data.dom = this.element.innerHTML; - * }); - * ~~~ - */ -/**@ - * #LoadData event - * @comp Storage - * @param data - An object containing all the data that been saved - * @param process - The function to turn a string into an entity - * - * Handlers for processing any data that needs more than straight assignment - * - * Note that data stored in the .attr object is automatically added to the entity. - * It does not need to be handled here - * - * @example - * ~~~ - * Sets the innerHTML from a saved entity - * Crafty.e("2D DOM").bind("LoadData", function (data, process) { - * this.element.innerHTML = data.dom; - * }); - * ~~~ - */ -Crafty.storage = (function () { - var db = null, - url, gameName, timestamps = {}, - transactionType = { - READ: "readonly", - READ_WRITE: "readwrite" - }; - - /* - * Processes a retrieved object. - * Creates an entity if it is one - */ - - function process(obj) { - if (obj.c) { - var d = Crafty.e(obj.c) - .attr(obj.attr) - .trigger('LoadData', obj, process); - return d; - } else if (typeof obj == 'object') { - for (var prop in obj) { - obj[prop] = process(obj[prop]); - } - } - return obj; - } - - function unserialize(str) { - if (typeof str != 'string') return null; - var data = (JSON ? JSON.parse(str) : eval('(' + str + ')')); - return process(data); - } - - /* recursive function - * searches for entities in an object and processes them for serialization - */ - - function prep(obj) { - if (obj.__c) { - // object is entity - var data = { - c: [], - attr: {} - }; - obj.trigger("SaveData", data, prep); - for (var i in obj.__c) { - data.c.push(i); - } - data.c = data.c.join(', '); - obj = data; - } else if (typeof obj == 'object') { - // recurse and look for entities - for (var prop in obj) { - obj[prop] = prep(obj[prop]); - } - } - return obj; - } - - function serialize(e) { - if (JSON) { - var data = prep(e); - return JSON.stringify(data); - } else { - alert("Crafty does not support saving on your browser. Please upgrade to a newer browser."); - return false; - } - } - - // for saving a game to a central server - - function external(setUrl) { - url = setUrl; - } - - function openExternal() { - if (1 && typeof url == "undefined") return; - // get the timestamps for external saves and compare them to local - // if the external is newer, load it - - var xml = new XMLHttpRequest(); - xhr.open("POST", url); - xhr.onreadystatechange = function (evt) { - if (xhr.readyState == 4) { - if (xhr.status == 200) { - var data = eval("(" + xhr.responseText + ")"); - for (var i in data) { - if (Crafty.storage.check(data[i].key, data[i].timestamp)) { - loadExternal(data[i].key); - } - } - } - } - }; - xhr.send("mode=timestamps&game=" + gameName); - } - - function saveExternal(key, data, ts) { - if (1 && typeof url == "undefined") return; - var xhr = new XMLHttpRequest(); - xhr.open("POST", url); - xhr.send("mode=save&key=" + key + "&data=" + encodeURIComponent(data) + "&ts=" + ts + "&game=" + gameName); - } - - function loadExternal(key) { - if (1 && typeof url == "undefined") return; - var xhr = new XMLHttpRequest(); - xhr.open("POST", url); - xhr.onreadystatechange = function (evt) { - if (xhr.readyState == 4) { - if (xhr.status == 200) { - var data = eval("(" + xhr.responseText + ")"); - Crafty.storage.save(key, 'save', data); - } - } - }; - xhr.send("mode=load&key=" + key + "&game=" + gameName); - } - - /** - * get timestamp - */ - - function ts() { - var d = new Date(); - return d.getTime(); - } - - // everyone names their object different. Fix that nonsense. - if (typeof indexedDB != 'object') { - window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; - window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction; - window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange; - - /* Numeric constants for transaction type are deprecated - * Ensure that the script will work consistenly for recent and legacy browser versions - */ - if (typeof IDBTransaction == 'object') { - transactionType.READ = IDBTransaction.READ || IDBTransaction.readonly || transactionType.READ || 'read'; - transactionType.READ_WRITE = IDBTransaction.READ_WRITE || IDBTransaction.readwrite || transactionType.READ_WRITE || 'readwrite'; - } - } - - if (typeof indexedDB == 'object') { - - return { - open: function (gameName_n) { - gameName = gameName_n; - var stores = []; - - if (arguments.length == 1) { - stores.push('save'); - stores.push('cache'); - } else { - stores = arguments; - stores.shift(); - stores.push('save'); - stores.push('cache'); - } - if (db === null) { - var request = indexedDB.open(gameName); - request.onsuccess = function (e) { - db = e.target.result; - getTimestamps(); - openExternal(); - }; - request.onupgradeneeded = function (e) { - createStores(); - }; - } else { - createStores(); - getTimestamps(); - openExternal(); - } - - // get all the timestamps for existing keys - - function getTimestamps() { - try { - var trans = db.transaction(['save'], "read"), - store = trans.objectStore('save'), - request = store.getAll(); - request.onsuccess = function (e) { - var i = 0, - a = event.target.result, - l = a.length; - for (; i < l; i++) { - timestamps[a[i].key] = a[i].timestamp; - } - }; - } catch (e) {} - } - - function createStores() { - var request = db.setVersion("1.0"); - request.onsuccess = function (e) { - for (var i = 0; i < stores.length; i++) { - var st = stores[i]; - if (db.objectStoreNames.contains(st)) continue; - var store = db.createObjectStore(st, { - keyPath: "key" - }); - } - }; - } - }, - - save: function (key, type, data, callback) { - if (db === null) { - setTimeout(function () { - Crafty.storage.save(key, type, data); - }, 1); - return; - } - - var str = serialize(data), - t = ts(); - if (type == 'save') saveExternal(key, str, t); - try { - var request = db.transaction([type], transactionType.READ_WRITE).objectStore(type).add({ - "data": str, - "timestamp": t, - "key": key - }); - if (typeof callback == 'function') { - request.onsuccess = callback; - } - } catch (e) { - console.error(e); - } - }, - - load: function (key, type, callback) { - if (db === null) { - setTimeout(function () { - Crafty.storage.load(key, type, callback); - }, 1); - return; - } - try { - var request = db.transaction([type], transactionType.READ).objectStore(type).get(key); - request.onsuccess = function (e) { - callback(unserialize(e.target.result.data)); - }; - } catch (e) { - console.error(e); - } - }, - - getAllKeys: function (type, callback) { - if (db === null) { - setTimeout(function () { - Crafty.storage.getAllkeys(type, callback); - }, 1); - } - try { - var request = db.transaction([type], transactionType.READ).objectStore(type).openCursor(), - res = []; - request.onsuccess = function (e) { - var cursor = e.target.result; - if (cursor) { - res.push(cursor.key); - // 'continue' is a reserved word, so .continue() causes IE8 to completely bark with "SCRIPT1010: Expected identifier". - cursor['continue'](); - } else { - callback(res); - } - }; - } catch (e) { - console.error(e); - } - }, - - check: function (key, timestamp) { - return (timestamps[key] > timestamp); - }, - - external: external - }; - } else if (typeof openDatabase == 'function') { - return { - open: function (gameName_n) { - gameName = gameName_n; - if (arguments.length == 1) { - db = { - save: openDatabase(gameName_n + '_save', '1.0', 'Saves games for ' + gameName_n, 5 * 1024 * 1024), - cache: openDatabase(gameName_n + '_cache', '1.0', 'Cache for ' + gameName_n, 5 * 1024 * 1024) - }; - } else { - // allows for any other types that can be thought of - var args = arguments, - i = 0; - args.shift(); - for (; i < args.length; i++) { - if (typeof db[args[i]] == 'undefined') - db[args[i]] = openDatabase(gameName + '_' + args[i], '1.0', type, 5 * 1024 * 1024); - } - } - - db.save.transaction(function (tx) { - tx.executeSql('SELECT key, timestamp FROM data', [], function (tx, res) { - var i = 0, - a = res.rows, - l = a.length; - for (; i < l; i++) { - timestamps[a.item(i).key] = a.item(i).timestamp; - } - }); - }); - }, - - save: function (key, type, data) { - if (typeof db[type] == 'undefined' && gameName !== '') { - this.open(gameName, type); - } - - var str = serialize(data), - t = ts(); - if (type == 'save') saveExternal(key, str, t); - db[type].transaction(function (tx) { - tx.executeSql('CREATE TABLE IF NOT EXISTS data (key unique, text, timestamp)'); - tx.executeSql('SELECT * FROM data WHERE key = ?', [key], function (tx, results) { - if (results.rows.length) { - tx.executeSql('UPDATE data SET text = ?, timestamp = ? WHERE key = ?', [str, t, key]); - } else { - tx.executeSql('INSERT INTO data VALUES (?, ?, ?)', [key, str, t]); - } - }); - }); - }, - - load: function (key, type, callback) { - if (typeof db[type] === 'undefined') { - setTimeout(function () { - Crafty.storage.load(key, type, callback); - }, 1); - return; - } - db[type].transaction(function (tx) { - tx.executeSql('SELECT text FROM data WHERE key = ?', [key], function (tx, results) { - if (results.rows.length) { - res = unserialize(results.rows.item(0).text); - callback(res); - } - }); - }); - }, - - getAllKeys: function (type, callback) { - if (typeof db[type] === 'undefined') { - setTimeout(function () { - Crafty.storage.getAllKeys(type, callback); - }, 1); - return; - } - db[type].transaction(function (tx) { - tx.executeSql('SELECT key FROM data', [], function (tx, results) { - callback(results.rows); - }); - }); - }, - - check: function (key, timestamp) { - return (timestamps[key] > timestamp); - }, - - external: external - }; - } else if (typeof window.localStorage == 'object') { - return { - open: function (gameName_n) { - gameName = gameName_n; - }, - - save: function (key, type, data) { - var k = gameName + '.' + type + '.' + key, - str = serialize(data), - t = ts(); - if (type == 'save') saveExternal(key, str, t); - window.localStorage[k] = str; - if (type == 'save') - window.localStorage[k + '.ts'] = t; - }, + /**@ + * #.sprite + * @comp Sprite + * @sign public this .sprite(Number x, Number y[, Number w, Number h]) + * @param x - X cell position + * @param y - Y cell position + * @param w - Width in cells. Optional. + * @param h - Height in cells. Optional. + * + * Uses a new location on the sprite map as its sprite. If w or h are ommitted, the width and height are not changed. + * + * Values should be in tiles or cells (not pixels). + * + * @example + * ~~~ + * Crafty.e("2D, DOM, Sprite") + * .sprite(0, 0, 2, 2); + * ~~~ + */ - load: function (key, type, callback) { - var k = gameName + '.' + type + '.' + key, - str = window.localStorage[k]; + /**@ + * #.__coord + * @comp Sprite + * + * The coordinate of the slide within the sprite in the format of [x, y, w, h]. + */ + sprite: function (x, y, w, h) { + this.__coord = this.__coord || [0, 0, 0, 0]; - callback(unserialize(str)); - }, + this.__coord[0] = x * (this.__tile + this.__padding[0]) + (this.__padBorder ? this.__padding[0] : 0) + this.__trim[0]; + this.__coord[1] = y * (this.__tileh + this.__padding[1]) + (this.__padBorder ? this.__padding[1] : 0) + this.__trim[1]; + if (typeof(w)!=='undefined' && typeof(h)!=='undefined') { + this.__coord[2] = this.__trim[2] || w * this.__tile || this.__tile; + this.__coord[3] = this.__trim[3] || h * this.__tileh || this.__tileh; + } - getAllKeys: function (type, callback) { - var res = {}, output = [], - header = gameName + '.' + type; - for (var i in window.localStorage) { - if (i.indexOf(header) != -1) { - var key = i.replace(header, '').replace('.ts', ''); - res[key] = true; - } - } - for (i in res) { - output.push(i); - } - callback(output); - }, + this.trigger("Invalidate"); + return this; + }, - check: function (key, timestamp) { - var ts = window.localStorage[gameName + '.save.' + key + '.ts']; + /**@ + * #.crop + * @comp Sprite + * @sign public this .crop(Number x, Number y, Number w, Number h) + * @param x - Offset x position + * @param y - Offset y position + * @param w - New width + * @param h - New height + * + * If the entity needs to be smaller than the tile size, use this method to crop it. + * + * The values should be in pixels rather than tiles. + * + * @example + * ~~~ + * Crafty.e("2D, DOM, Sprite") + * .crop(40, 40, 22, 23); + * ~~~ + */ + crop: function (x, y, w, h) { + var old = this._mbr || this.pos(); + this.__trim = []; + this.__trim[0] = x; + this.__trim[1] = y; + this.__trim[2] = w; + this.__trim[3] = h; - return (parseInt(timestamp, 10) > parseInt(ts, 10)); - }, + this.__coord[0] += x; + this.__coord[1] += y; + this.__coord[2] = w; + this.__coord[3] = h; + this._w = w; + this._h = h; - external: external - }; - } else { - // default fallback to cookies - return { - open: function (gameName_n) { - gameName = gameName_n; - }, + this.trigger("Invalidate", old); + return this; + } +}); +},{"./core.js":10}],27:[function(require,module,exports){ +var Crafty = require('./core.js'), + document = window.document; - save: function (key, type, data) { - // cookies are very limited in space. we can only keep saves there - if (type != 'save') return; - var str = serialize(data), - t = ts(); - if (type == 'save') saveExternal(key, str, t); - document.cookie = gameName + '_' + key + '=' + str + '; ' + gameName + '_' + key + '_ts=' + t + '; expires=Thur, 31 Dec 2099 23:59:59 UTC; path=/'; - }, +/**@ + * #Storage + * @category Utilities + * Very simple way to get and set values, which will persist when the browser is closed also. Storage wraps around HTML5 Web Storage, which is well-supported across browsers and platforms, but limited to 5MB total storage per domain. + */ +/**@ + * #.storage + * @comp Storage + * @sign .storage(String key) + * @param key - a key you would like to get from the storage. It will return null if the key does not exists. + * @sign .storage(String key, String value) + * @param key - the key you would like to save the data under. + * @param value - the value you would like to save. + * @sign .storage(String key, [Object value, Array value, Boolean value]) + * @param key - the key you would like to save the data under. + * @param value - the value you would like to save, can be an Object or an Array. + * + * Storage function is very simple and can be used to either get or set values. + * You can store both booleans, strings, objects and arrays. + * + * Please note: You should not store data, while the game is playing, as it can cause the game to slow down. You should load data when you start the game, or when the user for an example click a "Save gameprocess" button. + * + * @example + * Get an already stored value + * ~~~ + * var playername = Crafty.storage('playername'); + * ~~~ + * + * @example + * Save a value + * ~~~ + * Crafty.storage('playername', 'Hero'); + * ~~~ + * + * @example + * Test to see if a value is already there. + * ~~~ + * var heroname = Crafty.storage('name'); + * if(!heroname){ + * // Maybe ask the player what their name is here + * heroname = 'Guest'; + * } + * // Do something with heroname + * ~~~ + */ - load: function (key, type, callback) { - if (type != 'save') return; - var reg = new RegExp(gameName + '_' + key + '=[^;]*'), - result = reg.exec(document.cookie), - data = unserialize(result[0].replace(gameName + '_' + key + '=', '')); +Crafty.storage = function(key, value){ + var storage = window.localStorage, + _value = value; - callback(data); - }, + if(!storage){ + return false; + } - getAllKeys: function (type, callback) { - if (type != 'save') return; - var reg = new RegExp(gameName + '_[^_=]', 'g'), - matches = reg.exec(document.cookie), - i = 0, - l = matches.length, - res = {}, output = []; - for (; i < l; i++) { - var key = matches[i].replace(gameName + '_', ''); - res[key] = true; - } - for (i in res) { - output.push(i); - } - callback(output); - }, + if(arguments.length === 1) { + try { + return JSON.parse(storage.getItem(key)); + } + catch (e) { + return storage.getItem(key); + } + } else { + if(typeof value === "object") { + _value = JSON.stringify(value); + } - check: function (key, timestamp) { - var header = gameName + '_' + key + '_ts', - reg = new RegExp(header + '=[^;]'), - result = reg.exec(document.cookie), - ts = result[0].replace(header + '=', ''); + storage.setItem(key, _value); + + } - return (parseInt(timestamp, 10) > parseInt(ts, 10)); - }, +}; +/**@ + * #.storage.remove + * @comp Storage + * @sign .storage.remove(String key) + * @param key - a key where you will like to delete the value of. + * + * Generally you do not need to remove values from localStorage, but if you do + * store large amount of text, or want to unset something you can do that with + * this function. + * + * @example + * Get an already stored value + * ~~~ + * Crafty.storage.remove('playername'); + * ~~~ + * + */ +Crafty.storage.remove = function(key){ + window.localStorage.removeItem(key); +}; - external: external - }; - } - /* template - return { - open: function (gameName) { - }, - save: function (key, type, data) { - }, - load: function (key, type, callback) { - }, - }*/ -})(); -},{"./core.js":9}],23:[function(require,module,exports){ +},{"./core.js":10}],28:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; /**@ * #Text * @category Graphics - * @trigger Change - when the text is changed + * @trigger Invalidate - when the text is changed * @requires Canvas or DOM * Component to make a text entity. * @@ -11547,6 +11903,8 @@ Crafty.c("Text", { _text: "", defaultSize: "10px", defaultFamily: "sans-serif", + defaultVariant: "normal", + defaultLineHeight: "normal", ready: true, init: function () { @@ -11555,7 +11913,9 @@ Crafty.c("Text", { "type": "", "weight": "", "size": this.defaultSize, - "family": this.defaultFamily + "lineHeight":this.defaultLineHeight, + "family": this.defaultFamily, + "variant": this.defaultVariant }; this.bind("Draw", function (e) { @@ -11577,7 +11937,7 @@ Crafty.c("Text", { context.fillStyle = this._textColor || "rgb(0,0,0)"; context.font = font; - context.fillText(this._text, this._x, this._y); + context.fillText(this._text, e.pos._x, e.pos._y); context.restore(); } @@ -11596,11 +11956,11 @@ Crafty.c("Text", { "cm": 96/2.54, "mm": 96/25.4, "in": 96, - "em": undefined, + "em": undefined, "ex": undefined }; return function (font){ - var number = parseFloat(font); + var number = parseFloat(font); var match = re.exec(font); var unit = match ? match[1] : "px"; if (multipliers[unit] !== undefined) @@ -11644,7 +12004,7 @@ Crafty.c("Text", { if (this.has("Canvas") ) this._resizeForCanvas(); - this.trigger("Change"); + this.trigger("Invalidate"); return this; }, @@ -11662,17 +12022,17 @@ Crafty.c("Text", { // Returns the font string to use _fontString: function(){ - return this._textFont.type + ' ' + this._textFont.weight + ' ' + this._textFont.size + ' ' + this._textFont.family; + return this._textFont.type + ' ' + this._textFont.variant + ' ' + this._textFont.weight + ' ' + this._textFont.size + ' / ' + this._textFont.lineHeight + ' ' + this._textFont.family; }, - /**@ * #.textColor * @comp Text - * @sign public this .textColor(String color, Number strength) - * @param color - The color in hexadecimal - * @param strength - Level of opacity + * @sign public this .textColor(String color) + * @param color - The color in name, hex, rgb or rgba * - * Modify the text color and level of opacity. + * Change the color of the text. You can use HEX, rgb and rgba colors. + * + * If you want the text to be transparent, you should use rgba where you can define alphaChannel. * * @example * ~~~ @@ -11680,21 +12040,24 @@ Crafty.c("Text", { * .textColor('#FF0000'); * * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 }).text('Look at me!!') - * .textColor('#FF0000', 0.6); + * .textColor('rgba(0, 255, 0, 0.5)'); + * + * Crafty.e("2D, Canvas, Text").attr({ x: 100, y: 100 }).text('Look at me!!') + * .textColor('white'); * ~~~ - * @see Crafty.toRGB + * @see Crafty.assignColor */ - textColor: function (color, strength) { - this._strength = strength; - this._textColor = Crafty.toRGB(color, this._strength); - this.trigger("Change"); + textColor: function (color) { + Crafty.assignColor(color, this); + this._textColor = "rgba(" + this._red + ", " + this._green + ", " + this._blue + ", " + this._strength + ")"; + this.trigger("Invalidate"); return this; }, /**@ * #.textFont * @comp Text - * @triggers Change + * @triggers Invalidate * @sign public this .textFont(String key, * value) * @param key - Property of the entity to modify * @param value - Value to set the property to @@ -11702,7 +12065,9 @@ Crafty.c("Text", { * @sign public this .textFont(Object map) * @param map - Object where the key is the property to modify and the value as the property value * - * Use this method to set font property of the text entity. + * Use this method to set font property of the text entity. Possible values are: type, weight, size, family, lineHeight, and variant. + * + * When rendered by the canvas, lineHeight and variant will be ignored. * * @example * ~~~ @@ -11725,7 +12090,7 @@ Crafty.c("Text", { if(propertyKey == 'family'){ this._textFont[propertyKey] = "'" + key[propertyKey] + "'"; } else { - this._textFont[propertyKey] = key[propertyKey]; + this._textFont[propertyKey] = key[propertyKey]; } } } @@ -11736,18 +12101,20 @@ Crafty.c("Text", { if (this.has("Canvas") ) this._resizeForCanvas(); - this.trigger("Change"); + this.trigger("Invalidate"); return this; }, /**@ * #.unselectable * @comp Text - * @triggers Change + * @triggers Invalidate * @sign public this .unselectable() * * This method sets the text so that it cannot be selected (highlighted) by dragging. * (Canvas text can never be highlighted, so this only matters for DOM text.) * Works by changing the css property "user-select" and its variants. + * + * Likewise, this sets the mouseover cursor to be "default" (arrow), not "text" (I-beam) * * @example * ~~~ @@ -11763,15 +12130,16 @@ Crafty.c("Text", { '-khtml-user-select': 'none', '-moz-user-select': 'none', '-ms-user-select': 'none', - 'user-select': 'none' + 'user-select': 'none', + 'cursor': 'default' }); - this.trigger("Change"); + this.trigger("Invalidate"); } return this; } }); -},{"./core.js":9}],24:[function(require,module,exports){ +},{"./core.js":10}],29:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -11782,49 +12150,44 @@ var Crafty = require('./core.js'), Crafty.c("Delay", { init: function () { this._delays = []; - this.bind("EnterFrame", function () { - var now = new Date().getTime(); + this.bind("EnterFrame", function (frameData) { var index = this._delays.length; while (--index >= 0) { var item = this._delays[index]; - if (item.start + item.delay + item.pause < now) { - item.func.call(this); - if (item.repeat > 0) { - // reschedule item - item.start = now; - item.pause = 0; - item.pauseBuffer = 0; + if (item === false) { + // remove canceled item from array + this._delays.splice(index, 1); + } else { + item.accumulator+=frameData.dt; + // The while loop handles the (pathological) case where dt>delay + while(item.accumulator >= item.delay && item.repeat >= 0){ + item.accumulator -= item.delay; item.repeat--; - } else if (item.repeat <= 0) { - // remove item from array + item.callback.call(this); + } + // remove finished item from array + if (item.repeat<0){ this._delays.splice(index, 1); + if(typeof item.callbackOff === "function") + item.callbackOff.call(this); } } } }); - this.bind("Pause", function () { - var now = new Date().getTime(); - for (var index in this._delays) { - this._delays[index].pauseBuffer = now; - } - }); - this.bind("Unpause", function () { - var now = new Date().getTime(); - for (var index in this._delays) { - var item = this._delays[index]; - item.pause += now - item.pauseBuffer; - } - }); + }, /**@ * #.delay * @comp Delay - * @sign public this.delay(Function callback, Number delay) - * @param callback - Method to execute after given amount of milliseconds - * @param delay - Amount of milliseconds to execute the method - * @param repeat - How often to repeat the delayed function. A value of 0 triggers the delayed + * @sign public this.delay(Function callback, Number delay[, Number repeat[, Function callbackOff]]) + * @param callback - Method to execute after given amount of milliseconds. If reference of a + * method is passed, there's possibility to cancel the delay. + * @param delay - Amount of milliseconds to execute the method. + * @param repeat - (optional) How often to repeat the delayed function. A value of 0 triggers the delayed * function exactly once. A value n > 0 triggers the delayed function exactly n+1 times. A - * value of -1 triggers the delayed function indefinitely. + * value of -1 triggers the delayed function indefinitely. Defaults to one execution. + * @param callbackOff - (optional) Method to execute after delay ends(after all iterations are executed). + * If repeat value equals -1, callbackOff will never be triggered. * * The delay method will execute a function after a given amount of time in milliseconds. * @@ -11835,28 +12198,72 @@ Crafty.c("Delay", { * If the entity is destroyed, the delay is also destroyed and will not have effect. * * @example + * + * The simplest delay * ~~~ * console.log("start"); * Crafty.e("Delay").delay(function() { * console.log("100ms later"); * }, 100, 0); * ~~~ + * + * Delay with callbackOff to be executed after all delay iterations + * ~~~ + * console.log("start"); + * Crafty.e("Delay").delay(function() { + * console.log("100ms later"); + * }, 100, 3, function() { + * console.log("delay finished"); + * }); + * ~~~ + * */ - delay: function (func, delay, repeat) { + delay: function (callback, delay, repeat, callbackOff) { this._delays.push({ - start: new Date().getTime(), - func: func, + accumulator: 0, + callback: callback, + callbackOff: callbackOff, delay: delay, repeat: (repeat < 0 ? Infinity : repeat) || 0, - pauseBuffer: 0, - pause: 0 }); return this; + }, + /**@ + * #.cancelDelay + * @comp Delay + * @sign public this.cancelDelay(Function callback) + * @param callback - Method reference passed to .delay + * + * The cancelDelay method will cancel a delay set previously. + * + * @example + * ~~~ + * var doSomething = function(){ + * console.log("doing something"); + * }; + * + * // execute doSomething each 100 miliseconds indefinetely + * var ent = Crafty.e("Delay").delay(doSomething, 100, -1); + * + * // and some time later, cancel further execution of doSomething + * ent.cancelDelay(doSomething); + * ~~~ + */ + cancelDelay: function (callback) { + var index = this._delays.length; + while (--index >= 0) { + var item = this._delays[index]; + if(item && item.callback == callback){ + this._delays[index] = false; + } + } + return this; } }); -},{"./core.js":9}],25:[function(require,module,exports){ -module.exports = "0.6.0"; -},{}],26:[function(require,module,exports){ + +},{"./core.js":10}],30:[function(require,module,exports){ +module.exports = "0.6.3-beta"; +},{}],31:[function(require,module,exports){ var Crafty = require('./core.js'), document = window.document; @@ -11866,10 +12273,19 @@ Crafty.extend({ * @category Stage * @trigger ViewportScroll - when the viewport's x or y coordinates change * @trigger ViewportScale - when the viewport's scale changes + * @trigger ViewportResize - when the viewport's dimension's change * @trigger InvalidateViewport - when the viewport changes + * @trigger StopCamera - when any camera animations should stop, such as at the start of a new animation. + * @trigger CameraAnimationDone - when a camera animation comes reaches completion * - * Viewport is essentially a 2D camera looking at the stage. Can be moved which + * Viewport is essentially a 2D camera looking at the stage. Can be moved or zoomed, which * in turn will react just like a camera moving in that direction. + * + * Tip: At any given moment, the stuff that you can see is... + * + * `x` between `(-Crafty.viewport._x)` and `(-Crafty.viewport._x + (Crafty.viewport._width / Crafty.viewport._scale))` + * + * `y` between `(-Crafty.viewport._y)` and `(-Crafty.viewport._y + (Crafty.viewport._height / Crafty.viewport._scale))` */ viewport: { /**@ @@ -11882,8 +12298,8 @@ Crafty.extend({ * For development it can be useful to set this to false. */ clampToEntities: true, - width: 0, - height: 0, + _width: 0, + _height: 0, /**@ * #Crafty.viewport.x * @comp Crafty.viewport @@ -11913,7 +12329,12 @@ Crafty.extend({ * #Crafty.viewport._scale * @comp Crafty.viewport * - * What scale to render the viewport at. This does not alter the size of the stage itself, but the magnification of what it shows. + * This value is the current scale (zoom) of the viewport. When the value is bigger than 1, everything + * looks bigger (zoomed in). When the value is less than 1, everything looks smaller (zoomed out). This + * does not alter the size of the stage itself, just the magnification of what it shows. + * + * This is a read-only property: Do not set it directly. Instead, use `Crafty.viewport.scale(...)` + * or `Crafty.viewport.zoom(...)` */ _scale: 1, @@ -11923,11 +12344,15 @@ Crafty.extend({ * @comp Crafty.viewport * * A rectangle which defines the bounds of the viewport. - * It should be an object with two properties, `max` and `min`, + * It should be an object with two properties, `max` and `min`, * which are each an object with `x` and `y` properties. * * If this property is null, Crafty uses the bounding box of all the items - * on the stage. This is the initial value. + * on the stage. This is the initial value. (To prevent this behavior, set `Crafty.viewport.clampToEntities` to `false`) + * + * If you wish to bound the viewport along one axis but not the other, you can use `-Infinity` and `+Infinity` as bounds. + * + * @see Crafty.viewport.clampToEntities * * @example * Set the bounds to a 500 by 500 square: @@ -11941,9 +12366,9 @@ Crafty.extend({ /**@ * #Crafty.viewport.scroll * @comp Crafty.viewport - * @sign Crafty.viewport.scroll(String axis, Number v) + * @sign Crafty.viewport.scroll(String axis, Number val) * @param axis - 'x' or 'y' - * @param v - The new absolute position on the axis + * @param val - The new absolute position on the axis * * Will move the viewport to the position given on the specified axis * @@ -11955,69 +12380,75 @@ Crafty.extend({ * Crafty.viewport.scroll('_x', 500); * ~~~ */ - scroll: function (axis, v) { - v = Math.floor(v); - this[axis] = v; + scroll: function (axis, val) { + this[axis] = val; Crafty.trigger("ViewportScroll"); Crafty.trigger("InvalidateViewport"); }, + rect_object: { _x: 0, _y: 0, _w: 0, _h: 0}, + rect: function () { - return { - _x: -this._x / this._scale, - _y: -this._y / this._scale, - _w: this.width / this._scale, - _h: this.height / this._scale - }; + this.rect_object._x = -this._x; + this.rect_object._y = -this._y; + this.rect_object._w = this._width / this._scale; + this.rect_object._h = this._height / this._scale; + return this.rect_object; }, - /**@ + /**@ + * #Crafty.viewport.pan * @comp Crafty.viewport - * @sign public void Crafty.viewport.pan(String axis, Number v, Number time) - * @param String axis - 'x' or 'y'. The axis to move the camera on - * @param Number v - the distance to move the camera by - * @param Number time - The duration in frames for the entire camera movement + * @sign public void Crafty.viewport.pan(Number dx, Number dy, Number time) + * @param Number dx - The distance along the x axis + * @param Number dy - The distance along the y axis + * @param Number time - The duration in ms for the entire camera movement * - * Pans the camera a given number of pixels over a given number of frames + * Pans the camera a given number of pixels over the specified time */ pan: (function () { var tweens = {}, i, bound = false; + var targetX, targetY, startingX, startingY, easing; function enterFrame(e) { - var l = 0; - for (var i in tweens) { - var prop = tweens[i]; - if (prop.remTime > 0) { - prop.current += prop.diff; - prop.remTime--; - Crafty.viewport[i] = Math.floor(prop.current); - l++; - } else { - delete tweens[i]; - } + easing.tick(e.dt); + var v = easing.value(); + Crafty.viewport.x = (1-v) * startingX + v * targetX; + Crafty.viewport.y = (1-v) * startingY + v * targetY; + Crafty.viewport._clamp(); + + if (easing.complete){ + stopPan(); + Crafty.trigger("CameraAnimationDone"); } - if (l) Crafty.viewport._clamp(); } - return function (axis, v, time) { - Crafty.viewport.follow(); - if (axis == 'reset') { - for (var i in tweens) { - tweens[i].remTime = 0; - } - return; - } - if (time === 0) time = 1; - tweens[axis] = { - diff: -v / time, - current: Crafty.viewport[axis], - remTime: time - }; - if (!bound) { - Crafty.bind("EnterFrame", enterFrame); - bound = true; + function stopPan(){ + Crafty.unbind("EnterFrame", enterFrame); + } + + Crafty.bind("StopCamera", stopPan); + + return function (dx, dy, time) { + // Cancel any current camera control + Crafty.trigger("StopCamera"); + + // Handle request to reset + if (dx == 'reset') { + return; } + + startingX = Crafty.viewport._x; + startingY = Crafty.viewport._y; + targetX = startingX - dx; + targetY = startingY - dy; + + easing = new Crafty.easing(time); + + // bind to event, using uniqueBind prevents multiple copies from being bound + Crafty.uniqueBind("EnterFrame", enterFrame); + }; })(), @@ -12047,18 +12478,23 @@ Crafty.extend({ Crafty.viewport._clamp(); } - return function (target, offsetx, offsety) { + function stopFollow(){ if (oldTarget) - oldTarget.unbind('Change', change); + oldTarget.unbind('Move', change); + } + + Crafty.bind("StopCamera", stopFollow); + + return function (target, offsetx, offsety) { if (!target || !target.has('2D')) return; - Crafty.viewport.pan('reset'); + Crafty.trigger("StopCamera"); oldTarget = target; offx = (typeof offsetx != 'undefined') ? offsetx : 0; offy = (typeof offsety != 'undefined') ? offsety : 0; - target.bind('Change', change); + target.bind('Move', change); change.call(target); }; })(), @@ -12068,9 +12504,9 @@ Crafty.extend({ * @comp Crafty.viewport * @sign public void Crafty.viewport.centerOn(Object target, Number time) * @param Object target - An entity with the 2D component - * @param Number time - The number of frames to perform the centering over + * @param Number time - The duration in ms of the camera motion * - * Centers the viewport on the given entity + * Centers the viewport on the given entity. */ centerOn: function (targ, time) { var x = targ.x + Crafty.viewport.x, @@ -12082,17 +12518,8 @@ Crafty.extend({ new_x = x + mid_x - cent_x, new_y = y + mid_y - cent_y; - Crafty.viewport.pan('reset'); - Crafty.viewport.pan('x', new_x, time); - Crafty.viewport.pan('y', new_y, time); + Crafty.viewport.pan(new_x, new_y, time); }, - /**@ - * #Crafty.viewport._zoom - * @comp Crafty.viewport - * - * This value keeps an amount of viewport zoom, required for calculating mouse position at entity - */ - _zoom: 1, /**@ * #Crafty.viewport.zoom @@ -12101,104 +12528,113 @@ Crafty.extend({ * @param Number amt - amount to zoom in on the target by (eg. 2, 4, 0.5) * @param Number cent_x - the center to zoom on * @param Number cent_y - the center to zoom on - * @param Number time - the duration in frames of the entire zoom operation + * @param Number time - the duration in ms of the entire zoom operation * * Zooms the camera in on a given point. amt > 1 will bring the camera closer to the subject - * amt < 1 will bring it farther away. amt = 0 will do nothing. + * amt < 1 will bring it farther away. amt = 0 will reset to the default zoom level * Zooming is multiplicative. To reset the zoom amount, pass 0. */ zoom: (function () { - var zoom = 1, - zoom_tick = 0, - dur = 0, - prop = Crafty.support.prefix + "Transform", - bound = false, - act = {}, - prct = {}; - // what's going on: - // 1. Get the original point as a percentage of the stage - // 2. Scale the stage - // 3. Get the new size of the stage - // 4. Get the absolute position of our point using previous percentage - // 4. Offset inner by that much - - function enterFrame() { - if (dur > 0) { - if (isFinite(Crafty.viewport._zoom)) zoom = Crafty.viewport._zoom; - var old = { - width: act.width * zoom, - height: act.height * zoom - }; - zoom += zoom_tick; - Crafty.viewport._zoom = zoom; - var new_s = { - width: act.width * zoom, - height: act.height * zoom - }, - diff = { - width: new_s.width - old.width, - height: new_s.height - old.height - }; - Crafty.stage.inner.style[prop] = 'scale(' + zoom + ',' + zoom + ')'; - if (Crafty.canvas._canvas) { - var czoom = zoom / (zoom - zoom_tick); - Crafty.canvas.context.scale(czoom, czoom); - Crafty.trigger("InvalidateViewport"); - } - Crafty.viewport.x -= diff.width * prct.width; - Crafty.viewport.y -= diff.height * prct.height; - dur--; + + + function stopZoom(){ + Crafty.unbind("EnterFrame", enterFrame); + } + Crafty.bind("StopCamera", stopZoom); + + var startingZoom, finalZoom, finalAmount, startingX, finalX, startingY, finalY, easing; + + function enterFrame(e){ + var amount, v; + + easing.tick(e.dt); + + // The scaling should happen smoothly -- start at 1, end at finalAmount, and at half way scaling should be by finalAmount^(1/2) + // Since value goes smoothly from 0 to 1, this fufills those requirements + amount = Math.pow(finalAmount, easing.value() ); + + // The viewport should move in such a way that no point reverses + // If a and b are the top left/bottom right of the viewport, then the below can be derived from + // (a_0-b_0)/(a-b) = amount, + // and the assumption that both a and b have the same form + // a = a_0 * (1-v) + a_f * v, + // b = b_0 * (1-v) + b_f * v. + // This is just an arbitrary parameterization of the only sensible path for the viewport corners to take. + // And by symmetry they should be parameterized in the same way! So not much choice here. + if (finalAmount === 1) + v = easing.value(); // prevent NaN! If zoom is used this way, it'll just become a pan. + else + v = (1/amount - 1 ) / (1/finalAmount - 1); + + // Set new scale and viewport position + Crafty.viewport.scale( amount * startingZoom ); + Crafty.viewport.scroll("_x", startingX * (1-v) + finalX * v ); + Crafty.viewport.scroll("_y", startingY * (1-v) + finalY * v ); + Crafty.viewport._clamp(); + + if (easing.complete){ + stopZoom(); + Crafty.trigger("CameraAnimationDone"); } + + } - return function (amt, cent_x, cent_y, time) { - var bounds = this.bounds || Crafty.map.boundaries(), - final_zoom = amt ? zoom * amt : 1; + return function (amt, cent_x, cent_y, time){ if (!amt) { // we're resetting to defaults - zoom = 1; - this._zoom = 1; + Crafty.viewport.scale(1); + return; + } + + if (arguments.length <= 2) { + time = cent_x; + cent_x = Crafty.viewport.x - Crafty.viewport.width; + cent_y = Crafty.viewport.y - Crafty.viewport.height; } - act.width = bounds.max.x - bounds.min.x; - act.height = bounds.max.y - bounds.min.y; + Crafty.trigger("StopCamera"); + startingZoom = Crafty.viewport._scale; + finalAmount = amt; + finalZoom = startingZoom * finalAmount; + - prct.width = cent_x / act.width; - prct.height = cent_y / act.height; + startingX = Crafty.viewport.x; + startingY = Crafty.viewport.y; + finalX = - (cent_x - Crafty.viewport.width / (2 * finalZoom) ); + finalY = - (cent_y - Crafty.viewport.height / (2 * finalZoom) ); - if (time === 0) time = 1; - zoom_tick = (final_zoom - zoom) / time; - dur = time; + easing = new Crafty.easing(time); - Crafty.viewport.pan('reset'); - if (!bound) { - Crafty.bind('EnterFrame', enterFrame); - bound = true; - } + Crafty.uniqueBind("EnterFrame", enterFrame); }; + + })(), /**@ * #Crafty.viewport.scale * @comp Crafty.viewport * @sign public void Crafty.viewport.scale(Number amt) - * @param Number amt - amount to zoom/scale in on the element on the viewport by (eg. 2, 4, 0.5) + * @param Number amt - amount to zoom/scale in on the elements + * + * Adjusts the scale (zoom). When `amt` is 1, it is set to the normal scale, + * e.g. an entity with `this.w == 20` would appear exactly 20 pixels wide. + * When `amt` is 10, that same entity would appear 200 pixels wide (i.e., zoomed in + * by a factor of 10), and when `amt` is 0.1, that same entity would be 2 pixels wide + * (i.e., zoomed out by a factor of `(1 / 0.1)`). + * + * If you pass an `amt` of 0, it is treated the same as passing 1, i.e. the scale is reset. * - * Zooms/scale the camera. amt > 1 increase all entities on stage - * amt < 1 will reduce all entities on stage. amt = 0 will reset the zoom/scale. - * Zooming/scaling is multiplicative. To reset the zoom/scale amount, pass 0. + * This method sets the absolute scale, while `Crafty.viewport.zoom` sets the scale relative to the existing value. + * @see Crafty.viewport.zoom * * @example * ~~~ - * Crafty.viewport.scale(2); //to see effect add some entities on stage. + * Crafty.viewport.scale(2); // Zoom in -- all entities will appear twice as large. * ~~~ */ scale: (function () { return function (amt) { - var bounds = this.bounds || Crafty.map.boundaries(), - final_zoom = amt ? amt : 1; - - - this._zoom = final_zoom; - this._scale = final_zoom; + this._scale = amt ? amt : 1; Crafty.trigger("InvalidateViewport"); Crafty.trigger("ViewportScale"); @@ -12213,12 +12649,17 @@ Crafty.extend({ * Toggle mouselook on the current viewport. * Simply call this function and the user will be able to * drag the viewport around. + * + * If the user starts a drag, "StopCamera" will be triggered, which will cancel any existing camera animations. */ mouselook: (function () { var active = false, dragging = false, lastMouse = {}; old = {}; + function stopLook(){ + dragging = false; + } return function (op, arg) { @@ -12249,6 +12690,7 @@ Crafty.extend({ Crafty.viewport._clamp(); break; case 'start': + Crafty.trigger("StopCamera"); lastMouse.x = arg.clientX; lastMouse.y = arg.clientY; dragging = true; @@ -12264,15 +12706,13 @@ Crafty.extend({ // under no circumstances should the viewport see something outside the boundary of the 'world' if (!this.clampToEntities) return; var bound = this.bounds || Crafty.map.boundaries(); - bound.max.x *= this._zoom; - bound.min.x *= this._zoom; - bound.max.y *= this._zoom; - bound.min.y *= this._zoom; + bound.max.x *= this._scale; + bound.min.x *= this._scale; + bound.max.y *= this._scale; + bound.min.y *= this._scale; if (bound.max.x - bound.min.x > Crafty.viewport.width) { - bound.max.x -= Crafty.viewport.width; - - if (Crafty.viewport.x < -bound.max.x) { - Crafty.viewport.x = -bound.max.x; + if (Crafty.viewport.x < -bound.max.x + Crafty.viewport.width) { + Crafty.viewport.x = -bound.max.x + Crafty.viewport.width; } else if (Crafty.viewport.x > -bound.min.x) { Crafty.viewport.x = -bound.min.x; } @@ -12280,10 +12720,8 @@ Crafty.extend({ Crafty.viewport.x = -1 * (bound.min.x + (bound.max.x - bound.min.x) / 2 - Crafty.viewport.width / 2); } if (bound.max.y - bound.min.y > Crafty.viewport.height) { - bound.max.y -= Crafty.viewport.height; - - if (Crafty.viewport.y < -bound.max.y) { - Crafty.viewport.y = -bound.max.y; + if (Crafty.viewport.y < -bound.max.y + Crafty.viewport.height) { + Crafty.viewport.y = -bound.max.y + Crafty.viewport.height; } else if (Crafty.viewport.y > -bound.min.y) { Crafty.viewport.y = -bound.min.y; } @@ -12301,7 +12739,7 @@ Crafty.extend({ * @param Number height - Height of the viewport * @param String or HTMLElement stage_elem - the element to use as the stage (either its id or the actual element). * - * Initialize the viewport. If the arguments 'width' or 'height' are missing, or Crafty.mobile is true, use Crafty.DOM.window.width and Crafty.DOM.window.height (full screen model). + * Initialize the viewport. If the arguments 'width' or 'height' are missing, use Crafty.DOM.window.width and Crafty.DOM.window.height (full screen model). * * The argument 'stage_elem' is used to specify a stage element other than the default, and can be either a string or an HTMLElement. If a string is provided, it will look for an element with that id and, if none exists, create a div. If an HTMLElement is provided, that is used directly. Omitting this argument is the same as passing an id of 'cr-stage'. * @@ -12310,9 +12748,12 @@ Crafty.extend({ init: function (w, h, stage_elem) { Crafty.DOM.window.init(); - //fullscreen if mobile or not specified - this.width = (!w || Crafty.mobile) ? Crafty.DOM.window.width : w; - this.height = (!h || Crafty.mobile) ? Crafty.DOM.window.height : h; + // setters+getters for the viewport + this._defineViewportProperties(); + // If no width or height is defined, the width and height is set to fullscreen + this._width = (!w) ? Crafty.DOM.window.width : w; + this._height = (!h) ? Crafty.DOM.window.height : h; + //check if stage exists if (typeof stage_elem === 'undefined') @@ -12344,13 +12785,12 @@ Crafty.extend({ * `Crafty.stage.inner` is a div inside the `#cr-stage` div that holds all DOM entities. * If you use canvas, a `canvas` element is created at the same level in the dom * as the the `Crafty.stage.inner` div. So the hierarchy in the DOM is - * - * `Crafty.stage.elem` - * - * - * - `Crafty.stage.inner` (a div HTMLElement) - * - * - `Crafty.canvas._canvas` (a canvas HTMLElement) + * + * ~~~ + * Crafty.stage.elem + * - Crafty.stage.inner (a div HTMLElement) + * - Crafty.canvas._canvas (a canvas HTMLElement) + * ~~~ */ //create stage div to contain everything @@ -12363,7 +12803,7 @@ Crafty.extend({ }; //fullscreen, stop scrollbars - if ((!w && !h) || Crafty.mobile) { + if (!w && !h) { document.body.style.overflow = "hidden"; Crafty.stage.fullscreen = true; } @@ -12423,10 +12863,11 @@ Crafty.extend({ elem.height = this.height + "px"; elem.overflow = "hidden"; + + // resize events + Crafty.bind("ViewportResize", function(){Crafty.trigger("InvalidateViewport");}); + if (Crafty.mobile) { - elem.position = "absolute"; - elem.left = "0px"; - elem.top = "0px"; // remove default gray highlighting after touch if (typeof elem.webkitTapHighlightColor !== undefined) { @@ -12434,28 +12875,18 @@ Crafty.extend({ } var meta = document.createElement("meta"), - head = document.getElementsByTagName("HEAD")[0]; - - //stop mobile zooming and scrolling - meta.setAttribute("name", "viewport"); - meta.setAttribute("content", "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"); - head.appendChild(meta); + head = document.getElementsByTagName("head")[0]; //hide the address bar meta = document.createElement("meta"); meta.setAttribute("name", "apple-mobile-web-app-capable"); meta.setAttribute("content", "yes"); head.appendChild(meta); - setTimeout(function () { - window.scrollTo(0, 1); - }, 0); - Crafty.addEvent(this, window, "touchmove", function (e) { + Crafty.addEvent(this, Crafty.stage.elem, "touchmove", function (e) { e.preventDefault(); }); - Crafty.stage.x = 0; - Crafty.stage.y = 0; } else { elem.position = "relative"; @@ -12465,55 +12896,49 @@ Crafty.extend({ Crafty.stage.y = offset.y; } - if (Crafty.support.setter) { - //define getters and setters to scroll the viewport - this.__defineSetter__('x', function (v) { + + }, + + // Create setters/getters for x, y, width, height + _defineViewportProperties: function(){ + Object.defineProperty(this, 'x', { + set: function (v) { this.scroll('_x', v); - }); - this.__defineSetter__('y', function (v) { - this.scroll('_y', v); - }); - this.__defineGetter__('x', function () { + }, + get: function () { return this._x; - }); - this.__defineGetter__('y', function () { + }, + configurable : true + }); + Object.defineProperty(this, 'y', { + set: function (v) { + this.scroll('_y', v); + }, + get: function () { return this._y; - }); - - //IE9 - } else if (Crafty.support.defineProperty) { - Object.defineProperty(this, 'x', { - set: function (v) { - this.scroll('_x', v); - }, - get: function () { - return this._x; - }, - configurable : true - }); - Object.defineProperty(this, 'y', { - set: function (v) { - this.scroll('_y', v); - }, - get: function () { - return this._y; - }, - configurable : true - }); - } else { - // IE8 has no getter/setters -- Check for an update each frame. - this.x = this._x; - this.y = this._y; - Crafty.bind("EnterFrame", function () { - if (Crafty.viewport._x !== Crafty.viewport.x) { - Crafty.viewport.scroll('_x', Crafty.viewport.x); - } - - if (Crafty.viewport._y !== Crafty.viewport.y) { - Crafty.viewport.scroll('_y', Crafty.viewport.y); - } - }); - } + }, + configurable : true + }); + Object.defineProperty(this, 'width', { + set: function (v) { + this._width = v; + Crafty.trigger("ViewportResize"); + }, + get: function () { + return this._width; + }, + configurable : true + }); + Object.defineProperty(this, 'height', { + set: function (v) { + this._height = v; + Crafty.trigger("ViewportResize"); + }, + get: function () { + return this._height; + }, + configurable : true + }); }, /**@ @@ -12524,6 +12949,7 @@ Crafty.extend({ * * Recalculate and reload stage width, height and position. * Useful when browser return wrong results on init (like safari on Ipad2). + * You should also call this method if you insert custom DOM elements that affect Crafty's stage offset. * */ reload: function () { @@ -12534,16 +12960,9 @@ Crafty.extend({ if (Crafty.stage.fullscreen) { - this.width = w; - this.height = h; - Crafty.stage.elem.style.width = w + "px"; - Crafty.stage.elem.style.height = h + "px"; - - if (Crafty.canvas._canvas) { - Crafty.canvas._canvas.width = w; - Crafty.canvas._canvas.height = h; - Crafty.trigger("InvalidateViewport"); - } + this._width = w; + this._height = h; + Crafty.trigger("ViewportResize"); } offset = Crafty.DOM.inner(Crafty.stage.elem); @@ -12554,20 +12973,19 @@ Crafty.extend({ /**@ * #Crafty.viewport.reset * @comp Crafty.stage + * @trigger StopCamera - called to cancel camera animations * * @sign public Crafty.viewport.reset() * - * Resets the viewport to starting values + * Resets the viewport to starting values, and cancels any existing camera animations. * Called when scene() is run. */ reset: function () { - Crafty.viewport.pan('reset'); - Crafty.viewport.follow(); - Crafty.viewport.mouselook('stop'); - Crafty.viewport.scale(); + Crafty.viewport.mouselook("stop"); + Crafty.trigger("StopCamera"); + Crafty.viewport.scale(1); } } }); -},{"./core.js":9}]},{},[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]) -; \ No newline at end of file +},{"./core.js":10}]},{},[11]); \ No newline at end of file diff --git a/package.json b/package.json index 688a0888..d14973e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "crafty", - "version": "0.6.2", + "version": "0.6.3-beta", "title": "Crafty game framework", "author": { "name": "Louis Stowasser", diff --git a/src/version.js b/src/version.js index 9d6b1b94..91e56866 100644 --- a/src/version.js +++ b/src/version.js @@ -1 +1 @@ -module.exports = "0.6.2"; \ No newline at end of file +module.exports = "0.6.3-beta"; \ No newline at end of file