/* The MIT License Copyright (c) 2010-2011-2012-2013-2014-2015 Abdul.R.Alargha [alargha1970@gmail.com] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Version: 0.4 build: 68 Created on: DATE: 2015-04-01 TIME: 01:03:47 */ /** * See LICENSE file. * * Library namespace. * CAAT stands for: Canvas Advanced Animation Toolkit. */ /** * @namespace */ var CAAT= CAAT || {}; /** * Common bind function. Allows to set an object's function as callback. Set for every function in the * javascript context. */ Function.prototype.bind= Function.prototype.bind || function() { var fn= this; // the function var args= Array.prototype.slice.call(arguments); // copy the arguments. var obj= args.shift(); // first parameter will be context 'this' return function() { return fn.apply( obj, args.concat(Array.prototype.slice.call(arguments))); } }; isArray= function(input) { return typeof(input)=='object'&&(input instanceof Array); }; isString= function(input){ return typeof(input)=='string'; }; /** * See LICENSE file. * * Extend a prototype with another to form a classical OOP inheritance procedure. * * @param subc {object} Prototype to define the base class * @param superc {object} Prototype to be extended (derived class). */ function extend(subc, superc) { var subcp = subc.prototype; // Class pattern. var F = function() { }; F.prototype = superc.prototype; subc.prototype = new F(); // chain prototypes. subc.superclass = superc.prototype; subc.prototype.constructor = subc; // Reset constructor. See Object Oriented Javascript for an in-depth explanation of this. if (superc.prototype.constructor === Object.prototype.constructor) { superc.prototype.constructor = superc; } // los metodos de superc, que no esten en esta clase, crear un metodo que // llama al metodo de superc. for (var method in subcp) { if (subcp.hasOwnProperty(method)) { subc.prototype[method] = subcp[method]; /** * Sintactic sugar to add a __super attribute on every overriden method. * Despite comvenient, it slows things down by 5fps. * * Uncomment at your own risk. * // tenemos en super un metodo con igual nombre. if ( superc.prototype[method]) { subc.prototype[method]= (function(fn, fnsuper) { return function() { var prevMethod= this.__super; this.__super= fnsuper; var retValue= fn.apply( this, Array.prototype.slice.call(arguments) ); this.__super= prevMethod; return retValue; }; })(subc.prototype[method], superc.prototype[method]); } */ } } } /** * Dynamic Proxy for an object or wrap/decorate a function. * * @param object * @param preMethod * @param postMethod * @param errorMethod */ function proxy(object, preMethod, postMethod, errorMethod) { // proxy a function if ( typeof object==='function' ) { if ( object.__isProxy ) { return object; } return (function(fn) { var proxyfn= function() { if ( preMethod ) { preMethod({ fn: fn, arguments: Array.prototype.slice.call(arguments)} ); } var retValue= null; try { // apply original function call with itself as context retValue= fn.apply(fn, Array.prototype.slice.call(arguments)); // everything went right on function call, then call // post-method hook if present if ( postMethod ) { retValue= postMethod({ fn: fn, arguments: Array.prototype.slice.call(arguments)} ); } } catch(e) { // an exeception was thrown, call exception-method hook if // present and return its result as execution result. if( errorMethod ) { retValue= errorMethod({ fn: fn, arguments: Array.prototype.slice.call(arguments), exception: e} ); } else { // since there's no error hook, just throw the exception throw e; } } // return original returned value to the caller. return retValue; }; proxyfn.__isProxy= true; for( var method in fn ) { if ( typeof fn[method]!=="function" ) { if (method!=="__object" && method!=="__isProxy") { (function(proxyfn, fn) { proxyfn.__defineGetter__( method, function() { return fn[method]; }); proxyfn.__defineSetter__( method, function(vale) { fn[method]= vale; }); })(proxyfn, fn); } } } return proxyfn; })(object); } /** * If not a function then only non privitive objects can be proxied. * If it is a previously created proxy, return the proxy itself. */ if ( !typeof object==='object' || isArray(object) || isString(object) || object.__isProxy ) { return object; } // Our proxy object class. var cproxy= function() {}; // A new proxy instance. var proxy= new cproxy(); // hold the proxied object as member. Needed to assign proper // context on proxy method call. proxy.__object= object; proxy.__isProxy= true; // For every element in the object to be proxied for( var method in object ) { // only function members if ( typeof object[method]==='function' ) { // add to the proxy object a method of equal signature to the // method present at the object to be proxied. // cache references of object, function and function name. proxy[method]= (function(proxy,fn,method) { return function() { // call pre-method hook if present. if ( preMethod ) { preMethod({ object: proxy.__object, method: method, arguments: Array.prototype.slice.call(arguments)} ); } var retValue= null; try { // apply original object call with proxied object as // function context. retValue= fn.apply( proxy.__object, arguments ); // everything went right on function call, the call // post-method hook if present if ( postMethod ) { postMethod({ object: proxy.__object, method: method, arguments: Array.prototype.slice.call(arguments)} ); } } catch(e) { // an exeception was thrown, call exception-method hook if // present and return its result as execution result. if( errorMethod ) { retValue= errorMethod({ object: proxy.__object, method: method, arguments: Array.prototype.slice.call(arguments), exception: e} ); } else { // since there's no error hook, just throw the exception throw e; } } // return original returned value to the caller. return retValue; }; })(proxy,object[method],method); } else { if (method!=="__object" && method!=="__isProxy") { (function(proxy, method) { proxy.__defineGetter__( method, function() { return proxy.__object[method]; }); proxy.__defineSetter__( method, function(vale) { proxy.__object[method]= vale; }); })(proxy, method); } } } // return our newly created and populated of functions proxy object. return proxy; } /** proxy sample usage var c0= new Meetup.C1(5); var cp1= proxy( c1, function() { console.log('pre method on object: ', arguments[0].object.toString(), arguments[0].method, arguments[0].arguments ); }, function() { console.log('post method on object: ', arguments[0].object.toString(), arguments[0].method, arguments[0].arguments ); }, function() { console.log('exception on object: ', arguments[0].object.toString(), arguments[0].method, arguments[0].arguments, arguments[0].exception); return -1; }); **/ function proxify( ns, preMethod, postMethod, errorMethod, getter, setter ) { var nns= "__"+ns+"__"; var obj= window; var path= ns.split("."); while( path.length>1) { obj= obj[ path.shift() ]; } window[nns] = obj[path]; (function(obj,path, nns,ns) { var newC= function() { console.log("Creating object of type proxy["+ns+"]"); var obj= new window[nns]( Array.prototype.slice.call(arguments) ); obj.____name= ns; return proxyObject( obj, preMethod, postMethod, errorMethod, getter, setter ); }; // set new constructor function prototype as previous one. newC.prototype= window[nns].prototype; for( var method in obj[path] ) { if ( typeof obj[path][method]!=="function" ) { if (method!=="__object" && method!=="__isProxy") { (function(prevConstructor, method, newC) { newC.__defineGetter__( method, function() { return prevConstructor[method]; }); newC.__defineSetter__( method, function(vale) { prevConstructor[method]= vale; }); })(obj[path],method,newC); } } } obj[path]= newC; })(obj,path,nns,ns); } function proxyObject(object, preMethod, postMethod, errorMethod, getter, setter) { /** * If not a function then only non privitive objects can be proxied. * If it is a previously created proxy, return the proxy itself. */ if ( !typeof object==='object' || isArray(object) || isString(object) || object.__isProxy ) { return object; } // hold the proxied object as member. Needed to assign proper // context on proxy method call. object.$proxy__isProxy= true; // For every element in the object to be proxied for( var method in object ) { if ( method==="constructor" ) { continue; } // only function members if ( typeof object[method]==='function' ) { var fn= object[method]; object["$proxy__"+method]= fn; object[method]= (function(object,fn,fnname) { return function() { var args= Array.prototype.slice.call(arguments); // call pre-method hook if present. if ( preMethod ) { preMethod({ object: object, objectName: object.____name, method: fnname, arguments: args } ); } var retValue= null; try { // apply original object call with proxied object as // function context. retValue= fn.apply( object, args ); // everything went right on function call, the call // post-method hook if present if ( postMethod ) { var rr= postMethod({ object: object, objectName: object.____name, method: fnname, arguments: args } ); if ( typeof rr!=="undefined" ) { //retValue= rr; } } } catch(e) { // an exeception was thrown, call exception-method hook if // present and return its result as execution result. if( errorMethod ) { retValue= errorMethod({ object: object, objectName: object.____name, method: fnname, arguments: args, exception: e} ); } else { // since there's no error hook, just throw the exception throw e; } } // return original returned value to the caller. return retValue; }; })(object,fn,method); } else { if ( method!=="____name" ) { (function(object, attribute, getter, setter) { object["$proxy__"+attribute]= object[attribute]; object.__defineGetter__( attribute, function() { if ( getter) { getter( object.____name, attribute ); } return object["$proxy__"+attribute]; }); object.__defineSetter__( attribute, function (value) { object["$proxy__"+attribute] = value; if ( setter ) { setter( object.____name, attribute, value ); } }); })( object, method, getter, setter ); } } } // return our newly created and populated with functions proxied object. return object; }/** * See LICENSE file. * * Manages every Actor affine transformations. * Take into account that Canvas' renderingContext computes postive rotation clockwise, so hacks * to handle it properly are hardcoded. * * Contained classes are CAAT.Matrix and CAAT.MatrixStack. * **/ (function() { /** * * Define a matrix to hold three dimensional affine transforms. * * @constructor */ CAAT.Matrix3= function() { this.matrix= [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]; this.fmatrix= [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; return this; }; CAAT.Matrix3.prototype= { matrix: null, fmatrix:null, transformCoord : function(point) { var x= point.x; var y= point.y; var z= point.z; point.x= x*this.matrix[0][0] + y*this.matrix[0][1] + z*this.matrix[0][2] + this.matrix[0][3]; point.y= x*this.matrix[1][0] + y*this.matrix[1][1] + z*this.matrix[1][2] + this.matrix[1][3]; point.z= x*this.matrix[2][0] + y*this.matrix[2][1] + z*this.matrix[2][2] + this.matrix[2][3]; return point; }, initialize : function( x0,y0,z0, x1,y1,z1, x2,y2,z2 ) { this.identity( ); this.matrix[0][0]= x0; this.matrix[0][1]= y0; this.matrix[0][2]= z0; this.matrix[1][0]= x1; this.matrix[1][1]= y1; this.matrix[1][2]= z1; this.matrix[2][0]= x2; this.matrix[2][1]= y2; this.matrix[2][2]= z2; return this; }, initWithMatrix : function(matrixData) { this.matrix= matrixData; return this; }, flatten : function() { var d= this.fmatrix; var s= this.matrix; d[ 0]= s[0][0]; d[ 1]= s[1][0]; d[ 2]= s[2][0]; d[ 3]= s[3][0]; d[ 4]= s[0][1]; d[ 5]= s[1][1]; d[ 6]= s[2][1]; d[ 7]= s[2][1]; d[ 8]= s[0][2]; d[ 9]= s[1][2]; d[10]= s[2][2]; d[11]= s[3][2]; d[12]= s[0][3]; d[13]= s[1][3]; d[14]= s[2][3]; d[15]= s[3][3]; return this.fmatrix; }, /** * Set this matrix to identity matrix. * @return this */ identity : function() { for( var i=0; i<4; i++ ) { for( var j=0; j<4; j++ ) { this.matrix[i][j]= (i===j) ? 1.0 : 0.0; } } return this; }, /** * Get this matri'x internal representation data. The bakced structure is a 4x4 array of number. */ getMatrix : function() { return this.matrix; }, /** * Multiply this matrix by a created rotation matrix. The rotation matrix is set up to rotate around * xy axis. * * @param xy {Number} radians to rotate. * * @return this */ rotateXY : function( xy ) { return this.rotate( xy, 0, 0 ); }, /** * Multiply this matrix by a created rotation matrix. The rotation matrix is set up to rotate around * xz axis. * * @param xz {Number} radians to rotate. * * @return this */ rotateXZ : function( xz ) { return this.rotate( 0, xz, 0 ); }, /** * Multiply this matrix by a created rotation matrix. The rotation matrix is set up to rotate aroind * yz axis. * * @param yz {Number} radians to rotate. * * @return this */ rotateYZ : function( yz ) { return this.rotate( 0, 0, yz ); }, /** * * @param xy * @param xz * @param yz */ setRotate : function( xy, xz, yz ) { var m= this.rotate(xy,xz,yz); this.copy(m); return this; }, /** * Creates a matrix to represent arbitrary rotations around the given planes. * @param xy {number} radians to rotate around xy plane. * @param xz {number} radians to rotate around xz plane. * @param yz {number} radians to rotate around yz plane. * * @return {CAAT.Matrix3} a newly allocated matrix. * @static */ rotate : function( xy, xz, yz ) { var res=new CAAT.Matrix3(); var s,c,m; if (xy!==0) { m =new CAAT.Matrix3( ); s=Math.sin(xy); c=Math.cos(xy); m.matrix[1][1]=c; m.matrix[1][2]=-s; m.matrix[2][1]=s; m.matrix[2][2]=c; res.multiply(m); } if (xz!==0) { m =new CAAT.Matrix3( ); s=Math.sin(xz); c=Math.cos(xz); m.matrix[0][0]=c; m.matrix[0][2]=-s; m.matrix[2][0]=s; m.matrix[2][2]=c; res.multiply(m); } if (yz!==0) { m =new CAAT.Matrix3( ); s=Math.sin(yz); c=Math.cos(yz); m.matrix[0][0]=c; m.matrix[0][1]=-s; m.matrix[1][0]=s; m.matrix[1][1]=c; res.multiply(m); } return res; }, /** * Creates a new matrix being a copy of this matrix. * @return {CAAT.Matrix3} a newly allocated matrix object. */ getClone : function() { var m= new CAAT.Matrix3( ); m.copy(this); return m; }, /** * Multiplies this matrix by another matrix. * * @param n {CAAT.Matrix3} a CAAT.Matrix3 object. * @return this */ multiply : function( m ) { var n= this.getClone( ); var nm= n.matrix; var n00= nm[0][0]; var n01= nm[0][1]; var n02= nm[0][2]; var n03= nm[0][3]; var n10= nm[1][0]; var n11= nm[1][1]; var n12= nm[1][2]; var n13= nm[1][3]; var n20= nm[2][0]; var n21= nm[2][1]; var n22= nm[2][2]; var n23= nm[2][3]; var n30= nm[3][0]; var n31= nm[3][1]; var n32= nm[3][2]; var n33= nm[3][3]; var mm= m.matrix; var m00= mm[0][0]; var m01= mm[0][1]; var m02= mm[0][2]; var m03= mm[0][3]; var m10= mm[1][0]; var m11= mm[1][1]; var m12= mm[1][2]; var m13= mm[1][3]; var m20= mm[2][0]; var m21= mm[2][1]; var m22= mm[2][2]; var m23= mm[2][3]; var m30= mm[3][0]; var m31= mm[3][1]; var m32= mm[3][2]; var m33= mm[3][3]; this.matrix[0][0] = n00*m00 + n01*m10 + n02*m20 + n03*m30; this.matrix[0][1] = n00*m01 + n01*m11 + n02*m21 + n03*m31; this.matrix[0][2] = n00*m02 + n01*m12 + n02*m22 + n03*m32; this.matrix[0][3] = n00*m03 + n01*m13 + n02*m23 + n03*m33; this.matrix[1][0] = n10*m00 + n11*m10 + n12*m20 + n13*m30; this.matrix[1][1] = n10*m01 + n11*m11 + n12*m21 + n13*m31; this.matrix[1][2] = n10*m02 + n11*m12 + n12*m22 + n13*m32; this.matrix[1][3] = n10*m03 + n11*m13 + n12*m23 + n13*m33; this.matrix[2][0] = n20*m00 + n21*m10 + n22*m20 + n23*m30; this.matrix[2][1] = n20*m01 + n21*m11 + n22*m21 + n23*m31; this.matrix[2][2] = n20*m02 + n21*m12 + n22*m22 + n23*m32; this.matrix[2][3] = n20*m03 + n21*m13 + n22*m23 + n23*m33; return this; }, /** * Pre multiplies this matrix by a given matrix. * * @param m {CAAT.Matrix3} a CAAT.Matrix3 object. * * @return this */ premultiply : function(m) { var n= this.getClone( ); var nm= n.matrix; var n00= nm[0][0]; var n01= nm[0][1]; var n02= nm[0][2]; var n03= nm[0][3]; var n10= nm[1][0]; var n11= nm[1][1]; var n12= nm[1][2]; var n13= nm[1][3]; var n20= nm[2][0]; var n21= nm[2][1]; var n22= nm[2][2]; var n23= nm[2][3]; var n30= nm[3][0]; var n31= nm[3][1]; var n32= nm[3][2]; var n33= nm[3][3]; var mm= m.matrix; var m00= mm[0][0]; var m01= mm[0][1]; var m02= mm[0][2]; var m03= mm[0][3]; var m10= mm[1][0]; var m11= mm[1][1]; var m12= mm[1][2]; var m13= mm[1][3]; var m20= mm[2][0]; var m21= mm[2][1]; var m22= mm[2][2]; var m23= mm[2][3]; var m30= mm[3][0]; var m31= mm[3][1]; var m32= mm[3][2]; var m33= mm[3][3]; this.matrix[0][0] = n00*m00 + n01*m10 + n02*m20; this.matrix[0][1] = n00*m01 + n01*m11 + n02*m21; this.matrix[0][2] = n00*m02 + n01*m12 + n02*m22; this.matrix[0][3] = n00*m03 + n01*m13 + n02*m23 + n03; this.matrix[1][0] = n10*m00 + n11*m10 + n12*m20; this.matrix[1][1] = n10*m01 + n11*m11 + n12*m21; this.matrix[1][2] = n10*m02 + n11*m12 + n12*m22; this.matrix[1][3] = n10*m03 + n11*m13 + n12*m23 + n13; this.matrix[2][0] = n20*m00 + n21*m10 + n22*m20; this.matrix[2][1] = n20*m01 + n21*m11 + n22*m21; this.matrix[2][2] = n20*m02 + n21*m12 + n22*m22; this.matrix[2][3] = n20*m03 + n21*m13 + n22*m23 + n23; return this; }, /** * Set this matrix translation values to be the given parameters. * * @param x {number} x component of translation point. * @param y {number} y component of translation point. * @param z {number} z component of translation point. * * @return this */ setTranslate : function(x,y,z) { this.identity(); this.matrix[0][3]=x; this.matrix[1][3]=y; this.matrix[2][3]=z; return this; }, /** * Create a translation matrix. * @param x {number} * @param y {number} * @param z {number} * @return {CAAT.Matrix3} a new matrix. */ translate : function( x,y,z ) { var m= new CAAT.Matrix3(); m.setTranslate( x,y,z ); return m; }, setScale : function( sx, sy, sz ) { this.identity(); this.matrix[0][0]= sx; this.matrix[1][1]= sy; this.matrix[2][2]= sz; return this; }, scale : function( sx, sy, sz ) { var m= new CAAT.Matrix3(); m.setScale(sx,sy,sz); return m; }, /** * Set this matrix as the rotation matrix around the given axes. * @param xy {number} radians of rotation around z axis. * @param xz {number} radians of rotation around y axis. * @param yz {number} radians of rotation around x axis. * * @return this */ rotateModelView : function( xy, xz, yz ) { var sxy= Math.sin( xy ); var sxz= Math.sin( xz ); var syz= Math.sin( yz ); var cxy= Math.cos( xy ); var cxz= Math.cos( xz ); var cyz= Math.cos( yz ); this.matrix[0][0]= cxz*cxy; this.matrix[0][1]= -cxz*sxy; this.matrix[0][2]= sxz; this.matrix[0][3]= 0; this.matrix[1][0]= syz*sxz*cxy+sxy*cyz; this.matrix[1][1]= cyz*cxy-syz*sxz*sxy; this.matrix[1][2]= -syz*cxz; this.matrix[1][3]= 0; this.matrix[2][0]= syz*sxy-cyz*sxz*cxy; this.matrix[2][1]= cyz*sxz*sxy+syz*cxy; this.matrix[2][2]= cyz*cxz; this.matrix[2][3]= 0; this.matrix[3][0]= 0; this.matrix[3][1]= 0; this.matrix[3][2]= 0; this.matrix[3][3]= 1; return this; }, /** * Copy a given matrix values into this one's. * @param m {CAAT.Matrix} a matrix * * @return this */ copy : function( m ) { for( var i=0; i<4; i++ ) { for( var j=0; j<4; j++ ) { this.matrix[i][j]= m.matrix[i][j]; } } return this; }, /** * Calculate this matrix's determinant. * @return {number} matrix determinant. */ calculateDeterminant: function () { var mm= this.matrix; var m11= mm[0][0], m12= mm[0][1], m13= mm[0][2], m14= mm[0][3], m21= mm[1][0], m22= mm[1][1], m23= mm[1][2], m24= mm[1][3], m31= mm[2][0], m32= mm[2][1], m33= mm[2][2], m34= mm[2][3], m41= mm[3][0], m42= mm[3][1], m43= mm[3][2], m44= mm[3][3]; return m14 * m22 * m33 * m41 + m12 * m24 * m33 * m41 + m14 * m23 * m31 * m42 + m13 * m24 * m31 * m42 + m13 * m21 * m34 * m42 + m11 * m23 * m34 * m42 + m14 * m21 * m32 * m43 + m11 * m24 * m32 * m43 + m13 * m22 * m31 * m44 + m12 * m23 * m31 * m44 + m12 * m21 * m33 * m44 + m11 * m22 * m33 * m44 + m14 * m23 * m32 * m41 - m13 * m24 * m32 * m41 - m13 * m22 * m34 * m41 - m12 * m23 * m34 * m41 - m14 * m21 * m33 * m42 - m11 * m24 * m33 * m42 - m14 * m22 * m31 * m43 - m12 * m24 * m31 * m43 - m12 * m21 * m34 * m43 - m11 * m22 * m34 * m43 - m13 * m21 * m32 * m44 - m11 * m23 * m32 * m44; }, /** * Return a new matrix which is this matrix's inverse matrix. * @return {CAAT.Matrix3} a new matrix. */ getInverse : function() { var mm= this.matrix; var m11 = mm[0][0], m12 = mm[0][1], m13 = mm[0][2], m14 = mm[0][3], m21 = mm[1][0], m22 = mm[1][1], m23 = mm[1][2], m24 = mm[1][3], m31 = mm[2][0], m32 = mm[2][1], m33 = mm[2][2], m34 = mm[2][3], m41 = mm[3][0], m42 = mm[3][1], m43 = mm[3][2], m44 = mm[3][3]; var m2= new CAAT.Matrix3(); m2.matrix[0][0]= m23*m34*m42 + m24*m32*m43 + m22*m33*m44 - m24*m33*m42 - m22*m34*m43 - m23*m32*m44; m2.matrix[0][1]= m14*m33*m42 + m12*m34*m43 + m13*m32*m44 - m12*m33*m44 - m13*m34*m42 - m14*m32*m43; m2.matrix[0][2]= m13*m24*m42 + m12*m23*m44 + m14*m22*m43 - m12*m24*m43 - m13*m22*m44 - m14*m23*m42; m2.matrix[0][3]= m14*m23*m32 + m12*m24*m33 + m13*m22*m34 - m13*m24*m32 - m14*m22*m33 - m12*m23*m34; m2.matrix[1][0]= m24*m33*m41 + m21*m34*m43 + m23*m31*m44 - m23*m34*m41 - m24*m31*m43 - m21*m33*m44; m2.matrix[1][1]= m13*m34*m41 + m14*m31*m43 + m11*m33*m44 - m14*m33*m41 - m11*m34*m43 - m13*m31*m44; m2.matrix[1][2]= m14*m23*m41 + m11*m24*m43 + m13*m21*m44 - m13*m24*m41 - m14*m21*m43 - m11*m23*m44; m2.matrix[1][3]= m13*m24*m31 + m14*m21*m33 + m11*m23*m34 - m14*m23*m31 - m11*m24*m33 - m13*m21*m34; m2.matrix[2][0]= m22*m34*m41 + m24*m31*m42 + m21*m32*m44 - m24*m32*m41 - m21*m34*m42 - m22*m31*m44; m2.matrix[2][1]= m14*m32*m41 + m11*m34*m42 + m12*m31*m44 - m11*m32*m44 - m12*m34*m41 - m14*m31*m42; m2.matrix[2][2]= m13*m24*m41 + m14*m21*m42 + m11*m22*m44 - m14*m22*m41 - m11*m24*m42 - m12*m21*m44; m2.matrix[2][3]= m14*m22*m31 + m11*m24*m32 + m12*m21*m34 - m11*m22*m34 - m12*m24*m31 - m14*m21*m32; m2.matrix[3][0]= m23*m32*m41 + m21*m33*m42 + m22*m31*m43 - m22*m33*m41 - m23*m31*m42 - m21*m32*m43; m2.matrix[3][1]= m12*m33*m41 + m13*m31*m42 + m11*m32*m43 - m13*m32*m41 - m11*m33*m42 - m12*m31*m43; m2.matrix[3][2]= m13*m22*m41 + m11*m23*m42 + m12*m21*m43 - m11*m22*m43 - m12*m23*m41 - m13*m21*m42; m2.matrix[3][3]= m12*m23*m31 + m13*m21*m32 + m11*m22*m33 - m13*m22*m31 - m11*m23*m32 - m12*m21*m33; return m2.multiplyScalar( 1/this.calculateDeterminant() ); }, /** * Multiply this matrix by a scalar. * @param scalar {number} scalar value * * @return this */ multiplyScalar : function( scalar ) { var i,j; for( i=0; i<4; i++ ) { for( j=0; j<4; j++ ) { this.matrix[i][j]*=scalar; } } return this; } }; })(); (function() { /** * 2D affinetransform matrix represeantation. * It includes matrices for *
* Ported from the excellent java algorithm by Eugene Vishnevsky at:
* http://www.cs.rit.edu/~ncs/color/t_convert.html
*
* @static
*/
hsvToRgb: function(h, s, v)
{
var r, g, b;
var i;
var f, p, q, t;
// Make sure our arguments stay in-range
h = Math.max(0, Math.min(360, h));
s = Math.max(0, Math.min(100, s));
v = Math.max(0, Math.min(100, v));
// We accept saturation and value arguments from 0 to 100 because that's
// how Photoshop represents those values. Internally, however, the
// saturation and value are calculated from a range of 0 to 1. We make
// That conversion here.
s /= 100;
v /= 100;
if(s === 0) {
// Achromatic (grey)
r = g = b = v;
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
h /= 60; // sector 0 to 5
i = Math.floor(h);
f = h - i; // factorial part of h
p = v * (1 - s);
q = v * (1 - s * f);
t = v * (1 - s * (1 - f));
switch(i) {
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default: // case 5:
r = v;
g = p;
b = q;
}
return new CAAT.Color.RGB(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255));
},
/**
* Enumeration to define types of color ramps.
* @enum {number}
*/
RampEnumeration : {
RAMP_RGBA: 0,
RAMP_RGB: 1,
RAMP_CHANNEL_RGB: 2,
RAMP_CHANNEL_RGBA: 3,
RAMP_CHANNEL_RGB_ARRAY: 4,
RAMP_CHANNEL_RGBA_ARRAY:5
},
/**
* Interpolate the color between two given colors. The return value will be a calculated color
* among the two given initial colors which corresponds to the 'step'th color of the 'nsteps'
* calculated colors.
* @param r0 {number} initial color red component.
* @param g0 {number} initial color green component.
* @param b0 {number} initial color blue component.
* @param r1 {number} final color red component.
* @param g1 {number} final color green component.
* @param b1 {number} final color blue component.
* @param nsteps {number} number of colors to calculate including the two given colors. If 16 is passed as value,
* 14 colors plus the two initial ones will be calculated.
* @param step {number} return this color index of all the calculated colors.
*
* @return { r{number}, g{number}, b{number} } return an object with the new calculated color components.
* @static
*/
interpolate : function( r0, g0, b0, r1, g1, b1, nsteps, step) {
if ( step<=0 ) {
return {
r:r0,
g:g0,
b:b0
};
} else if ( step>=nsteps ) {
return {
r:r1,
g:g1,
b:b1
};
}
var r= (r0+ (r1-r0)/nsteps*step)>>0;
var g= (g0+ (g1-g0)/nsteps*step)>>0;
var b= (b0+ (b1-b0)/nsteps*step)>>0;
if ( r>255 ) {r=255;} else if (r<0) {r=0;}
if ( g>255 ) {g=255;} else if (g<0) {g=0;}
if ( b>255 ) {b=255;} else if (b<0) {b=0;}
return {
r:r,
g:g,
b:b
};
},
/**
* Generate a ramp of colors from an array of given colors.
* @param fromColorsArray {[number]} an array of colors. each color is defined by an integer number from which
* color components will be extracted. Be aware of the alpha component since it will also be interpolated for
* new colors.
* @param rampSize {number} number of colors to produce.
* @param returnType {CAAT.ColorUtils.RampEnumeration} a value of CAAT.ColorUtils.RampEnumeration enumeration.
*
* @return { [{number},{number},{number},{number}] } an array of integers each of which represents a color of
* the calculated color ramp.
*
* @static
*/
makeRGBColorRamp : function( fromColorsArray, rampSize, returnType ) {
var ramp= [];
var nc= fromColorsArray.length-1;
var chunk= rampSize/nc;
for( var i=0; i
* This object manages one single catmull rom segment, that is 4 points.
* A complete spline should be managed with CAAT.Path.setCatmullRom with a complete list of points.
*
* @constructor
* @extends CAAT.Curve
*/
CAAT.CatmullRom = function() {
CAAT.CatmullRom.superclass.constructor.call(this);
return this;
};
CAAT.CatmullRom.prototype= {
/**
* Set curve control points.
* @param p0
* CAAt.Interpolator is defined by a createXXXX method which sets up an internal getPosition(time)
* function. You could set as an Interpolator up any object which exposes a method getPosition(time)
* and returns a CAAT.Point or an object of the form {x:{number}, y:{number}}.
*
* In the return value, the x attribute's value will be the same value as that of the time parameter,
* and y attribute will hold a value between 0 and 1 with the resulting value of applying the
* interpolation function for the time parameter.
*
*
* For am exponential interpolation, the getPosition function would look like this:
*
* For a visual understanding of interpolators see tutorial 4 interpolators, or play with technical
* demo 1 where a SpriteActor moves along a path and the way it does can be modified by every
* out-of-the-box interpolator.
*
* @constructor
*
*/
CAAT.Interpolator = function() {
this.interpolated= new CAAT.Point(0,0,0);
return this;
};
CAAT.Interpolator.prototype= {
interpolated: null, // a coordinate holder for not building a new CAAT.Point for each interpolation call.
paintScale: 90, // the size of the interpolation draw on screen in pixels.
/**
* Set a linear interpolation function.
*
* @param bPingPong {boolean}
* @param bInverse {boolean} will values will be from 1 to 0 instead of 0 to 1 ?.
*/
createLinearInterpolator : function(bPingPong, bInverse) {
/**
* Linear and inverse linear interpolation function.
* @param time {number}
*/
this.getPosition= function getPosition(time) {
var orgTime= time;
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
if ( bInverse!==null && bInverse ) {
time= 1-time;
}
return this.interpolated.set(orgTime,time);
};
return this;
},
createBackOutInterpolator : function(bPingPong) {
this.getPosition= function getPosition(time) {
var orgTime= time;
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
time = time - 1;
var overshoot= 1.70158;
return this.interpolated.set(
orgTime,
time * time * ((overshoot + 1) * time + overshoot) + 1);
};
return this;
},
/**
* Set an exponential interpolator function. The function to apply will be Math.pow(time,exponent).
* This function starts with 0 and ends in values of 1.
*
* @param exponent {number} exponent of the function.
* @param bPingPong {boolean}
*/
createExponentialInInterpolator : function(exponent, bPingPong) {
this.getPosition= function getPosition(time) {
var orgTime= time;
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
return this.interpolated.set(orgTime,Math.pow(time,exponent));
};
return this;
},
/**
* Set an exponential interpolator function. The function to apply will be 1-Math.pow(time,exponent).
* This function starts with 1 and ends in values of 0.
*
* @param exponent {number} exponent of the function.
* @param bPingPong {boolean}
*/
createExponentialOutInterpolator : function(exponent, bPingPong) {
this.getPosition= function getPosition(time) {
var orgTime= time;
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
return this.interpolated.set(orgTime,1-Math.pow(1-time,exponent));
};
return this;
},
/**
* Set an exponential interpolator function. Two functions will apply:
* Math.pow(time*2,exponent)/2 for the first half of the function (t<0.5) and
* 1-Math.abs(Math.pow(time*2-2,exponent))/2 for the second half (t>=.5)
* This function starts with 0 and goes to values of 1 and ends with values of 0.
*
* @param exponent {number} exponent of the function.
* @param bPingPong {boolean}
*/
createExponentialInOutInterpolator : function(exponent, bPingPong) {
this.getPosition= function getPosition(time) {
var orgTime= time;
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
if ( time*2<1 ) {
return this.interpolated.set(orgTime,Math.pow(time*2,exponent)/2);
}
return this.interpolated.set(orgTime,1-Math.abs(Math.pow(time*2-2,exponent))/2);
};
return this;
},
/**
* Creates a Quadric bezier curbe as interpolator.
*
* @param p0 {CAAT.Point} a CAAT.Point instance.
* @param p1 {CAAT.Point} a CAAT.Point instance.
* @param p2 {CAAT.Point} a CAAT.Point instance.
* @param bPingPong {boolean} a boolean indicating if the interpolator must ping-pong.
*/
createQuadricBezierInterpolator : function(p0,p1,p2,bPingPong) {
this.getPosition= function getPosition(time) {
var orgTime= time;
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
time= (1-time)*(1-time)*p0.y + 2*(1-time)*time*p1.y + time*time*p2.y;
return this.interpolated.set( orgTime, time );
};
return this;
},
/**
* Creates a Cubic bezier curbe as interpolator.
*
* @param p0 {CAAT.Point} a CAAT.Point instance.
* @param p1 {CAAT.Point} a CAAT.Point instance.
* @param p2 {CAAT.Point} a CAAT.Point instance.
* @param p3 {CAAT.Point} a CAAT.Point instance.
* @param bPingPong {boolean} a boolean indicating if the interpolator must ping-pong.
*/
createCubicBezierInterpolator : function(p0,p1,p2,p3,bPingPong) {
this.getPosition= function getPosition(time) {
var orgTime= time;
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
var t2= time*time;
var t3= time*t2;
time = (p0.y + time * (-p0.y * 3 + time * (3 * p0.y -
p0.y * time))) + time * (3 * p1.y + time * (-6 * p1.y +
p1.y * 3 * time)) + t2 * (p2.y * 3 - p2.y * 3 * time) +
p3.y * t3;
return this.interpolated.set( orgTime, time );
};
return this;
},
createElasticOutInterpolator : function(amplitude,p,bPingPong) {
this.getPosition= function getPosition(time) {
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
if (time === 0) {
return {x:0,y:0};
}
if (time === 1) {
return {x:1,y:1};
}
var s = p/(2*Math.PI) * Math.asin (1/amplitude);
return this.interpolated.set(
time,
(amplitude*Math.pow(2,-10*time) * Math.sin( (time-s)*(2*Math.PI)/p ) + 1 ) );
};
return this;
},
createElasticInInterpolator : function(amplitude,p,bPingPong) {
this.getPosition= function getPosition(time) {
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
if (time === 0) {
return {x:0,y:0};
}
if (time === 1) {
return {x:1,y:1};
}
var s = p/(2*Math.PI) * Math.asin (1/amplitude);
return this.interpolated.set(
time,
-(amplitude*Math.pow(2,10*(time-=1)) * Math.sin( (time-s)*(2*Math.PI)/p ) ) );
};
return this;
},
createElasticInOutInterpolator : function(amplitude,p,bPingPong) {
this.getPosition= function getPosition(time) {
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
var s = p/(2*Math.PI) * Math.asin (1/amplitude);
time*=2;
if ( time<=1 ) {
return this.interpolated.set(
time,
-0.5*(amplitude*Math.pow(2,10*(time-=1)) * Math.sin( (time-s)*(2*Math.PI)/p )));
}
return this.interpolated.set(
time,
1+0.5*(amplitude*Math.pow(2,-10*(time-=1)) * Math.sin( (time-s)*(2*Math.PI)/p )));
};
return this;
},
/**
* @param time {number}
* @private
*/
bounce : function(time) {
if ((time /= 1) < (1 / 2.75)) {
return {x:time, y:7.5625 * time * time};
} else if (time < (2 / 2.75)) {
return {x:time, y:7.5625 * (time -= (1.5 / 2.75)) * time + 0.75};
} else if (time < (2.5 / 2.75)) {
return {x:time, y:7.5625 * (time -= (2.25 / 2.75)) * time + 0.9375};
} else {
return {x:time, y:7.5625*(time-=(2.625/2.75))*time+0.984375};
}
},
createBounceOutInterpolator : function(bPingPong) {
this.getPosition= function getPosition(time) {
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
return this.bounce(time);
};
return this;
},
createBounceInInterpolator : function(bPingPong) {
this.getPosition= function getPosition(time) {
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
var r= this.bounce(1-time);
r.y= 1-r.y;
return r;
};
return this;
},
createBounceInOutInterpolator : function(bPingPong) {
this.getPosition= function getPosition(time) {
if ( bPingPong ) {
if ( time<0.5 ) {
time*=2;
} else {
time= 1-(time-0.5)*2;
}
}
var r;
if (time < 0.5) {
r= this.bounce(1 - time * 2);
r.y= (1 - r.y)* 0.5;
return r;
}
r= this.bounce(time * 2 - 1,bPingPong);
r.y= r.y* 0.5 + 0.5;
return r;
};
return this;
},
/**
* Paints an interpolator on screen.
* @param director {CAAT.Director} a CAAT.Director instance.
* @param time {number} an integer indicating the scene time the Interpolator will be drawn at. This value is useless.
*/
paint : function(director,time) {
var canvas= director.crc;
canvas.save();
canvas.beginPath();
canvas.moveTo( 0, this.getPosition(0).y * this.paintScale );
for( var i=0; i<=this.paintScale; i++ ) {
canvas.lineTo( i, this.getPosition(i/this.paintScale).y * this.paintScale );
}
canvas.strokeStyle='black';
canvas.stroke();
canvas.restore();
},
/**
* Gets an array of coordinates which define the polyline of the intepolator's curve contour.
* Values for both coordinates range from 0 to 1.
* @param iSize {number} an integer indicating the number of contour segments.
* @return array {[CAAT.Point]} of object of the form {x:float, y:float}.
*/
getContour : function(iSize) {
var contour=[];
for( var i=0; i<=iSize; i++ ) {
contour.push( {x: i/iSize, y: this.getPosition(i/iSize).y} );
}
return contour;
},
/**
*
*/
enumerateInterpolators : function() {
return [
new CAAT.Interpolator().createLinearInterpolator(false, false), 'Linear pingpong=false, inverse=false',
new CAAT.Interpolator().createLinearInterpolator(true, false), 'Linear pingpong=true, inverse=false',
new CAAT.Interpolator().createLinearInterpolator(false, true), 'Linear pingpong=false, inverse=true',
new CAAT.Interpolator().createLinearInterpolator(true, true), 'Linear pingpong=true, inverse=true',
new CAAT.Interpolator().createExponentialInInterpolator( 2, false), 'ExponentialIn pingpong=false, exponent=2',
new CAAT.Interpolator().createExponentialOutInterpolator( 2, false), 'ExponentialOut pingpong=false, exponent=2',
new CAAT.Interpolator().createExponentialInOutInterpolator( 2, false), 'ExponentialInOut pingpong=false, exponent=2',
new CAAT.Interpolator().createExponentialInInterpolator( 2, true), 'ExponentialIn pingpong=true, exponent=2',
new CAAT.Interpolator().createExponentialOutInterpolator( 2, true), 'ExponentialOut pingpong=true, exponent=2',
new CAAT.Interpolator().createExponentialInOutInterpolator( 2, true), 'ExponentialInOut pingpong=true, exponent=2',
new CAAT.Interpolator().createExponentialInInterpolator( 4, false), 'ExponentialIn pingpong=false, exponent=4',
new CAAT.Interpolator().createExponentialOutInterpolator( 4, false), 'ExponentialOut pingpong=false, exponent=4',
new CAAT.Interpolator().createExponentialInOutInterpolator( 4, false), 'ExponentialInOut pingpong=false, exponent=4',
new CAAT.Interpolator().createExponentialInInterpolator( 4, true), 'ExponentialIn pingpong=true, exponent=4',
new CAAT.Interpolator().createExponentialOutInterpolator( 4, true), 'ExponentialOut pingpong=true, exponent=4',
new CAAT.Interpolator().createExponentialInOutInterpolator( 4, true), 'ExponentialInOut pingpong=true, exponent=4',
new CAAT.Interpolator().createExponentialInInterpolator( 6, false), 'ExponentialIn pingpong=false, exponent=6',
new CAAT.Interpolator().createExponentialOutInterpolator( 6, false), 'ExponentialOut pingpong=false, exponent=6',
new CAAT.Interpolator().createExponentialInOutInterpolator( 6, false), 'ExponentialInOut pingpong=false, exponent=6',
new CAAT.Interpolator().createExponentialInInterpolator( 6, true), 'ExponentialIn pingpong=true, exponent=6',
new CAAT.Interpolator().createExponentialOutInterpolator( 6, true), 'ExponentialOut pingpong=true, exponent=6',
new CAAT.Interpolator().createExponentialInOutInterpolator( 6, true), 'ExponentialInOut pingpong=true, exponent=6',
new CAAT.Interpolator().createBounceInInterpolator(false), 'BounceIn pingpong=false',
new CAAT.Interpolator().createBounceOutInterpolator(false), 'BounceOut pingpong=false',
new CAAT.Interpolator().createBounceInOutInterpolator(false), 'BounceInOut pingpong=false',
new CAAT.Interpolator().createBounceInInterpolator(true), 'BounceIn pingpong=true',
new CAAT.Interpolator().createBounceOutInterpolator(true), 'BounceOut pingpong=true',
new CAAT.Interpolator().createBounceInOutInterpolator(true), 'BounceInOut pingpong=true',
new CAAT.Interpolator().createElasticInInterpolator( 1.1, 0.4, false), 'ElasticIn pingpong=false, amp=1.1, d=.4',
new CAAT.Interpolator().createElasticOutInterpolator( 1.1, 0.4, false), 'ElasticOut pingpong=false, amp=1.1, d=.4',
new CAAT.Interpolator().createElasticInOutInterpolator( 1.1, 0.4, false), 'ElasticInOut pingpong=false, amp=1.1, d=.4',
new CAAT.Interpolator().createElasticInInterpolator( 1.1, 0.4, true), 'ElasticIn pingpong=true, amp=1.1, d=.4',
new CAAT.Interpolator().createElasticOutInterpolator( 1.1, 0.4, true), 'ElasticOut pingpong=true, amp=1.1, d=.4',
new CAAT.Interpolator().createElasticInOutInterpolator( 1.1, 0.4, true), 'ElasticInOut pingpong=true, amp=1.1, d=.4',
new CAAT.Interpolator().createElasticInInterpolator( 1.0, 0.2, false), 'ElasticIn pingpong=false, amp=1.0, d=.2',
new CAAT.Interpolator().createElasticOutInterpolator( 1.0, 0.2, false), 'ElasticOut pingpong=false, amp=1.0, d=.2',
new CAAT.Interpolator().createElasticInOutInterpolator( 1.0, 0.2, false), 'ElasticInOut pingpong=false, amp=1.0, d=.2',
new CAAT.Interpolator().createElasticInInterpolator( 1.0, 0.2, true), 'ElasticIn pingpong=true, amp=1.0, d=.2',
new CAAT.Interpolator().createElasticOutInterpolator( 1.0, 0.2, true), 'ElasticOut pingpong=true, amp=1.0, d=.2',
new CAAT.Interpolator().createElasticInOutInterpolator( 1.0, 0.2, true), 'ElasticInOut pingpong=true, amp=1.0, d=.2'
];
}
};
})();
/**
* See LICENSE file.
*
* Behaviors are keyframing elements.
* By using a BehaviorContainer, you can specify different actions on any animation Actor.
* An undefined number of Behaviors can be defined for each Actor.
*
* There're the following Behaviors:
* + AlphaBehavior: controls container/actor global alpha.
* + RotateBehavior: takes control of rotation affine transform.
* + ScaleBehavior: takes control of scaling on x/y axis affine transform.
* + PathBehavior: takes control of translating an Actor/ActorContainer across a path [ie. pathSegment collection].
* + GenericBehavior: applies a behavior to any given target object's property, or notifies a callback.
*
*
**/
(function() {
/**
* Behavior base class.
*
*
* A behavior is defined by a frame time (behavior duration) and a behavior application function called interpolator.
* In its default form, a behaviour is applied linearly, that is, the same amount of behavior is applied every same
* time interval.
*
* A concrete Behavior, a rotateBehavior in example, will change a concrete Actor's rotationAngle during the specified
* period.
*
* A behavior is guaranteed to notify (if any observer is registered) on behavior expiration.
*
* A behavior can keep an unlimited observers. Observers are objects of the form:
*
*
* behaviorExpired: function( behavior, time, actor). This method will be called for any registered observer when
* the scene time is greater than behavior's startTime+duration. This method will be called regardless of the time
* granurality.
*
* behaviorApplied : function( behavior, time, normalizedTime, actor, value). This method will be called once per
* frame while the behavior is not expired and is in frame time (behavior startTime>=scene time). This method can be
* called multiple times.
*
* Every behavior is applied to a concrete Actor.
* Every actor must at least define an start and end value. The behavior will set start-value at behaviorStartTime and
* is guaranteed to apply end-value when scene time= behaviorStartTime+behaviorDuration.
*
* You can set behaviors to apply forever that is cyclically. When a behavior is cycle=true, won't notify
* behaviorExpired to its registered observers.
*
* Other Behaviors simply must supply with the method
* It imposes some constraints to contained Behaviors:
*
* A ContainerBehavior can contain other ContainerBehaviors at will.
*
* A ContainerBehavior will not apply any CAAT.Actor property change by itself, but will instrument its contained
* Behaviors to do so.
*
* @constructor
* @extends CAAT.Behavior
*/
CAAT.ContainerBehavior= function() {
CAAT.ContainerBehavior.superclass.constructor.call(this);
this.behaviors= [];
return this;
};
CAAT.ContainerBehavior.prototype= {
behaviors: null, // contained behaviors array
/**
* Proportionally change this container duration to its children.
* @param duration {number} new duration in ms.
* @return this;
*/
conformToDuration : function( duration ) {
this.duration= duration;
var f= duration/this.duration;
var bh;
for( var i=0; i
* A generic behavior is supposed to be extended to create new behaviors when the out-of-the-box
* ones are not sufficient. It applies the behavior result to a given target object in two ways:
*
*
* For example, this code will move a dom element from 0 to 400 px on x during 1 second:
*
* var enterBehavior= new CAAT.GenericBehavior().
* This mainly overrides default behavior of a single entity and exposes methods to manage its children
* collection.
*
* @constructor
* @extends CAAT.Actor
*/
CAAT.ActorContainer= function(hint) {
CAAT.ActorContainer.superclass.constructor.call(this);
this.childrenList= [];
this.pendingChildrenList= [];
if ( typeof hint!=='undefined' ) {
this.addHint= hint;
this.boundingBox= new CAAT.Rectangle();
}
return this;
};
CAAT.ActorContainer.AddHint= {
CONFORM : 1
};
CAAT.ActorContainer.prototype= {
childrenList : null, // the list of children contained.
activeChildren : null,
pendingChildrenList : null,
addHint : 0,
boundingBox : null,
runion : new CAAT.Rectangle(), // Watch out. one for every container.
/**
* Draws this ActorContainer and all of its children screen bounding box.
*
* @param director the CAAT.Director object instance that contains the Scene the Actor is in.
* @param time an integer indicating the Scene time when the bounding box is to be drawn.
*/
drawScreenBoundingBox : function( director, time ) {
if (!this.inFrame) {
return;
}
var cl= this.childrenList;
for( var i=0; i
* The star actor will be of size 2*maxRadius.
*
* @param nPeaks {number} number of star points.
* @param maxRadius {number} maximum star radius
* @param minRadius {number} minimum star radius
*
* @return this
*/
initialize : function(nPeaks, maxRadius, minRadius) {
this.setSize( 2*maxRadius, 2*maxRadius );
this.nPeaks= nPeaks;
this.maxRadius= maxRadius;
this.minRadius= minRadius;
return this;
},
/**
* Paint the star.
*
* @param director {CAAT.Director}
* @param timer {number}
*/
paint : function(director, timer) {
var ctx= director.ctx;
var centerX= this.width/2;
var centerY= this.height/2;
var r1= this.maxRadius;
var r2= this.minRadius;
var ix= centerX + r1*Math.cos(this.initialAngle);
var iy= centerY + r1*Math.sin(this.initialAngle);
ctx.lineWidth= this.lineWidth;
if ( this.lineCap ) {
ctx.lineCap= this.lineCap;
}
if ( this.lineJoin ) {
ctx.lineJoin= this.lineJoin;
}
if ( this.miterLimit ) {
ctx.miterLimit= this.miterLimit;
}
ctx.globalCompositeOperation= this.compositeOp;
ctx.beginPath();
ctx.moveTo(ix,iy);
for( var i=1; i
* If more than 'numChannels' sounds want to be played at the same time the requests will be dropped,
* so no more than 'numChannels' sounds can be concurrently played.
*
* Available sounds to be played must be supplied to every CAAT.Director instance by calling
* The cached elements can be played, or looped. The
* Be aware of Audio.canPlay, is able to return 'yes', 'no', 'maybe', ..., so anything different from
* '' and 'no' will do.
*
* @constructor
*
*/
CAAT.AudioManager= function() {
this.browserInfo= new CAAT.BrowserDetect();
return this;
};
CAAT.AudioManager.prototype= {
browserInfo: null,
musicEnabled: true,
fxEnabled: true,
audioCache: null, // audio elements.
channels: null, // available playing channels.
workingChannels: null, // currently playing channels.
loopingChannels: [],
audioTypes: { // supported audio formats. Don't remember where i took them from :S
'mp3': 'audio/mpeg;',
'ogg': 'audio/ogg; codecs="vorbis"',
'wav': 'audio/wav; codecs="1"',
'mp4': 'audio/mp4; codecs="mp4a.40.2"'
},
/**
* Initializes the sound subsystem by creating a fixed number of Audio channels.
* Every channel registers a handler for sound playing finalization. If a callback is set, the
* callback function will be called with the associated sound id in the cache.
*
* @param numChannels {number} number of channels to pre-create. 8 by default.
*
* @return this.
*/
initialize : function(numChannels) {
this.audioCache= [];
this.channels= [];
this.workingChannels= [];
for( var i=0; i
* When the audio attribute is an array, this function will iterate throught the array elements
* until a suitable audio element to be played is found. When this is the case, the other array
* elements won't be taken into account. The valid form of using this addAudio method will be:
*
*
* 1.
* 2.
* 3.
* Firefox does not honor the loop property, so looping is performed by attending end playing
* event on audio elements.
*
* @return {HTMLElement} an Audio instance if a valid sound id is supplied. Null otherwise
*/
loop : function( id ) {
if (!this.musicEnabled) {
return this;
}
var audio_in_cache= this.getAudio(id);
// existe el audio, y ademas hay un canal de audio disponible.
if ( null!==audio_in_cache ) {
var audio= document.createElement('audio');
if ( null!==audio ) {
audio.src= audio_in_cache.src;
audio.preload = "auto";
if ( this.browserInfo.browser==='Firefox') {
audio.addEventListener(
'ended',
// on sound end, set channel to available channels list.
function(audioEvent) {
var target= audioEvent.target;
target.currentTime=0;
},
false
);
} else {
audio.loop= true;
}
audio.load();
audio.play();
this.loopingChannels.push(audio);
return audio;
}
}
return null;
},
/**
* Cancel all playing audio channels
* Get back the playing channels to available channel list.
*
* @return this
*/
endSound : function() {
var i;
for( i=0; i
* Be aware that actor mouse functions must be set prior to calling this method. The Dock actor
* needs set his own actor input events functions for mouseEnter, mouseExit and mouseMove and
* will then chain to the original methods set by the developer.
*
* @param actor {CAAT.Actor} a CAAT.Actor instance.
*
* @return this
*/
addChild : function(actor) {
var me= this;
actor.__Dock_mouseEnter= actor.mouseEnter;
actor.__Dock_mouseExit= actor.mouseExit;
actor.__Dock_mouseMove= actor.mouseMove;
/**
* @ignore
* @param mouseEvent
*/
actor.mouseEnter= function(mouseEvent) {
me.actorMouseEnter(mouseEvent);
this.__Dock_mouseEnter(mouseEvent);
};
/**
* @ignore
* @param mouseEvent
*/
actor.mouseExit= function(mouseEvent) {
me.actorMouseExit(mouseEvent);
this.__Dock_mouseExit(mouseEvent);
};
/**
* @ignore
* @param mouseEvent
*/
actor.mouseMove= function(mouseEvent) {
me.actorPointed( mouseEvent.point.x, mouseEvent.point.y, mouseEvent.source );
this.__Dock_mouseMove(mouseEvent);
};
actor.width= this.minSize;
actor.height= this.minSize;
return CAAT.Dock.superclass.addChild.call(this,actor);
}
};
extend( CAAT.Dock, CAAT.ActorContainer, null);
})();
/**
* See LICENSE file.
*
**/
(function() {
/**
* Director is the animator scene graph manager.
*
* The director elements is an ActorContainer itself with the main responsibility of managing
* different Scenes.
*
* It is responsible for:
*
* One document can contain different CAAT.Director instances which will be kept together in CAAT
* function.
*
* @constructor
* @extends CAAT.ActorContainer
*/
CAAT.Director = function() {
CAAT.Director.superclass.constructor.call(this);
this.browserInfo = new CAAT.BrowserDetect();
this.audioManager = new CAAT.AudioManager().initialize(8);
this.scenes = [];
// input related variables initialization
this.mousePoint = new CAAT.Point(0, 0, 0);
this.prevMousePoint = new CAAT.Point(0, 0, 0);
this.screenMousePoint = new CAAT.Point(0, 0, 0);
this.isMouseDown = false;
this.lastSelectedActor = null;
this.dragging = false;
this.cDirtyRects= [];
this.dirtyRects= [];
for( var i=0; i<64; i++ ) {
this.dirtyRects.push( new CAAT.Rectangle() );
}
this.dirtyRectsIndex= 0;
return this;
};
CAAT.Director.CLEAR_DIRTY_RECTS= 1;
CAAT.Director.CLEAR_ALL= true;
CAAT.Director.CLEAR_NONE= false;
CAAT.Director.prototype = {
debug: false, // flag indicating debug mode. It will draw affedted screen areas.
onRenderStart: null,
onRenderEnd: null,
// input related attributes
mousePoint: null, // mouse coordinate related to canvas 0,0 coord.
prevMousePoint: null, // previous mouse position cache. Needed for drag events.
screenMousePoint: null, // screen mouse coordinates.
isMouseDown: false, // is the left mouse button pressed ?
lastSelectedActor: null, // director's last actor receiving input.
dragging: false, // is in drag mode ?
// other attributes
scenes: null, // Scenes collection. An array.
currentScene: null, // The current Scene. This and only this will receive events.
canvas: null, // The canvas the Director draws on.
crc: null, // @deprecated. canvas rendering context
ctx: null, // refactoring crc for a more convenient name
time: 0, // virtual actor time.
timeline: 0, // global director timeline.
imagesCache: null, // An array of JSON elements of the form { id:string, image:Image }
audioManager: null,
clear: true, // clear background before drawing scenes ??
transitionScene: null,
browserInfo: null,
gl: null,
glEnabled: false,
glTextureManager: null,
glTtextureProgram: null,
glColorProgram: null,
pMatrix: null, // projection matrix
coords: null, // Float32Array
coordsIndex: 0,
uv: null,
uvIndex: 0,
front_to_back: false,
statistics: {
size_total: 0,
size_active: 0,
size_dirtyRects: 0,
draws: 0
},
currentTexturePage: 0,
currentOpacity: 1,
intervalId: null,
frameCounter: 0,
RESIZE_NONE: 1,
RESIZE_WIDTH: 2,
RESIZE_HEIGHT: 4,
RESIZE_BOTH: 8,
RESIZE_PROPORTIONAL:16,
resize: 1,
onResizeCallback : null,
__gestureScale : 0,
__gestureRotation : 0,
dirtyRects : null,
cDirtyRects : null,
dirtyRectsIndex : 0,
dirtyRectsEnabled : false,
nDirtyRects : 0,
stopped : false, // is stopped, this director will do nothing.
checkDebug : function() {
if ( CAAT.DEBUG ) {
var dd= new CAAT.Debug().initialize( this.width, 60 );
this.debugInfo= dd.debugInfo.bind(dd);
}
},
getRenderType : function() {
return this.glEnabled ? 'WEBGL' : 'CANVAS';
},
windowResized : function(w, h) {
switch (this.resize) {
case this.RESIZE_WIDTH:
this.setBounds(0, 0, w, this.height);
break;
case this.RESIZE_HEIGHT:
this.setBounds(0, 0, this.width, h);
break;
case this.RESIZE_BOTH:
this.setScaleBoth(w,h);
break;
case this.RESIZE_PROPORTIONAL:
this.setScaleProportional(w,h);
break;
}
if ( this.glEnabled ) {
this.glReset();
}
if ( this.onResizeCallback ) {
this.onResizeCallback( this, w, h );
}
},setScaleBoth : function(w,h) {
// var factor= Math.min(w/this.referenceWidth, h/this.referenceHeight);
var wf = w/this.referenceWidth;
var hf = h/this.referenceHeight
this.setScaleAnchored( wf, hf, 0, 0 );
this.canvas.width = this.referenceWidth*wf;
this.canvas.height = this.referenceHeight*hf;
this.ctx = this.canvas.getContext(this.glEnabled ? 'experimental-webgl' : '2d' );
this.crc = this.ctx;
if ( this.glEnabled ) {
this.glReset();
}
},
setScaleProportional : function(w,h) {
var factor= Math.min(w/this.referenceWidth, h/this.referenceHeight);
this.setScaleAnchored( factor, factor, 0, 0 );
this.canvas.width = this.referenceWidth*factor;
this.canvas.height = this.referenceHeight*factor;
this.ctx = this.canvas.getContext(this.glEnabled ? 'experimental-webgl' : '2d' );
this.crc = this.ctx;
if ( this.glEnabled ) {
this.glReset();
}
},
/**
* Enable window resize events and set redimension policy. A callback functio could be supplied
* to be notified on a Director redimension event. This is necessary in the case you set a redim
* policy not equal to RESIZE_PROPORTIONAL. In those redimension modes, director's area and their
* children scenes are resized to fit the new area. But scenes content is not resized, and have
* no option of knowing so uless an onResizeCallback function is supplied.
*
* @param mode {number} RESIZE_BOTH, RESIZE_WIDTH, RESIZE_HEIGHT, RESIZE_NONE.
* @param onResizeCallback {function(director{CAAT.Director}, width{integer}, height{integer})} a callback
* to notify on canvas resize.
*/
enableResizeEvents : function(mode, onResizeCallback) {
if (mode === this.RESIZE_BOTH || mode === this.RESIZE_WIDTH || mode === this.RESIZE_HEIGHT || mode===this.RESIZE_PROPORTIONAL) {
this.referenceWidth= this.width;
this.referenceHeight=this.height;
this.resize = mode;
CAAT.registerResizeListener(this);
this.onResizeCallback= onResizeCallback;
this.windowResized( window.innerWidth, window.innerHeight );
} else {
CAAT.unregisterResizeListener(this);
this.onResizeCallback= null;
}
},
/**
* Set this director's bounds as well as its contained scenes.
* @param x {number} ignored, will be 0.
* @param y {number} ignored, will be 0.
* @param w {number} director width.
* @param h {number} director height.
*
* @return this
*/
setBounds : function(x, y, w, h) {
CAAT.Director.superclass.setBounds.call(this, x, y, w, h);
this.canvas.width = w;
this.canvas.height = h;
this.ctx = this.canvas.getContext(this.glEnabled ? 'experimental-webgl' : '2d');
this.crc = this.ctx;
for (var i = 0; i < this.scenes.length; i++) {
this.scenes[i].setBounds(0, 0, w, h);
}
if ( this.glEnabled ) {
this.glReset();
}
return this;
},
/**
* This method performs Director initialization. Must be called once.
* If the canvas parameter is not set, it will create a Canvas itself,
* and the developer must explicitly add the canvas to the desired DOM position.
* This method will also set the Canvas dimension to the specified values
* by width and height parameters.
*
* @param width {number} a canvas width
* @param height {number} a canvas height
* @param canvas {HTMLCanvasElement=} An optional Canvas object.
* @param proxy {HTMLElement} this object can be an event proxy in case you'd like to layer different elements
* and want events delivered to the correct element.
*
* @return this
*/
initialize : function(width, height, canvas, proxy) {
if ( !canvas ) {
canvas= document.createElement('canvas');
document.body.appendChild(canvas);
}
this.canvas = canvas;
if ( typeof proxy==='undefined' ) {
proxy= canvas;
}
this.setBounds(0, 0, width, height);
this.create();
this.enableEvents(proxy);
this.timeline = new Date().getTime();
// transition scene
this.transitionScene = new CAAT.Scene().setBounds(0, 0, width, height);
var transitionCanvas = document.createElement('canvas');
transitionCanvas.width = width;
transitionCanvas.height = height;
var transitionImageActor = new CAAT.Actor().setBackgroundImage(transitionCanvas);
this.transitionScene.ctx = transitionCanvas.getContext('2d');
this.transitionScene.addChildImmediately(transitionImageActor);
this.transitionScene.setEaseListener(this);
this.checkDebug();
return this;
},
glReset : function() {
this.pMatrix= makeOrtho( 0, this.referenceWidth, this.referenceHeight, 0, -1, 1 );
this.gl.viewport(0,0,this.canvas.width,this.canvas.height);
this.glColorProgram.setMatrixUniform(this.pMatrix);
this.glTextureProgram.setMatrixUniform(this.pMatrix);
this.gl.viewportWidth = this.canvas.width;
this.gl.viewportHeight = this.canvas.height;
},
/**
* Experimental.
* Initialize a gl enabled director.
* @param width
* @param height
* @param canvas
*/
initializeGL : function(width, height, canvas, proxy) {
if ( !canvas ) {
canvas= document.createElement('canvas');
document.body.appendChild(canvas);
}
canvas.width = width;
canvas.height = height;
if ( typeof proxy==='undefined' ) {
proxy= canvas;
}
this.referenceWidth= width;
this.referenceHeight=height;
var i;
try {
this.gl = canvas.getContext("experimental-webgl"/*, {antialias: false}*/);
this.gl.viewportWidth = width;
this.gl.viewportHeight = height;
CAAT.GLRENDER= true;
} catch(e) {
}
if (this.gl) {
this.canvas = canvas;
this.create();
this.setBounds(0, 0, width, height);
this.crc = this.ctx;
this.enableEvents(canvas);
this.timeline = new Date().getTime();
this.glColorProgram = new CAAT.ColorProgram(this.gl).create().initialize();
this.glTextureProgram = new CAAT.TextureProgram(this.gl).create().initialize();
this.glTextureProgram.useProgram();
this.glReset();
var maxTris = 512;
this.coords = new Float32Array(maxTris * 12);
this.uv = new Float32Array(maxTris * 8);
this.gl.clearColor(0.0, 0.0, 0.0, 255);
if (this.front_to_back) {
this.gl.clearDepth(1.0);
this.gl.enable(this.gl.DEPTH_TEST);
this.gl.depthFunc(this.gl.LESS);
} else {
this.gl.disable(this.gl.DEPTH_TEST);
}
this.gl.enable(this.gl.BLEND);
// Fix FF this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
this.glEnabled = true;
this.checkDebug();
} else {
// fallback to non gl enabled canvas.
return this.initialize(width, height, canvas);
}
return this;
},
/**
* Creates an initializes a Scene object.
* @return {CAAT.Scene}
*/
createScene : function() {
var scene = new CAAT.Scene().create();
this.addScene(scene);
return scene;
},
setImagesCache : function(imagesCache, tpW, tpH) {
var i;
if (null !== this.glTextureManager) {
this.glTextureManager.deletePages();
this.glTextureManager = null;
}
// delete previous image identifiers
if ( this.imagesCache ) {
var ids= [];
for ( i = 0; i < this.imagesCache.length; i++) {
ids.push( this.imagesCache[i].id );
}
for( i=0; i
* Until the creation of this method, both scenes where drawn while transitioning with
* its performance penalty since drawing two scenes could be twice as expensive than drawing
* only one.
*
* Though a high performance increase, we should keep an eye on memory consumption.
*
* @param ctx a
* The type of transition will be one of the following values defined in CAAT.Scene.prototype:
*
* The anchor will be any of these values defined in CAAT.Actor.prototype:
*
* In example, for an entering scene performing a EASE_SCALE transition, the anchor is the
* point by which the scene will scaled.
*
* @param inSceneIndex integer indicating the Scene index to bring in to the Director.
* @param typein integer indicating the type of transition to apply to the bringing in Scene.
* @param anchorin integer indicating the anchor of the bringing in Scene.
* @param outSceneIndex integer indicating the Scene index to take away from the Director.
* @param typeout integer indicating the type of transition to apply to the taking away in Scene.
* @param anchorout integer indicating the anchor of the taking away Scene.
* @param time inteter indicating the time to perform the process of switchihg between Scene object
* in milliseconds.
* @param alpha boolean boolean indicating whether alpha transparency fading will be applied to
* the scenes.
* @param interpolatorIn CAAT.Interpolator object to apply to entering scene.
* @param interpolatorOut CAAT.Interpolator object to apply to exiting scene.
*/
easeInOut : function(inSceneIndex, typein, anchorin, outSceneIndex, typeout, anchorout, time, alpha, interpolatorIn, interpolatorOut) {
if (inSceneIndex === this.getCurrentSceneIndex()) {
return;
}
var ssin = this.scenes[ inSceneIndex ];
var sout = this.scenes[ outSceneIndex ];
if ( !CAAT.__CSS__ && !this.glEnabled ) {
this.renderToContext(this.transitionScene.ctx, sout);
sout = this.transitionScene;
}
ssin.setExpired(false);
sout.setExpired(false);
ssin.mouseEnabled = false;
sout.mouseEnabled = false;
ssin.resetTransform();
sout.resetTransform();
ssin.setLocation(0, 0);
sout.setLocation(0, 0);
ssin.alpha = 1;
sout.alpha = 1;
if (typein === CAAT.Scene.prototype.EASE_ROTATION) {
ssin.easeRotationIn(time, alpha, anchorin, interpolatorIn);
} else if (typein === CAAT.Scene.prototype.EASE_SCALE) {
ssin.easeScaleIn(0, time, alpha, anchorin, interpolatorIn);
} else {
ssin.easeTranslationIn(time, alpha, anchorin, interpolatorIn);
}
if (typeout === CAAT.Scene.prototype.EASE_ROTATION) {
sout.easeRotationOut(time, alpha, anchorout, interpolatorOut);
} else if (typeout === CAAT.Scene.prototype.EASE_SCALE) {
sout.easeScaleOut(0, time, alpha, anchorout, interpolatorOut);
} else {
sout.easeTranslationOut(time, alpha, anchorout, interpolatorOut);
}
this.childrenList = [];
this.addChild(sout);
this.addChild(ssin);
},
/**
* This method will switch between two given Scene indexes (ie, take away scene number 2,
* and bring in scene number 5).
*
* It will randomly choose for each Scene the type of transition to apply and the anchor
* point of each transition type.
*
* It will also set for different kind of transitions the following interpolators:
*
* These are the default values, and could not be changed by now.
* This method in final instance delegates the process to easeInOutMethod.
*
* @see easeInOutMethod.
*
* @param inIndex integer indicating the entering scene index.
* @param outIndex integer indicating the exiting scene index.
* @param time integer indicating the time to take for the process of Scene in/out in milliseconds.
* @param alpha boolean indicating whether alpha transparency fading should be applied to transitions.
*/
easeInOutRandom : function(inIndex, outIndex, time, alpha) {
var pin = Math.random();
var pout = Math.random();
var typeIn;
var interpolatorIn;
if (pin < 0.33) {
typeIn = CAAT.Scene.prototype.EASE_ROTATION;
interpolatorIn = new CAAT.Interpolator().createExponentialInOutInterpolator(4);
} else if (pin < 0.66) {
typeIn = CAAT.Scene.prototype.EASE_SCALE;
interpolatorIn = new CAAT.Interpolator().createElasticOutInterpolator(1.1, 0.4);
} else {
typeIn = CAAT.Scene.prototype.EASE_TRANSLATE;
interpolatorIn = new CAAT.Interpolator().createBounceOutInterpolator();
}
var typeOut;
var interpolatorOut;
if (pout < 0.33) {
typeOut = CAAT.Scene.prototype.EASE_ROTATION;
interpolatorOut = new CAAT.Interpolator().createExponentialInOutInterpolator(4);
} else if (pout < 0.66) {
typeOut = CAAT.Scene.prototype.EASE_SCALE;
interpolatorOut = new CAAT.Interpolator().createExponentialOutInterpolator(4);
} else {
typeOut = CAAT.Scene.prototype.EASE_TRANSLATE;
interpolatorOut = new CAAT.Interpolator().createBounceOutInterpolator();
}
this.easeInOut(
inIndex,
typeIn,
(Math.random() * 8.99) >> 0,
outIndex,
typeOut,
(Math.random() * 8.99) >> 0,
time,
alpha,
interpolatorIn,
interpolatorOut);
},
/**
* This method changes Director's current Scene to the scene index indicated by
* inSceneIndex parameter. The Scene running in the director won't be eased out.
*
* @see {CAAT.Interpolator}
* @see {CAAT.Actor}
* @see {CAAT.Scene}
*
* @param inSceneIndex integer indicating the new Scene to set as current.
* @param type integer indicating the type of transition to apply to bring the new current
* Scene to the Director. The values will be one of: CAAT.Scene.prototype.EASE_ROTATION,
* CAAT.Scene.prototype.EASE_SCALE, CAAT.Scene.prototype.EASE_TRANSLATION.
* @param time integer indicating how much time in milliseconds the Scene entrance will take.
* @param alpha boolean indicating whether alpha transparency fading will be applied to the
* entereing Scene.
* @param anchor integer indicating the anchor to fix for Scene transition. It will be any of
* CAAT.Actor.prototype.ANCHOR_* values.
* @param interpolator an CAAT.Interpolator object indicating the interpolation function to
* apply.
*/
easeIn : function(inSceneIndex, type, time, alpha, anchor, interpolator) {
var sin = this.scenes[ inSceneIndex ];
if (type === CAAT.Scene.prototype.EASE_ROTATION) {
sin.easeRotationIn(time, alpha, anchor, interpolator);
} else if (type === CAAT.Scene.prototype.EASE_SCALE) {
sin.easeScaleIn(0, time, alpha, anchor, interpolator);
} else {
sin.easeTranslationIn(time, alpha, anchor, interpolator);
}
this.childrenList = [];
this.addChild(sin);
sin.resetTransform();
sin.setLocation(0, 0);
sin.alpha = 1;
sin.mouseEnabled = false;
sin.setExpired(false);
},
/**
* Changes (or sets) the current Director scene to the index
* parameter. There will be no transition on scene change.
* @param sceneIndex {number} an integer indicating the index of the target Scene
* to be shown.
*/
setScene : function(sceneIndex) {
var sin = this.scenes[ sceneIndex ];
this.childrenList = [];
this.addChild(sin);
this.currentScene = sin;
sin.setExpired(false);
sin.mouseEnabled = true;
sin.resetTransform();
sin.setLocation(0, 0);
sin.alpha = 1;
sin.activated();
},
/**
* This method will change the current Scene by the Scene indicated as parameter.
* It will apply random values for anchor and transition type.
* @see easeInOutRandom
*
* @param iNewSceneIndex {number} an integer indicating the index of the new scene to run on the Director.
* @param time {number} an integer indicating the time the Scene transition will take.
* @param alpha {boolean} a boolean indicating whether Scene transition should be fading.
* @param transition {boolean} a boolean indicating whether the scene change must smoothly animated.
*/
switchToScene : function(iNewSceneIndex, time, alpha, transition) {
var currentSceneIndex = this.getSceneIndex(this.currentScene);
if (!transition) {
this.setScene(iNewSceneIndex);
}
else {
this.easeInOutRandom(iNewSceneIndex, currentSceneIndex, time, alpha);
}
},
/**
* Sets the previous Scene in sequence as the current Scene.
* @see switchToScene.
*
* @param time {number} integer indicating the time the Scene transition will take.
* @param alpha {boolean} a boolean indicating whether Scene transition should be fading.
* @param transition {boolean} a boolean indicating whether the scene change must smoothly animated.
*/
switchToPrevScene : function(time, alpha, transition) {
var currentSceneIndex = this.getSceneIndex(this.currentScene);
if (this.getNumScenes() <= 1 || currentSceneIndex === 0) {
return;
}
if (!transition) {
this.setScene(currentSceneIndex - 1);
}
else {
this.easeInOutRandom(currentSceneIndex - 1, currentSceneIndex, time, alpha);
}
},
/**
* Sets the previous Scene in sequence as the current Scene.
* @see switchToScene.
*
* @param time {number} integer indicating the time the Scene transition will take.
* @param alpha {boolean} a boolean indicating whether Scene transition should be fading.
* @param transition {boolean} a boolean indicating whether the scene change must smoothly animated.
*/
switchToNextScene: function(time, alpha, transition) {
var currentSceneIndex = this.getSceneIndex(this.currentScene);
if (this.getNumScenes() <= 1 || currentSceneIndex === this.getNumScenes() - 1) {
return;
}
if (!transition) {
this.setScene(currentSceneIndex + 1);
}
else {
this.easeInOutRandom(currentSceneIndex + 1, currentSceneIndex, time, alpha);
}
},
mouseEnter : function(mouseEvent) {
},
mouseExit : function(mouseEvent) {
},
mouseMove : function(mouseEvent) {
},
mouseDown : function(mouseEvent) {
},
mouseUp : function(mouseEvent) {
},
mouseDrag : function(mouseEvent) {
},
/**
* Scene easing listener. Notifies scenes when they're about to be activated (set as current
* director's scene).
*
* @param scene {CAAT.Scene} the scene that has just been brought in or taken out of the director.
* @param b_easeIn {boolean} scene enters or exits ?
*/
easeEnd : function(scene, b_easeIn) {
// scene is going out
if (!b_easeIn) {
scene.setExpired(true);
} else {
this.currentScene = scene;
this.currentScene.activated();
}
scene.mouseEnabled = true;
scene.emptyBehaviorList();
},
/**
* Return the index for a given Scene object contained in the Director.
* @param scene {CAAT.Scene}
*/
getSceneIndex : function(scene) {
for (var i = 0; i < this.scenes.length; i++) {
if (this.scenes[i] === scene) {
return i;
}
}
return -1;
},
/**
* Get a concrete director's scene.
* @param index {number} an integer indicating the scene index.
* @return {CAAT.Scene} a CAAT.Scene object instance or null if the index is oob.
*/
getScene : function(index) {
return this.scenes[index];
},
/**
* Return the index of the current scene in the Director's scene list.
* @return {number} the current scene's index.
*/
getCurrentSceneIndex : function() {
return this.getSceneIndex(this.currentScene);
},
/**
* Return the running browser name.
* @return {string} the browser name.
*/
getBrowserName : function() {
return this.browserInfo.browser;
},
/**
* Return the running browser version.
* @return {string} the browser version.
*/
getBrowserVersion : function() {
return this.browserInfo.version;
},
/**
* Return the operating system name.
* @return {string} the os name.
*/
getOSName : function() {
return this.browserInfo.OS;
},
/**
* Gets the resource with the specified resource name.
* The Director holds a collection called
* The fps parameter will set the animation quality. Higher values,
* means CAAT will try to render more frames in the same second (at the
* expense of cpu power at least until hardware accelerated canvas rendering
* context are available). A value of 60 is a high frame rate and should not be exceeded.
*
* @param fps {number} integer value indicating the target frames per second to run
* the animation at.
*/
renderFrame : function(fps, callback) {
if (this.stopped) {
return;
}
var t = new Date().getTime(),
delta = t - this.timeline;
/*
check for massive frame time. if for example the current browser tab is minified or taken out of
foreground, the system will account for a bit time interval. minify that impact by lowering down
the elapsed time (virtual timelines FTW)
*/
if ( delta > 500 ) {
delta= 500;
}
if ( this.onRenderStart ) {
this.onRenderStart(delta);
}
this.render(delta);
if ( this.debugInfo ) {
this.debugInfo(this.statistics);
}
this.timeline = t;
if (this.onRenderEnd) {
this.onRenderEnd(delta);
}
},
endLoop : function () {
},
/**
* This method states whether the director must clear background before rendering
* each frame.
*
* The clearing method could be:
* + CAAT.Director.CLEAR_ALL. previous to draw anything on screen the canvas will have clearRect called on it.
* + CAAT.Director.CLEAR_DIRTY_RECTS. Actors marked as invalid, or which have been moved, rotated or scaled
* will have their areas redrawn.
* + CAAT.Director.CLEAR_NONE. clears nothing.
*
* @param clear {CAAT.Director.CLEAR_ALL |�CAAT.Director.CLEAR_NONE | CAAT.Director.CLEAR_DIRTY_RECTS}
* @return this.
*/
setClear : function(clear) {
this.clear = clear;
if ( this.clear===CAAT.Director.CLEAR_DIRTY_RECTS ) {
this.dirtyRectsEnabled= true;
}
return this;
},
/**
* Get this Director's AudioManager instance.
* @return {CAAT.AudioManager} the AudioManager instance.
*/
getAudioManager : function() {
return this.audioManager;
},
/**
* Acculumate dom elements position to properly offset on-screen mouse/touch events.
* @param node
*/
cumulateOffset : function(node, parent, prop) {
var left= prop+'Left';
var top= prop+'Top';
var x=0, y=0, style;
while( navigator.browser!=='iOS' && node && node.style ) {
if ( node.currentStyle ) {
style= node.currentStyle['position'];
} else {
style= (node.ownerDocument.defaultView || node.ownerDocument.parentWindow).getComputedStyle(node, null);
style= style ? style.getPropertyValue('position') : null;
}
// if (!/^(relative|absolute|fixed)$/.test(style)) {
if (!/^(fixed)$/.test(style)) {
x += node[left];
y+= node[top];
node = node[parent];
} else {
break;
}
}
return {
x: x,
y: y,
style: style
};
},
getOffset : function( node ) {
var res= this.cumulateOffset(node, 'offsetParent', 'offset');
if ( res.style==='fixed' ) {
var res2= this.cumulateOffset(node, node.parentNode ? 'parentNode' : 'parentElement', 'scroll');
return {
x: res.x + res2.x,
y: res.y + res2.y
};
}
return {
x: res.x,
y: res.y
};
},
/**
* Normalize input event coordinates to be related to (0,0) canvas position.
* @param point {CAAT.Point} a CAAT.Point instance to hold the canvas coordinate.
* @param e {MouseEvent} a mouse event from an input event.
*/
getCanvasCoord : function(point, e) {
var pt= new CAAT.Point( );
var posx = 0;
var posy = 0;
if (!e) e = window.event;
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
}
else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
var offset= this.getOffset(this.canvas);
posx-= offset.x;
posy-= offset.y;
//////////////
// transformar coordenada inversamente con affine transform de director.
pt.x= posx;
pt.y= posy;
if ( !this.modelViewMatrixI ) {
this.modelViewMatrixI= this.modelViewMatrix.getInverse();
}
this.modelViewMatrixI.transformCoord(pt);
posx= pt.x;
posy= pt.y
point.set(posx, posy);
this.screenMousePoint.set(posx, posy);
},
__mouseDownHandler : function(e) {
/*
was dragging and mousedown detected, can only mean a mouseOut's been performed and on mouseOver, no
button was presses. Then, send a mouseUp for the previos actor, and return;
*/
if ( this.dragging && this.lastSelectedActor ) {
this.__mouseUpHandler(e);
return;
}
this.getCanvasCoord(this.mousePoint, e);
this.isMouseDown = true;
var lactor = this.findActorAtPosition(this.mousePoint);
if (null !== lactor) {
var pos = lactor.viewToModel(
new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0));
lactor.mouseDown(
new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
lactor,
new CAAT.Point(
this.screenMousePoint.x,
this.screenMousePoint.y )));
}
this.lastSelectedActor= lactor;
},
__mouseUpHandler : function(e) {
this.isMouseDown = false;
this.getCanvasCoord(this.mousePoint, e);
var pos= null;
var lactor= this.lastSelectedActor;
if (null !== lactor) {
pos = lactor.viewToModel(
new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0));
if ( lactor.actionPerformed && lactor.contains(pos.x, pos.y) ) {
lactor.actionPerformed(e)
}
lactor.mouseUp(
new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
lactor,
this.screenMousePoint,
this.currentScene.time));
}
if (!this.dragging && null !== lactor) {
if (lactor.contains(pos.x, pos.y)) {
lactor.mouseClick(
new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
lactor,
this.screenMousePoint,
this.currentScene.time));
}
}
this.dragging = false;
this.in_= false;
// CAAT.setCursor('default');
},
__mouseMoveHandler : function(e) {
//this.getCanvasCoord(this.mousePoint, e);
var lactor;
var pos;
var ct= this.currentScene ? this.currentScene.time : 0;
// drag
if (this.isMouseDown && null !== this.lastSelectedActor) {
lactor = this.lastSelectedActor;
pos = lactor.viewToModel(
new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0));
// check for mouse move threshold.
if (!this.dragging) {
if (Math.abs(this.prevMousePoint.x - pos.x) < CAAT.DRAG_THRESHOLD_X &&
Math.abs(this.prevMousePoint.y - pos.y) < CAAT.DRAG_THRESHOLD_Y) {
return;
}
}
this.dragging = true;
var px= lactor.x;
var py= lactor.y;
lactor.mouseDrag(
new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
lactor,
new CAAT.Point(
this.screenMousePoint.x,
this.screenMousePoint.y),
ct));
this.prevMousePoint.x= pos.x;
this.prevMousePoint.y= pos.y;
/**
* Element has not moved after drag, so treat it as a button.
*/
if ( px===lactor.x && py===lactor.y ) {
var contains= lactor.contains(pos.x, pos.y);
if (this.in_ && !contains) {
lactor.mouseExit(
new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
lactor,
this.screenMousePoint,
ct));
this.in_ = false;
}
if (!this.in_ && contains ) {
lactor.mouseEnter(
new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
lactor,
this.screenMousePoint,
ct));
this.in_ = true;
}
}
return;
}
// mouse move.
this.in_= true;
lactor = this.findActorAtPosition(this.mousePoint);
// cambiamos de actor.
if (lactor !== this.lastSelectedActor) {
if (null !== this.lastSelectedActor) {
pos = this.lastSelectedActor.viewToModel(
new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0));
this.lastSelectedActor.mouseExit(
new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
this.lastSelectedActor,
this.screenMousePoint,
ct));
}
if (null !== lactor) {
pos = lactor.viewToModel(
new CAAT.Point( this.screenMousePoint.x, this.screenMousePoint.y, 0));
lactor.mouseEnter(
new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
lactor,
this.screenMousePoint,
ct));
}
}
pos = lactor.viewToModel(
new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0));
if (null !== lactor) {
lactor.mouseMove(
new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
lactor,
this.screenMousePoint,
ct));
}
this.prevMousePoint.x= pos.x;
this.prevMousePoint.y= pos.y;
this.lastSelectedActor = lactor;
},
__mouseOutHandler : function(e) {
if ( this.dragging ) {
return;
}
if (null !== this.lastSelectedActor ) {
this.getCanvasCoord(this.mousePoint, e);
var pos = new CAAT.Point(this.mousePoint.x, this.mousePoint.y, 0);
this.lastSelectedActor.viewToModel(pos);
var ev= new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
this.lastSelectedActor,
this.screenMousePoint,
this.currentScene.time);
this.lastSelectedActor.mouseExit(ev);
this.lastSelectedActor.mouseOut(ev);
if ( !this.dragging ) {
this.lastSelectedActor = null;
}
} else {
this.isMouseDown = false;
this.in_ = false;
}
},
__mouseOverHandler : function(e) {
if (this.dragging ) {
return;
}
var lactor;
var pos, ev;
if ( null==this.lastSelectedActor ) {
lactor= this.findActorAtPosition( this.mousePoint );
if (null !== lactor) {
pos = lactor.viewToModel(
new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0));
ev= new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
lactor,
this.screenMousePoint,
this.currentScene ? this.currentScene.time : 0);
lactor.mouseOver(ev);
lactor.mouseEnter(ev);
}
this.lastSelectedActor= lactor;
} else {
lactor= this.lastSelectedActor;
pos = lactor.viewToModel(
new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0));
ev= new CAAT.MouseEvent().init(
pos.x,
pos.y,
e,
lactor,
this.screenMousePoint,
this.currentScene.time);
lactor.mouseOver(ev);
lactor.mouseEnter(ev);
}
},
__mouseDBLClickHandler : function(e) {
this.getCanvasCoord(this.mousePoint, e);
if (null !== this.lastSelectedActor) {
/*
var pos = this.lastSelectedActor.viewToModel(
new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0));
*/
this.lastSelectedActor.mouseDblClick(
new CAAT.MouseEvent().init(
this.mousePoint.x,
this.mousePoint.y,
e,
this.lastSelectedActor,
this.screenMousePoint,
this.currentScene.time));
}
},
/**
* Same as mouseDown but not preventing event.
* Will only take care of first touch.
* @param e
*/
__touchStartHandler : function(e) {
if ( e.target===this.canvas ) {
e.preventDefault();
e= e.targetTouches[0];
var mp= this.mousePoint;
this.getCanvasCoord(mp, e);
if ( mp.x<0 || mp.y<0 || mp.x>=this.width || mp.y>=this.height ) {
return;
}
this.touching= true;
this.__mouseDownHandler(e);
}
},
__touchEndHandler : function(e) {
if ( this.touching ) {
e.preventDefault();
e= e.changedTouches[0];
var mp= this.mousePoint;
this.getCanvasCoord(mp, e);
this.touching= false;
this.__mouseUpHandler(e);
}
},
__touchMoveHandler : function(e) {
if ( this.touching ) {
e.preventDefault();
if ( this.gesturing ) {
return;
}
for( var i=0; i
* It is able to draw its sub-images in the following ways:
*
* It is supposed to be used in conjunction with
* Upon TimerTask expiration, the TimerTask will notify (if set) the callback function callback_timeout.
* Upon a call to the method cancel, the timer will be set expired, and (if set) the callback to callback_cancel will be
* invoked.
*
* Timer notifications will be performed BEFORE scene loop.
*
* @constructor
*
*/
CAAT.TimerTask= function() {
return this;
};
CAAT.TimerTask.prototype= {
startTime: 0,
duration: 0,
callback_timeout: null,
callback_tick: null,
callback_cancel: null,
scene: null,
taskId: 0,
remove: false,
/**
* Create a TimerTask.
* The taskId will be set by the scene.
* @param startTime {number} an integer indicating TimerTask enable time.
* @param duration {number} an integer indicating TimerTask duration.
* @param callback_timeout {function( sceneTime {number}, timertaskTime{number}, timertask {CAAT.TimerTask} )} on timeout callback function.
* @param callback_tick {function( sceneTime {number}, timertaskTime{number}, timertask {CAAT.TimerTask} )} on tick callback function.
* @param callback_cancel {function( sceneTime {number}, timertaskTime{number}, timertask {CAAT.TimerTask} )} on cancel callback function.
*
* @return this
*/
create: function( startTime, duration, callback_timeout, callback_tick, callback_cancel ) {
this.startTime= startTime;
this.duration= duration;
this.callback_timeout= callback_timeout;
this.callback_tick= callback_tick;
this.callback_cancel= callback_cancel;
return this;
},
/**
* Performs TimerTask operation. The task will check whether it is in frame time, and will
* either notify callback_timeout or callback_tick.
*
* @param time {number} an integer indicating scene time.
* @return this
*
* @protected
*
*/
checkTask : function(time) {
var ttime= time;
ttime-= this.startTime;
if ( ttime>=this.duration ) {
this.remove= true;
if( this.callback_timeout ) {
this.callback_timeout( time, ttime, this );
}
} else {
if ( this.callback_tick ) {
this.callback_tick( time, ttime, this );
}
}
return this;
},
/**
* Reschedules this TimerTask by changing its startTime to current scene's time.
* @param time {number} an integer indicating scene time.
* @return this
*/
reset : function( time ) {
this.remove= false;
this.startTime= time;
this.scene.ensureTimerTask(this);
return this;
},
/**
* Cancels this timer by removing it on scene's next frame. The function callback_cancel will
* be called.
* @return this
*/
cancel : function() {
this.remove= true;
if ( null!=this.callback_cancel ) {
this.callback_cancel( this.scene.time, this.scene.time-this.startTime, this );
}
return this;
}
};
})();
/**
* See LICENSE file.
*
*/
(function() {
/**
* Scene is the top level ActorContainer of the Director at any given time.
* The only time when 2 scenes could be active will be during scene change.
* An scene controls the way it enters/exits the scene graph. It is also the entry point for all
* input related and timed related events to every actor on screen.
*
* @constructor
* @extends CAAT.ActorContainer
*
*/
CAAT.Scene= function() {
CAAT.Scene.superclass.constructor.call(this);
this.timerList= [];
this.fillStyle= null;
return this;
};
CAAT.Scene.prototype= {
easeContainerBehaviour: null, // Behavior container used uniquely for Scene switching.
easeContainerBehaviourListener: null, // who to notify about container behaviour events. Array.
easeIn: false, // When Scene switching, this boolean identifies whether the
// Scene is being brought in, or taken away.
EASE_ROTATION: 1, // Constant values to identify the type of Scene transition
EASE_SCALE: 2, // to perform on Scene switching by the Director.
EASE_TRANSLATE: 3,
timerList: null, // collection of CAAT.TimerTask objects.
timerSequence: 0, // incremental CAAT.TimerTask id.
paused: false,
isPaused : function() {
return this.paused;
},
setPaused : function( paused ) {
this.paused= paused;
},
/**
* Check and apply timers in frame time.
* @param time {number} the current Scene time.
*/
checkTimers : function(time) {
var tl= this.timerList;
var i=tl.length-1;
while( i>=0 ) {
if ( !tl[i].remove ) {
tl[i].checkTask(time);
}
i--;
}
},
/**
* Make sure the timertask is contained in the timer task list by adding it to the list in case it
* is not contained.
* @param timertask {CAAT.TimerTask} a CAAT.TimerTask object.
* @return this
*/
ensureTimerTask : function( timertask ) {
if ( !this.hasTimer(timertask) ) {
this.timerList.push(timertask);
}
return this;
},
/**
* Check whether the timertask is in this scene's timer task list.
* @param timertask {CAAT.TimerTask} a CAAT.TimerTask object.
* @return {boolean} a boolean indicating whether the timertask is in this scene or not.
*/
hasTimer : function( timertask ) {
var tl= this.timerList;
var i=tl.length-1;
while( i>=0 ) {
if ( tl[i]===timertask ) {
return true;
}
i--;
}
return false;
},
/**
* Creates a timer task. Timertask object live and are related to scene's time, so when an Scene
* is taken out of the Director the timer task is paused, and resumed on Scene restoration.
*
* @param startTime {number} an integer indicating the scene time this task must start executing at.
* @param duration {number} an integer indicating the timerTask duration.
* @param callback_timeout {function} timer on timeout callback function.
* @param callback_tick {function} timer on tick callback function.
* @param callback_cancel {function} timer on cancel callback function.
*
* @return {CAAT.TimerTask} a CAAT.TimerTask class instance.
*/
createTimer : function( startTime, duration, callback_timeout, callback_tick, callback_cancel ) {
var tt= new CAAT.TimerTask().create(
startTime,
duration,
callback_timeout,
callback_tick,
callback_cancel );
tt.taskId= this.timerSequence++;
tt.sceneTime = this.time;
tt.scene= this;
this.timerList.push( tt );
return tt;
},
/**
* Removes expired timers. This method must not be called directly.
*/
removeExpiredTimers : function() {
var i;
var tl= this.timerList;
for( i=0; i
* An example of CAAT.Path will be as follows:
*
* This code creates a path composed of four chained segments, starting at (x,y) and having each
* segment starting where the previous one ended.
*
* This class is intended to wrap the other kind of path segment classes when just a one segmented
* path is to be defined. The methods
* This method closes a path by setting its last path segment's last control point
* to be the first path segment's first control point.
*
* This method also sets the path as finished, and calculates all path's information
* such as length and bounding box.
*
* @return this
*/
closePath : function() {
this.getLastPathSegment().setPoint(
this.getFirstPathSegment().startCurvePosition(),
this.getLastPathSegment().numControlPoints()-1 );
this.trackPathX= this.beginPathX;
this.trackPathY= this.beginPathY;
this.endPath();
return this;
},
/**
* Finishes the process of building the path. It involves calculating each path segments length
* and proportional length related to a normalized path length of 1.
* It also sets current paths length.
* These calculi are needed to traverse the path appropriately.
*
* This method must be called explicitly, except when closing a path (that is, calling the
* method closePath) which calls this method as well.
*
* @return this
*/
endPath : function() {
this.pathSegmentStartTime=[];
this.pathSegmentDurationTime= [];
this.updatePath();
return this;
},
/**
* This method, returns a CAAT.Point instance indicating a coordinate in the path.
* The returned coordinate is the corresponding to normalizing the path's length to 1,
* and then finding what path segment and what coordinate in that path segment corresponds
* for the input time parameter.
*
* The parameter time must be a value ranging 0..1.
* If not constrained to these values, the parameter will be modulus 1, and then, if less
* than 0, be normalized to 1+time, so that the value always ranges from 0 to 1.
*
* This method is needed when traversing the path throughout a CAAT.Interpolator instance.
*
* @param time a value between 0 and 1 both inclusive. 0 will return path's starting coordinate.
* 1 will return path's end coordinate.
*
* @return {CAAT.Point}
*/
getPosition : function(time) {
if ( time>1 || time<0 ) {
time%=1;
}
if ( time<0 ) {
time= 1+time;
}
/*
var found= false;
for( var i=0; ifunction getPosition(time) { return { x:time, y: Math.pow(time,2) }�}
.
* meaning that for time=0.5, a value of 0,5*0,5 should use instead.
*
*
* {
* behaviorExpired : function( behavior, time, actor);
* behaviorApplied : function( behavior, time, normalizedTime, actor, value);
* }
*
* setForTime(time, actor)
overriden.
*
* @constructor
*/
CAAT.Behavior= function() {
this.lifecycleListenerList=[];
this.setDefaultInterpolator();
return this;
};
/**
* @enum
*/
CAAT.Behavior.Status= {
NOT_STARTED: 0,
STARTED: 1,
EXPIRED: 2
};
var DefaultInterpolator= new CAAT.Interpolator().createLinearInterpolator(false);
var DefaultPPInterpolator= new CAAT.Interpolator().createLinearInterpolator(true);
CAAT.Behavior.prototype= {
lifecycleListenerList: null, // observer list.
behaviorStartTime: -1, // scene time to start applying the behavior
behaviorDuration: -1, // behavior duration in ms.
cycleBehavior: false, // apply forever ?
status: CAAT.Behavior.NOT_STARTED,
interpolator: null, // behavior application function. linear by default.
actor: null, // actor the Behavior acts on.
id: 0, // an integer id suitable to identify this behavior by number.
timeOffset: 0,
doValueApplication: true,
solved : true,
setValueApplication : function( apply ) {
this.doValueApplication= apply;
return this;
},
setTimeOffset : function( offset ) {
this.timeOffset= offset;
return this;
},
/**
* Sets this behavior id.
* @param id an integer.
*
*/
setId : function( id ) {
this.id= id;
return this;
},
/**
* Sets the default interpolator to a linear ramp, that is, behavior will be applied linearly.
* @return this
*/
setDefaultInterpolator : function() {
this.interpolator= DefaultInterpolator;
return this;
},
/**
* Sets default interpolator to be linear from 0..1 and from 1..0.
* @return this
*/
setPingPong : function() {
this.interpolator= DefaultPPInterpolator;
return this;
},
/**
*
* @param status {CAAT.Behavior.Status}
*/
setStatus : function(status) {
this.status= status;
},
/**
* Sets behavior start time and duration.
* Scene time will be the time of the scene the behavior actor is bound to.
* @param startTime {number} an integer indicating behavior start time in scene time in ms..
* @param duration {number} an integer indicating behavior duration in ms.
*/
setFrameTime : function( startTime, duration ) {
this.behaviorStartTime= startTime;
this.behaviorDuration= duration;
this.setStatus( CAAT.Behavior.Status.NOT_STARTED );
return this;
},
/**
* Sets behavior start time and duration but instead as setFrameTime which sets initial time as absolute time
* regarding scene's time, it uses a relative time offset from current scene time.
* a call to
* setFrameTime( scene.time, duration ) is equivalent to
* setDelayTime( 0, duration )
* @param delay {number}
* @param duration {number}
*/
setDelayTime : function( delay, duration ) {
this.behaviorStartTime= delay;
this.behaviorDuration= duration;
this.setStatus( CAAT.Behavior.Status.NOT_STARTED );
this.solved= false;
return this;
},
setOutOfFrameTime : function() {
this.setStatus( CAAT.Behavior.Status.EXPIRED );
this.behaviorStartTime= Number.MAX_VALUE;
this.behaviorDuration= 0;
return this;
},
/**
* Changes behavior default interpolator to another instance of CAAT.Interpolator.
* If the behavior is not defined by CAAT.Interpolator factory methods, the interpolation function must return
* its values in the range 0..1. The behavior will only apply for such value range.
* @param interpolator a CAAT.Interpolator instance.
*/
setInterpolator : function(interpolator) {
this.interpolator= interpolator;
return this;
},
/**
* This method must no be called directly.
* The director loop will call this method in orther to apply actor behaviors.
* @param time the scene time the behaviro is being applied at.
* @param actor a CAAT.Actor instance the behavior is being applied to.
*/
apply : function( time, actor ) {
if ( !this.solved ) {
this.behaviorStartTime+= time;
this.solved= true;
}
time+= this.timeOffset*this.behaviorDuration;
var orgTime= time;
if ( this.isBehaviorInTime(time,actor) ) {
time= this.normalizeTime(time);
this.fireBehaviorAppliedEvent(
actor,
orgTime,
time,
this.setForTime( time, actor ) );
}
},
/**
* Sets the behavior to cycle, ie apply forever.
* @param bool a boolean indicating whether the behavior is cycle.
*/
setCycle : function(bool) {
this.cycleBehavior= bool;
return this;
},
/**
* Adds an observer to this behavior.
* @param behaviorListener an observer instance.
*/
addListener : function( behaviorListener ) {
this.lifecycleListenerList.push(behaviorListener);
return this;
},
/**
* Remove all registered listeners to the behavior.
*/
emptyListenerList : function() {
this.lifecycleListenerList= [];
return this;
},
/**
* @return an integer indicating the behavior start time in ms..
*/
getStartTime : function() {
return this.behaviorStartTime;
},
/**
* @return an integer indicating the behavior duration time in ms.
*/
getDuration : function() {
return this.behaviorDuration;
},
/**
* Chekcs whether the behaviour is in scene time.
* In case it gets out of scene time, and has not been tagged as expired, the behavior is expired and observers
* are notified about that fact.
* @param time the scene time to check the behavior against.
* @param actor the actor the behavior is being applied to.
* @return a boolean indicating whether the behavior is in scene time.
*/
isBehaviorInTime : function(time,actor) {
var S= CAAT.Behavior.Status;
if ( this.status===S.EXPIRED || this.behaviorStartTime<0 ) {
return false;
}
if ( this.cycleBehavior ) {
if ( time>=this.behaviorStartTime ) {
time= (time-this.behaviorStartTime)%this.behaviorDuration + this.behaviorStartTime;
}
}
if ( time>this.behaviorStartTime+this.behaviorDuration ) {
if ( this.status!==S.EXPIRED ) {
this.setExpired(actor,time);
}
return false;
}
if ( this.status===S.NOT_STARTED ) {
this.status=S.STARTED;
this.fireBehaviorStartedEvent(actor,time);
}
return this.behaviorStartTime<=time; // && time
*
*
* setInterpolator(CAAT.Interpolator)
*
will be useless.
* anchor==CAAT.Actor.prototype.ANCHOR_CUSTOM
*
the custom rotation point is set.
* @param rx
* @param ry
*
*/
setAnchor : function( actor, rx, ry ) {
this.anchorX= rx/actor.width;
this.anchorY= ry/actor.height;
return this;
},
calculateKeyFrameData : function( time ) {
time= this.interpolator.getPosition(time).y;
return "rotate(" + (this.startAngle + time*(this.endAngle-this.startAngle)) +"rad)";
},
/**
* @param prefix {string} browser vendor prefix
* @param name {string} keyframes animation name
* @param keyframessize {integer} number of keyframes to generate
* @override
*/
calculateKeyFramesData : function(prefix, name, keyframessize) {
if ( typeof keyframessize==='undefined' ) {
keyframessize= 100;
}
keyframessize>>=0;
var i;
var kfr;
var kfd= "@-"+prefix+"-keyframes "+name+" {";
for( i=0; i<=keyframessize; i++ ) {
kfr= "" +
(i/keyframessize*100) + "%" + // percentage
"{" +
"-"+prefix+"-transform:" + this.calculateKeyFrameData(i/keyframessize) +
"}\n";
kfd+= kfr;
}
kfd+="}";
return kfd;
}
};
extend( CAAT.RotateBehavior, CAAT.Behavior, null);
})();
(function() {
/**
*
*
*
*
*
*
* @constructor
* @extends CAAT.Behavior
*
*/
CAAT.GenericBehavior= function() {
CAAT.GenericBehavior.superclass.constructor.call(this);
return this;
};
CAAT.GenericBehavior.prototype= {
start: 0,
end: 0,
target: null,
property: null,
callback: null,
/**
* Sets the target objects property to the corresponding value for the given time.
* If a callback function is defined, it is called as well.
*
* @param time {number} the scene time to apply the behavior at.
* @param actor {CAAT.Actor} a CAAT.Actor object instance.
*/
setForTime : function(time, actor) {
var value= this.start+ time*(this.end-this.start);
if ( this.callback ) {
this.callback( value, this.target, actor );
}
if ( this.property ) {
this.target[this.property]= value;
}
},
/**
* Defines the values to apply this behavior.
*
* @param start {number} initial behavior value.
* @param end {number} final behavior value.
* @param target {object} an object. Usually a CAAT.Actor.
* @param property {string} target object's property to set value to.
* @param callback {function} a function of the form
* setFrameTime( scene.time, 1000 ).
* setValues(
* 0,
* 400,
* domElement,
* null,
* function( currentValue, target ) {
* target.style['left']= currentValue+'px';
* }
* );
* function( target, value )
.
*/
setValues : function( start, end, target, property, callback ) {
this.start= start;
this.end= end;
this.target= target;
this.property= property;
this.callback= callback;
return this;
}
};
extend( CAAT.GenericBehavior, CAAT.Behavior, null);
})();
(function() {
/**
* ScaleBehavior applies scale affine transforms in both axis.
* StartScale and EndScale must be supplied for each axis. This method takes care of a FF bug in which if a Scale is
* set to 0, the animation will fail playing.
*
* This behavior specifies anchors in values ranges 0..1
*
* @constructor
* @extends CAAT.Behavior
*
*/
CAAT.ScaleBehavior= function() {
CAAT.ScaleBehavior.superclass.constructor.call(this);
this.anchor= CAAT.Actor.prototype.ANCHOR_CENTER;
return this;
};
CAAT.ScaleBehavior.prototype= {
startScaleX: 1,
endScaleX: 1,
startScaleY: 1,
endScaleY: 1,
anchorX: .50,
anchorY: .50,
getPropertyName : function() {
return "scale";
},
/**
* Applies corresponding scale values for a given time.
*
* @param time the time to apply the scale for.
* @param actor the target actor to Scale.
* @return {object} an object of the form { scaleX: {float}, scaleY: {float}�}
*/
setForTime : function(time,actor) {
var scaleX= this.startScaleX + time*(this.endScaleX-this.startScaleX);
var scaleY= this.startScaleY + time*(this.endScaleY-this.startScaleY);
// Firefox 3.x & 4, will crash animation if either scaleX or scaleY equals 0.
if (0===scaleX ) {
scaleX=0.01;
}
if (0===scaleY ) {
scaleY=0.01;
}
if ( this.doValueApplication ) {
actor.setScaleAnchored( scaleX, scaleY, this.anchorX, this.anchorY );
}
return { scaleX: scaleX, scaleY: scaleY };
},
/**
* Define this scale behaviors values.
*
* Be aware the anchor values are supplied in RELATIVE PERCENT to
* actor's size.
*
* @param startX {number} initial X axis scale value.
* @param endX {number} final X axis scale value.
* @param startY {number} initial Y axis scale value.
* @param endY {number} final Y axis scale value.
* @param anchorx {float} the percent position for anchorX
* @param anchory {float} the percent position for anchorY
*
* @return this.
*/
setValues : function( startX, endX, startY, endY, anchorx, anchory ) {
this.startScaleX= startX;
this.endScaleX= endX;
this.startScaleY= startY;
this.endScaleY= endY;
if ( typeof anchorx!=='undefined' && typeof anchory!=='undefined' ) {
this.anchorX= anchorx;
this.anchorY= anchory;
}
return this;
},
/**
* Set an exact position scale anchor. Use this method when it is hard to
* set a thorough anchor position expressed in percentage.
* @param actor
* @param x
* @param y
*/
setAnchor : function( actor, x, y ) {
this.anchorX= x/actor.width;
this.anchorY= y/actor.height;
return this;
},
calculateKeyFrameData : function( time ) {
var scaleX;
var scaleY;
time= this.interpolator.getPosition(time).y;
scaleX= this.startScaleX + time*(this.endScaleX-this.startScaleX);
scaleY= this.startScaleY + time*(this.endScaleY-this.startScaleY);
return "scaleX("+scaleX+") scaleY("+scaleY+")";
},
calculateKeyFramesData : function(prefix, name, keyframessize) {
if ( typeof keyframessize==='undefined' ) {
keyframessize= 100;
}
keyframessize>>=0;
var i;
var kfr;
var kfd= "@-"+prefix+"-keyframes "+name+" {";
for( i=0; i<=keyframessize; i++ ) {
kfr= "" +
(i/keyframessize*100) + "%" + // percentage
"{" +
"-"+prefix+"-transform:" + this.calculateKeyFrameData(i/keyframessize) +
"}";
kfd+= kfr;
}
kfd+="}";
return kfd;
}
};
extend( CAAT.ScaleBehavior, CAAT.Behavior, null);
})();
(function() {
/**
* AlphaBehavior modifies alpha composition property for an actor.
*
* @constructor
* @extends CAAT.Behavior
*/
CAAT.AlphaBehavior= function() {
CAAT.AlphaBehavior.superclass.constructor.call(this);
return this;
};
CAAT.AlphaBehavior.prototype= {
startAlpha: 0,
endAlpha: 0,
getPropertyName : function() {
return "opacity";
},
/**
* Applies corresponding alpha transparency value for a given time.
*
* @param time the time to apply the scale for.
* @param actor the target actor to set transparency for.
* @return {number} the alpha value set. Normalized from 0 (total transparency) to 1 (total opacity)
*/
setForTime : function(time,actor) {
var alpha= (this.startAlpha+time*(this.endAlpha-this.startAlpha));
if ( this.doValueApplication ) {
actor.setAlpha( alpha );
}
return alpha;
},
/**
* Set alpha transparency minimum and maximum value.
* This value can be coerced by Actor's property isGloblAlpha.
*
* @param start {number} a float indicating the starting alpha value.
* @param end {number} a float indicating the ending alpha value.
*/
setValues : function( start, end ) {
this.startAlpha= start;
this.endAlpha= end;
return this;
},
calculateKeyFrameData : function( time ) {
time= this.interpolator.getPosition(time).y;
return (this.startAlpha+time*(this.endAlpha-this.startAlpha));
},
/**
* @param prefix {string} browser vendor prefix
* @param name {string} keyframes animation name
* @param keyframessize {integer} number of keyframes to generate
* @override
*/
calculateKeyFramesData : function(prefix, name, keyframessize) {
if ( typeof keyframessize==='undefined' ) {
keyframessize= 100;
}
keyframessize>>=0;
var i;
var kfr;
var kfd= "@-"+prefix+"-keyframes "+name+" {";
for( i=0; i<=keyframessize; i++ ) {
kfr= "" +
(i/keyframessize*100) + "%" + // percentage
"{" +
"opacity: " + this.calculateKeyFrameData( i / keyframessize ) +
"}";
kfd+= kfr;
}
kfd+="}";
return kfd;
}
};
extend( CAAT.AlphaBehavior, CAAT.Behavior, null);
})();
(function() {
/**
* CAAT.PathBehavior modifies the position of a CAAT.Actor along the path represented by an
* instance of CAAT.Path
.
*
* @constructor
* @extends CAAT.Behavior
*
*/
CAAT.PathBehavior= function() {
CAAT.PathBehavior.superclass.constructor.call(this);
return this;
};
/**
* @enum
*/
CAAT.PathBehavior.autorotate = {
LEFT_TO_RIGHT: 0, // fix left_to_right direction
RIGHT_TO_LEFT: 1, // fix right_to_left
FREE: 2 // do not apply correction
};
CAAT.PathBehavior.prototype= {
path: null, // the path to traverse
autoRotate : false, // set whether the actor must be rotated tangentially to the path.
prevX: -1, // private, do not use.
prevY: -1, // private, do not use.
autoRotateOp: CAAT.PathBehavior.autorotate.FREE,
getPropertyName : function() {
return "translate";
},
/**
* Sets an actor rotation to be heading from past to current path's point.
* Take into account that this will be incompatible with rotation Behaviors
* since they will set their own rotation configuration.
* @param autorotate {boolean}
* @param autorotateOp {CAAT.PathBehavior.autorotate} whether the sprite is drawn heading to the right.
* @return this.
*/
setAutoRotate : function( autorotate, autorotateOp ) {
this.autoRotate= autorotate;
if (autorotateOp!==undefined) {
this.autoRotateOp= autorotateOp;
}
return this;
},
/**
* Set the behavior path.
* The path can be any length, and will take behaviorDuration time to be traversed.
* @param {CAAT.Path}
*
* @deprecated
*/
setPath : function(path) {
this.path= path;
return this;
},
/**
* Set the behavior path.
* The path can be any length, and will take behaviorDuration time to be traversed.
* @param {CAAT.Path}
* @return this
*/
setValues : function(path) {
return this.setPath(path);
},
/**
* @see Acotr.setPositionAcchor
* @deprecated
* @param tx a float with xoffset.
* @param ty a float with yoffset.
*/
setTranslation : function( tx, ty ) {
return this;
},
calculateKeyFrameData : function( time ) {
time= this.interpolator.getPosition(time).y;
var point= this.path.getPosition(time);
return "translateX("+point.x+"px) translateY("+point.y+"px)" ;
},
calculateKeyFramesData : function(prefix, name, keyframessize) {
if ( typeof keyframessize==='undefined' ) {
keyframessize= 100;
}
keyframessize>>=0;
var i;
var kfr;
var time;
var kfd= "@-"+prefix+"-keyframes "+name+" {";
for( i=0; i<=keyframessize; i++ ) {
kfr= "" +
(i/keyframessize*100) + "%" + // percentage
"{" +
"-"+prefix+"-transform:" + this.calculateKeyFrameData(i/keyframessize) +
"}";
kfd+= kfr;
}
kfd+="}";
return kfd;
},
/**
* Translates the Actor to the corresponding time path position.
* If autoRotate=true, the actor is rotated as well. The rotation anchor will (if set) always be ANCHOR_CENTER.
* @param time an integer indicating the time the behavior is being applied at.
* @param actor a CAAT.Actor instance to be translated.
* @return {object} an object of the form { x: {float}, y: {float}�}
.
*/
setForTime : function(time,actor) {
if ( !this.path ) {
return {
x: actor.x,
y: actor.y
};
}
var point= this.path.getPosition(time);
if ( this.autoRotate ) {
if ( -1===this.prevX && -1===this.prevY ) {
this.prevX= point.x;
this.prevY= point.y;
}
var ax= point.x-this.prevX;
var ay= point.y-this.prevY;
if ( ax===0 && ay===0 ) {
actor.setLocation( point.x, point.y );
return { x: actor.x, y: actor.y };
}
var angle= Math.atan2( ay, ax );
var si= CAAT.SpriteImage.prototype;
var pba= CAAT.PathBehavior.autorotate;
// actor is heading left to right
if ( this.autoRotateOp===pba.LEFT_TO_RIGHT ) {
if ( this.prevX<=point.x ) {
actor.setImageTransformation( si.TR_NONE );
}
else {
actor.setImageTransformation( si.TR_FLIP_HORIZONTAL );
angle+=Math.PI;
}
} else if ( this.autoRotateOp===pba.RIGHT_TO_LEFT ) {
if ( this.prevX<=point.x ) {
actor.setImageTransformation( si.TR_FLIP_HORIZONTAL );
}
else {
actor.setImageTransformation( si.TR_NONE );
angle-=Math.PI;
}
}
actor.setRotation(angle);
this.prevX= point.x;
this.prevY= point.y;
var modulo= Math.sqrt(ax*ax+ay*ay);
ax/=modulo;
ay/=modulo;
}
if ( this.doValueApplication ) {
actor.setLocation( point.x, point.y );
return { x: actor.x, y: actor.y };
} else {
return {
x: point.x,
y: point.y
};
}
},
/**
* Get a point on the path.
* If the time to get the point at is in behaviors frame time, a point on the path will be returned, otherwise
* a default {x:-1, y:-1} point will be returned.
*
* @param time {number} the time at which the point will be taken from the path.
* @return {object} an object of the form {x:float y:float}
*/
positionOnTime : function(time) {
if ( this.isBehaviorInTime(time,null) ) {
time= this.normalizeTime(time);
return this.path.getPosition( time );
}
return {x:-1, y:-1};
}
};
extend( CAAT.PathBehavior, CAAT.Behavior );
})();
(function() {
/**
* ColorBehavior interpolates between two given colors.
* @constructor
*/
CAAT.ColorBehavior= function() {
return this;
};
CAAT.ColorBehavior.prototype= {
};
extend( CAAT.ColorBehavior, CAAT.Behavior );
})();
(function() {
/**
*
* Scale only X or Y axis, instead both at the same time as ScaleBehavior.
*
* @constructor
*/
CAAT.Scale1Behavior= function() {
CAAT.Scale1Behavior.superclass.constructor.call(this);
this.anchor= CAAT.Actor.prototype.ANCHOR_CENTER;
return this;
};
CAAT.Scale1Behavior.prototype= {
startScale: 1,
endScale: 1,
anchorX: .50,
anchorY: .50,
sx : 1,
sy : 1,
applyOnX : true,
getPropertyName : function() {
return "scale";
},
/**
* Applies corresponding scale values for a given time.
*
* @param time the time to apply the scale for.
* @param actor the target actor to Scale.
* @return {object} an object of the form { scaleX: {float}, scaleY: {float}�}
*/
setForTime : function(time,actor) {
var scale= this.startScale + time*(this.endScale-this.startScale);
// Firefox 3.x & 4, will crash animation if either scaleX or scaleY equals 0.
if (0===scale ) {
scale=0.01;
}
if ( this.doValueApplication ) {
if ( this.applyOnX ) {
actor.setScaleAnchored( scale, actor.scaleY, this.anchorX, this.anchorY );
} else {
actor.setScaleAnchored( actor.scaleX, scale, this.anchorX, this.anchorY );
}
}
return scale;
},
/**
* Define this scale behaviors values.
*
* Be aware the anchor values are supplied in RELATIVE PERCENT to
* actor's size.
*
* @param start {number} initial X axis scale value.
* @param end {number} final X axis scale value.
* @param anchorx {float} the percent position for anchorX
* @param anchory {float} the percent position for anchorY
*
* @return this.
*/
setValues : function( start, end, applyOnX, anchorx, anchory ) {
this.startScale= start;
this.endScale= end;
this.applyOnX= !!applyOnX;
if ( typeof anchorx!=='undefined' && typeof anchory!=='undefined' ) {
this.anchorX= anchorx;
this.anchorY= anchory;
}
return this;
},
/**
* Set an exact position scale anchor. Use this method when it is hard to
* set a thorough anchor position expressed in percentage.
* @param actor
* @param x
* @param y
*/
setAnchor : function( actor, x, y ) {
this.anchorX= x/actor.width;
this.anchorY= y/actor.height;
return this;
},
calculateKeyFrameData : function( time ) {
var scale;
time= this.interpolator.getPosition(time).y;
scale= this.startScale + time*(this.endScale-this.startScale);
return this.applyOnX ? "scaleX("+scale+")" : "scaleY("+scale+")";
},
calculateKeyFramesData : function(prefix, name, keyframessize) {
if ( typeof keyframessize==='undefined' ) {
keyframessize= 100;
}
keyframessize>>=0;
var i;
var kfr;
var kfd= "@-"+prefix+"-keyframes "+name+" {";
for( i=0; i<=keyframessize; i++ ) {
kfr= "" +
(i/keyframessize*100) + "%" + // percentage
"{" +
"-"+prefix+"-transform:" + this.calculateKeyFrameData(i/keyframessize) +
"}";
kfd+= kfr;
}
kfd+="}";
return kfd;
}
};
extend( CAAT.Scale1Behavior, CAAT.Behavior );
})();/**
* See LICENSE file.
*
* This object manages CSS3 transitions reflecting applying behaviors.
*
**/
(function() {
CAAT.CSS= {};
CAAT.CSS.PREFIX= (function() {
var prefix = "";
var prefixes = ['WebKit', 'Moz', 'O'];
var keyframes= "";
// guess this browser vendor prefix.
for (var i = 0; i < prefixes.length; i++) {
if (window[prefixes[i] + 'CSSKeyframeRule']) {
prefix = prefixes[i].toLowerCase();
break;
}
}
CAAT.CSS.PROP_ANIMATION= '-'+prefix+'-animation';
return prefix;
})();
CAAT.CSS.applyKeyframe= function( domElement, name, secs, forever ) {
domElement.style[CAAT.CSS.PROP_ANIMATION]= name+' '+(secs/1000)+'s linear both '+(forever ? 'infinite' : '') ;
};
CAAT.CSS.unregisterKeyframes= function( name ) {
var index= CAAT.CSS.getCSSKeyframesIndex(name);
if ( -1!==index ) {
document.styleSheets[0].deleteRule( index );
}
};
/**
*
* @param kfDescriptor {object{ name{string}, behavior{CAAT.Behavior}, size{!number}, overwrite{boolean}}
*/
CAAT.CSS.registerKeyframes= function( kfDescriptor ) {
var name= kfDescriptor.name;
var behavior= kfDescriptor.behavior;
var size= kfDescriptor.size;
var overwrite= kfDescriptor.overwrite;
if ( typeof name==='undefined' || typeof behavior==='undefined' ) {
throw 'Keyframes must be defined by a name and a CAAT.Behavior instance.';
}
if ( typeof size==='undefined' ) {
size= 100;
}
if ( typeof overwrite==='undefined' ) {
overwrite= false;
}
// find if keyframes has already a name set.
var cssRulesIndex= CAAT.CSS.getCSSKeyframesIndex(name);
if (-1!==cssRulesIndex && !overwrite) {
return;
}
var keyframesRule= behavior.calculateKeyframesData(CAAT.CSS.PREFIX, name, size );
if (document.styleSheets) {
if ( !document.styleSheets.length) {
var s = document.createElement('style');
s.type="text/css";
document.getElementsByTagName('head')[ 0 ].appendChild(s);
}
if ( -1!==cssRulesIndex ) {
document.styleSheets[0].deleteRule( cssRulesIndex );
}
document.styleSheets[0].insertRule( keyframesRule, 0 );
}
};
CAAT.CSS.getCSSKeyframesIndex= function(name) {
var ss = document.styleSheets;
for (var i = ss.length - 1; i >= 0; i--) {
try {
var s = ss[i],
rs = s.cssRules ? s.cssRules :
s.rules ? s.rules :
[];
for (var j = rs.length - 1; j >= 0; j--) {
if ( ( rs[j].type === window.CSSRule.WEBKIT_KEYFRAMES_RULE ||
rs[j].type === window.CSSRule.MOZ_KEYFRAMES_RULE ) && rs[j].name === name) {
return j;
}
}
} catch(e) {
}
}
return -1;
};
CAAT.CSS.getCSSKeyframes= function(name) {
var ss = document.styleSheets;
for (var i = ss.length - 1; i >= 0; i--) {
try {
var s = ss[i],
rs = s.cssRules ? s.cssRules :
s.rules ? s.rules :
[];
for (var j = rs.length - 1; j >= 0; j--) {
if ( ( rs[j].type === window.CSSRule.WEBKIT_KEYFRAMES_RULE ||
rs[j].type === window.CSSRule.MOZ_KEYFRAMES_RULE ) && rs[j].name === name) {
return rs[j];
}
}
}
catch(e) {
}
}
return null;
};
})();/**
*
* taken from: http://www.quirksmode.org/js/detect.html
*
* 20101008 Hyperandroid. IE9 seems to identify himself as Explorer and stopped calling himself MSIE.
* Added Explorer description to browser list. Thanks @alteredq for this tip.
*
*/
(function() {
CAAT.BrowserDetect = function() {
this.init();
return this;
};
CAAT.BrowserDetect.prototype = {
browser: '',
version: 0,
OS: '',
init: function()
{
this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
this.version = this.searchVersion(navigator.userAgent) ||
this.searchVersion(navigator.appVersion) ||
"an unknown version";
this.OS = this.searchString(this.dataOS) || "an unknown OS";
},
searchString: function (data) {
for (var i=0;i
*
*
* @constructor
*/
CAAT.Actor = function() {
this.behaviorList= [];
// this.keyframesList= [];
this.lifecycleListenerList= [];
this.AABB= new CAAT.Rectangle();
this.viewVertices= [
new CAAT.Point(0,0,0),
new CAAT.Point(0,0,0),
new CAAT.Point(0,0,0),
new CAAT.Point(0,0,0)
];
this.scaleAnchor= this.ANCHOR_CENTER;
this.modelViewMatrix= new CAAT.Matrix();
this.worldModelViewMatrix= new CAAT.Matrix();
this.resetTransform();
this.setScale(1,1);
this.setRotation(0);
return this;
};
/**
* Reflection information needed to use the inspector.
* Each key defined identifies an object field. For each field, it could be specified:
* + get : accessor function or field name. if ended with () a function will be assumed.
* + set : mutator function or field name. if ended with () a function will be assumed.
* + type : field or accessor function return type.
*
* If not get or set method is defined, the inspector will assume either the field can't be read and/or set.
* If neither get and set are defined, the property will be avoided.
*
* The key can contain a set of comma separated values. This means these properties must be set/modified
* at once in the inspector editor field (if any). The way these functions will be set will be by calling
* the set method (must be a method) as previously defined.
*/
CAAT.Actor.__reflectionInfo= {
"x" : "set:setX(), get:x, type:number",
"cached" : "get:isCached(), type:boolean",
"scaleX,scaleY" : "set:setScale(), type:number"
/*
"y" : "setY,w",
"width" : "setWidth,w",
"height" : "setHeight,w",
"start_time" : "setStartTime,w",
"duration" : "setDuration,w",
"clip" : "setClip,w",
"rotationAngle" : "setRotation,w",
"alpha" : "setAlpha,w",
"isGlobalAlpha" : "isGlobalAlpha,w",
"visible" : "isVisible",
"id" : "getId",
"backgroundImage" : ""*/
};
CAAT.Actor.ANCHOR_CENTER= 0; // constant values to determine different affine transform
CAAT.Actor.ANCHOR_TOP= 1; // anchors.
CAAT.Actor.ANCHOR_BOTTOM= 2;
CAAT.Actor.ANCHOR_LEFT= 3;
CAAT.Actor.ANCHOR_RIGHT= 4;
CAAT.Actor.ANCHOR_TOP_LEFT= 5;
CAAT.Actor.ANCHOR_TOP_RIGHT= 6;
CAAT.Actor.ANCHOR_BOTTOM_LEFT= 7;
CAAT.Actor.ANCHOR_BOTTOM_RIGHT= 8;
CAAT.Actor.ANCHOR_CUSTOM= 9;
CAAT.Actor.CACHE_SIMPLE= 1;
CAAT.Actor.CACHE_DEEP= 2;
CAAT.Actor.prototype= {
lifecycleListenerList: null, // Array of life cycle listener
behaviorList: null, // Array of behaviors to apply to the Actor
parent: null, // Parent of this Actor. May be Scene.
x: 0, // x position on parent. In parent's local coord. system.
y: 0, // y position on parent. In parent's local coord. system.
width: 0, // Actor's width. In parent's local coord. system.
height: 0, // Actor's height. In parent's local coord. system.
start_time: 0, // Start time in Scene time.
duration: Number.MAX_VALUE, // Actor duration in Scene time
clip: false, // should clip the Actor's content against its contour.
clipPath: null,
tAnchorX : 0,
tAnchorY : 0,
scaleX: 0, // transformation. width scale parameter
scaleY: 0, // transformation. height scale parameter
scaleTX: .50, // transformation. scale anchor x position
scaleTY: .50, // transformation. scale anchor y position
scaleAnchor: 0, // transformation. scale anchor
rotationAngle: 0, // transformation. rotation angle in radians
rotationY: .50, // transformation. rotation center y
alpha: 1, // alpha transparency value
rotationX: .50, // transformation. rotation center x
isGlobalAlpha: false, // is this a global alpha
frameAlpha: 1, // hierarchically calculated alpha for this Actor.
expired: false, // set when the actor has been expired
discardable: false, // set when you want this actor to be removed if expired
pointed: false, // is the mouse pointer inside this actor
mouseEnabled: true, // events enabled ?
visible: true,
ANCHOR_CENTER: 0, // constant values to determine different affine transform
ANCHOR_TOP: 1, // anchors.
ANCHOR_BOTTOM: 2,
ANCHOR_LEFT: 3,
ANCHOR_RIGHT: 4,
ANCHOR_TOP_LEFT: 5,
ANCHOR_TOP_RIGHT: 6,
ANCHOR_BOTTOM_LEFT: 7,
ANCHOR_BOTTOM_RIGHT: 8,
ANCHOR_CUSTOM: 9,
fillStyle: null, // any canvas rendering valid fill style.
strokeStyle: null, // any canvas rendering valid stroke style.
time: 0, // Cache Scene time.
AABB: null, // CAAT.Rectangle
viewVertices: null, // model to view transformed vertices.
inFrame: false, // boolean indicating whether this Actor was present on last frame.
dirty: true, // model view is dirty ?
wdirty: true, // world model view is dirty ?
oldX: -1,
oldY: -1,
modelViewMatrix: null, // model view matrix.
worldModelViewMatrix: null, // world model view matrix.
modelViewMatrixI: null, // model view matrix.
worldModelViewMatrixI: null, // world model view matrix.
glEnabled: false,
backgroundImage: null,
id: null,
size_active: 1, // number of animated children
size_total: 1,
__next: null,
__d_ax: -1, // for drag-enabled actors.
__d_ay: -1,
gestureEnabled: false,
invalid : true,
cached : 0, // 0 no, CACHE_SIMPLE | CACHE_DEEP
collides : false,
collidesAsRect : true,
isAA : true, // is this actor/container Axis aligned ? if so, much faster inverse matrices
// can be calculated.
isVisible : function() {
return this.isVisible;
},
setupCollission : function( collides, isCircular ) {
this.collides= collides;
this.collidesAsRect= !isCircular;
},
invalidate : function() {
this.invalid= true;
},
setGestureEnabled : function( enable ) {
this.gestureEnabled= !!enable;
},
isGestureEnabled : function() {
return this.gestureEnabled;
},
getId : function() {
return this.id;
},
setId : function(id) {
this.id= id;
return this;
},
/**
* Set this actor's parent.
* @param parent {CAAT.ActorContainer}
* @return this
*/
setParent : function(parent) {
this.parent= parent;
return this;
},
/**
* Set this actor's background image.
* The need of a background image is to kept compatibility with the new CSSDirector class.
* The image parameter can be either an Image/Canvas or a CAAT.SpriteImage instance. If an image
* is supplied, it will be wrapped into a CAAT.SriteImage instance of 1 row by 1 column.
* If the actor has set an image in the background, the paint method will draw the image, otherwise
* and if set, will fill its background with a solid color.
* If adjust_size_to_image is true, the host actor will be redimensioned to the size of one
* single image from the SpriteImage (either supplied or generated because of passing an Image or
* Canvas to the function). That means the size will be set to [width:SpriteImage.singleWidth,
* height:singleHeight].
*
* WARN: if using a CSS renderer, the image supplied MUST be a HTMLImageElement instance.
*
* @see CAAT.SpriteImage
*
* @param image {Image|HTMLCanvasElement|CAAT.SpriteImage}
* @param adjust_size_to_image {boolean} whether to set this actor's size based on image parameter.
*
* @return this
*/
setBackgroundImage : function(image, adjust_size_to_image ) {
if ( image ) {
if ( !(image instanceof CAAT.SpriteImage) ) {
image= new CAAT.SpriteImage().initialize(image,1,1);
}
image.setOwner(this);
this.backgroundImage= image;
if ( typeof adjust_size_to_image==='undefined' || adjust_size_to_image ) {
this.width= image.getWidth();
this.height= image.getHeight();
}
this.glEnabled= true;
} else {
this.backgroundImage= null;
}
return this;
},
/**
* Set the actor's SpriteImage index from animation sheet.
* @see CAAT.SpriteImage
* @param index {number}
*
* @return this
*/
setSpriteIndex: function(index) {
if ( this.backgroundImage ) {
this.backgroundImage.setSpriteIndex(index);
this.invalidate();
}
return this;
},
/**
* Set this actor's background SpriteImage offset displacement.
* The values can be either positive or negative meaning the texture space of this background
* image does not start at (0,0) but at the desired position.
* @see CAAT.SpriteImage
* @param ox {number} horizontal offset
* @param oy {number} vertical offset
*
* @return this
*/
setBackgroundImageOffset : function( ox, oy ) {
if ( this.backgroundImage ) {
this.backgroundImage.setOffset(ox,oy);
}
return this;
},
/**
* Set this actor's background SpriteImage its animation sequence.
* In its simplet's form a SpriteImage treats a given image as an array of rows by columns
* subimages. If you define d Sprite Image of 2x2, you'll be able to draw any of the 4 subimages.
* This method defines the animation sequence so that it could be set [0,2,1,3,2,1] as the
* animation sequence
* @param ii {ArrayactorLyfeCycleEvent( actor, string_event_type, long_time )
*/
addListener : function( actorListener ) {
this.lifecycleListenerList.push(actorListener);
return this;
},
/**
* Removes an Actor's life cycle listener.
* It will only remove the first occurrence of the given actorListener.
* @param actorListener {object} an Actor's life cycle listener.
*/
removeListener : function( actorListener ) {
var n= this.lifecycleListenerList.length;
while(n--) {
if ( this.lifecycleListenerList[n]===actorListener ) {
// remove the nth element.
this.lifecycleListenerList.splice(n,1);
return;
}
}
},
/**
* Set alpha composition scope. global will mean this alpha value will be its children maximum.
* If set to false, only this actor will have this alpha value.
* @param global {boolean} whether the alpha value should be propagated to children.
*/
setGlobalAlpha : function( global ) {
this.isGlobalAlpha= global;
return this;
},
/**
* Notifies the registered Actor's life cycle listener about some event.
* @param sEventType an string indicating the type of event being notified.
* @param time an integer indicating the time related to Scene's timeline when the event
* is being notified.
*/
fireEvent : function(sEventType, time) {
for( var i=0; iCAAT.Path
.
*
* @constructor
* @extends CAAT.ActorContainer
*
*/
CAAT.TextActor = function() {
CAAT.TextActor.superclass.constructor.call(this);
this.font= "10px sans-serif";
this.textAlign= "left";
this.textBaseline= "top";
this.outlineColor= "black";
this.clip= false;
return this;
};
CAAT.TextActor.TRAVERSE_PATH_FORWARD= 1;
CAAT.TextActor.TRAVERSE_PATH_BACKWARD= -1;
CAAT.TextActor.prototype= {
font: null, // a valid canvas rendering context font description. Default font
// will be "10px sans-serif".
textAlign: null, // a valid canvas rendering context textAlign string. Any of:
// start, end, left, right, center.
// defaults to "left".
textBaseline: null, // a valid canvas rendering context textBaseLine string. Any of:
// top, hanging, middle, alphabetic, ideographic, bottom.
// defaults to "top".
fill: true, // a boolean indicating whether the text should be filled.
textFillStyle : '#eee', // text fill color
text: null, // a string with the text to draw.
textWidth: 0, // an integer indicating text width in pixels.
textHeight: 0, // an integer indicating text height in pixels.
outline: false, // a boolean indicating whether the text should be outlined.
// not all browsers support it.
outlineColor: null, // a valid color description string.
path: null, // a CAAT.Path which will be traversed by the text. [Optional]
pathInterpolator: null, // a CAAT.Interpolator to apply to the path traversing.
pathDuration: 10000, // an integer indicating the time to be taken to traverse the path. ms.
sign: 1, // traverse the path forward or backwards.
/**
* Set the text to be filled. The default Filling style will be set by calling setFillStyle method.
* Default value is true.
* @param fill {boolean} a boolean indicating whether the text will be filled.
* @return this;
*/
setFill : function( fill ) {
this.fill= fill;
return this;
},
setTextFillStyle : function( style ) {
this.textFillStyle= style;
return this;
},
/**
* Sets whether the text will be outlined.
* @param outline {boolean} a boolean indicating whether the text will be outlined.
* @return this;
*/
setOutline : function( outline ) {
this.outline= outline;
return this;
},
setPathTraverseDirection : function(direction) {
this.sign= direction;
return this;
},
/**
* Defines text's outline color.
*
* @param color {string} sets a valid canvas context color.
* @return this.
*/
setOutlineColor : function( color ) {
this.outlineColor= color;
return this;
},
/**
* Set the text to be shown by the actor.
* @param sText a string with the text to be shwon.
* @return this
*/
setText : function( sText ) {
this.text= sText;
if ( null===this.text || this.text==="" ) {
this.width= this.height= 0;
}
this.calcTextSize( CAAT.director[0] );
return this;
},
setTextAlign : function( align ) {
this.textAlign= align;
return this;
},
/**
* Sets text alignment
* @param align
* @deprecated use setTextAlign
*/
setAlign : function( align ) {
return this.setTextAlign(align);
},
/**
* Set text baseline.
* @param baseline
*/
setTextBaseline : function( baseline ) {
this.textBaseline= baseline;
return this;
},
setBaseline : function( baseline ) {
return this.setTextBaseline(baseline);
},
/**
* Sets the font to be applied for the text.
* @param font a string with a valid canvas rendering context font description.
* @return this
*/
setFont : function(font) {
if ( !font ) {
font= "10px sans-serif";
}
this.font= font;
this.calcTextSize( CAAT.director[0] );
return this;
},
/**
* Calculates the text dimension in pixels and stores the values in textWidth and textHeight
* attributes.
* If Actor's width and height were not set, the Actor's dimension will be set to these values.
* @param director a CAAT.Director instance.
* @return this
*/
calcTextSize : function(director) {
if ( typeof this.text==='undefined' || null===this.text || ""===this.text ) {
this.textWidth= 0;
this.textHeight= 0;
return this;
}
if ( director.glEnabled ) {
return this;
}
if ( this.font instanceof CAAT.SpriteImage ) {
this.textWidth= this.font.stringWidth( this.text );
this.textHeight=this.font.stringHeight();
this.width= this.textWidth;
this.height= this.textHeight;
return this;
}
var ctx= director.ctx;
ctx.save();
ctx.font= this.font;
this.textWidth= ctx.measureText( this.text ).width;
if (this.width===0) {
this.width= this.textWidth;
}
try {
var pos= this.font.indexOf("px");
var s = this.font.substring(0, pos );
this.textHeight= parseInt(s,10);
// needed to calculate the descent.
// no context.getDescent(font) WTF !!!
this.textHeight+= (this.textHeight/4)>>0;
} catch(e) {
this.textHeight=20; // default height;
}
if ( this.height===0 ) {
this.height= this.textHeight;
}
ctx.restore();
return this;
},
/**
* Custom paint method for TextActor instances.
* If the path attribute is set, the text will be drawn traversing the path.
*
* @param director a valid CAAT.Director instance.
* @param time an integer with the Scene time the Actor is being drawn.
*/
paint : function(director, time) {
CAAT.TextActor.superclass.paint.call(this, director, time );
if ( this.cached ) {
// cacheAsBitmap sets this actor's background image as a representation of itself.
// So if after drawing the background it was cached, we're done.
return;
}
if ( null===this.text) {
return;
}
if ( this.textWidth===0 || this.textHeight===0 ) {
this.calcTextSize(director);
}
var ctx= director.ctx;
if ( this.font instanceof CAAT.SpriteImage ) {
return this.drawSpriteText(director,time);
}
if( null!==this.font ) {
ctx.font= this.font;
}
if ( null!==this.textAlign ) {
ctx.textAlign= this.textAlign;
}
if ( null!==this.textBaseline ) {
ctx.textBaseline= this.textBaseline;
}
if ( this.fill && null!==this.textFillStyle ) {
ctx.fillStyle= this.textFillStyle;
}
if ( this.outline && null!==this.outlineColor ) {
ctx.strokeStyle= this.outlineColor;
}
if (null===this.path) {
var tx=0;
if ( this.textAlign==='center') {
tx= (this.width/2)|0;
} else if ( this.textAlign==='right' ) {
tx= this.width;
}
if ( this.fill ) {
ctx.fillText( this.text, tx, 0 );
if ( this.outline ) {
// firefox necesita beginPath, si no, dibujara ademas el cuadrado del
// contenedor de los textos.
// if ( null!==this.outlineColor ) {
// ctx.strokeStyle= this.outlineColor;
// }
ctx.beginPath();
ctx.strokeText( this.text, tx, 0 );
}
} else {
if ( null!==this.outlineColor ) {
ctx.strokeStyle= this.outlineColor;
}
ctx.beginPath();
ctx.strokeText( this.text, tx, 0 );
}
}
else {
this.drawOnPath(director,time);
}
},
/**
* Private.
* Draw the text traversing a path.
* @param director a valid CAAT.Director instance.
* @param time an integer with the Scene time the Actor is being drawn.
*/
drawOnPath : function(director, time) {
var ctx= director.ctx;
var textWidth=this.sign * this.pathInterpolator.getPosition(
(time%this.pathDuration)/this.pathDuration ).y * this.path.getLength() ;
var p0= new CAAT.Point(0,0,0);
var p1= new CAAT.Point(0,0,0);
for( var i=0; iaddSound
* method. The default implementation will accept a URL/URI or a HTMLAudioElement as source.
* loop
method will return a handler to
* give the opportunity of cancelling the sound.
*
*
*
*
* addAudio( id, url } ). In this case, if the resource pointed by url is
* not suitable to be played (i.e. a call to the Audio element's canPlayType method return 'no')
* no resource will be added under such id, so no sound will be played when invoking the play(id)
* method.
*
* addAudio( id, dom_audio_tag ). In this case, the same logic than previous case is applied, but
* this time, the parameter url is expected to be an audio tag present in the html file.
*
* addAudio( id, [array_of_url_or_domaudiotag] ). In this case, the function tries to locate a valid
* resource to be played in any of the elements contained in the array. The array element's can
* be any type of case 1 and 2. As soon as a valid resource is found, it will be associated to the
* id in the valid audio resources to be played list.
*
* @return this
*/
addAudio : function( id, array_of_url_or_domnodes, endplaying_callback ) {
if ( array_of_url_or_domnodes instanceof Array ) {
/*
iterate throught array elements until we can safely add an audio element.
*/
for( var i=0; ipause()
method to stop playing a loop.
*
*
*
* director.addImage(id,image,true)
to finally command the director to create texture pages.
*
* @param id {string|object} an identitifier to retrieve the image with
* @param image {Image|HTMLCanvasElement} image to add to cache
* @param noUpdateGL {!boolean} unless otherwise stated, the director will
* try to recreate the texture pages.
*/
addImage : function( id, image, noUpdateGL ) {
if ( this.getImage(id) ) {
for (var i = 0; i < this.imagesCache.length; i++) {
if (this.imagesCache[i].id === id) {
this.imagesCache[i].image = image;
break;
}
}
this.imagesCache[ id ] = image;
} else {
this.imagesCache.push( { id: id, image: image } );
this.imagesCache[id]= image;
}
if ( !!!noUpdateGL ) {
this.updateGLPages( );
}
},
deleteImage : function( id, noUpdateGL ) {
for (var i = 0; i < this.imagesCache.length; i++) {
if (this.imagesCache[i].id === id) {
delete this.imagesCache[id];
this.imagesCache.splice(i,1);
break;
}
}
if ( !!!noUpdateGL ) {
this.updateGLPages();
}
},
setGLCurrentOpacity : function(opacity) {
this.currentOpacity = opacity;
this.glTextureProgram.setAlpha(opacity);
},
/**
* Render buffered elements.
* @param vertex
* @param coordsIndex
* @param uv
*/
glRender : function(vertex, coordsIndex, uv) {
vertex = vertex || this.coords;
uv = uv || this.uv;
coordsIndex = coordsIndex || this.coordsIndex;
var gl = this.gl;
var numTris = coordsIndex / 12 * 2;
var numVertices = coordsIndex / 3;
this.glTextureProgram.updateVertexBuffer(vertex);
this.glTextureProgram.updateUVBuffer(uv);
gl.drawElements(gl.TRIANGLES, 3 * numTris, gl.UNSIGNED_SHORT, 0);
},
glFlush : function() {
if (this.coordsIndex !== 0) {
this.glRender(this.coords, this.coordsIndex, this.uv);
}
this.coordsIndex = 0;
this.uvIndex = 0;
this.statistics.draws++;
},
findActorAtPosition : function(point) {
// z-order
var cl= this.childrenList;
for( var i=cl.length-1; i>=0; i-- ) {
var child= this.childrenList[i];
var np= new CAAT.Point( point.x, point.y, 0 );
var contained= child.findActorAtPosition( np );
if ( null!==contained ) {
return contained;
}
}
return this;
},
/**
*
* Reset statistics information.
*
* @private
*/
resetStats : function() {
this.statistics.size_total= 0;
this.statistics.size_active=0;
this.statistics.draws= 0;
},
/**
* This is the entry point for the animation system of the Director.
* The director is fed with the elapsed time value to maintain a virtual timeline.
* This virtual timeline will provide each Scene with its own virtual timeline, and will only
* feed time when the Scene is the current Scene, or is being switched.
*
* If dirty rectangles are enabled and canvas is used for rendering, the dirty rectangles will be
* set up as a single clip area.
*
* @param time {number} integer indicating the elapsed time between two consecutive frames of the
* Director.
*/
render : function(time) {
this.time += time;
this.animate(this,time);
if ( CAAT.DEBUG ) {
this.resetStats();
}
/**
* draw director active scenes.
*/
var ne = this.childrenList.length;
var i, tt, c;
var ctx= this.ctx;
if (this.glEnabled) {
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
this.coordsIndex = 0;
this.uvIndex = 0;
for (i = 0; i < ne; i++) {
c = this.childrenList[i];
if (c.isInAnimationFrame(this.time)) {
tt = c.time - c.start_time;
if ( c.onRenderStart ) {
c.onRenderStart(tt);
}
c.paintActorGL(this, tt);
if ( c.onRenderEnd ) {
c.onRenderEnd(tt);
}
if ( !c.isPaused() ) {
c.time += time;
}
if ( CAAT.DEBUG ) {
this.statistics.size_total+= c.size_total;
this.statistics.size_active+= c.size_active;
}
}
}
this.glFlush();
} else {
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = 'source-over';
ctx.save();
if ( this.dirtyRectsEnabled ) {
this.modelViewMatrix.transformRenderingContext( ctx );
if ( !CAAT.DEBUG_DIRTYRECTS ) {
ctx.beginPath();
this.nDirtyRects=0;
var dr= this.cDirtyRects;
for( i=0; icanvas.getContext('2d')
instnce.
* @param scene {CAAT.Scene} the scene to draw offscreen.
*/
renderToContext : function(ctx, scene) {
/**
* draw actors on scene.
*/
if (scene.isInAnimationFrame(this.time)) {
ctx.setTransform(1,0,0,1, 0,0);
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = 'source-over';
ctx.clearRect(0, 0, this.width, this.height);
var octx = this.ctx;
var ocrc = this.crc;
this.ctx = ctx;
this.crc = ctx;
ctx.save();
/**
* to draw an scene to an offscreen canvas, we have to:
* 1.- save diector's world model view matrix
* 2.- set no transformation on director since we want the offscreen to
* be drawn 1:1.
* 3.- set world dirty flag, so that the scene will recalculate its matrices
* 4.- animate the scene
* 5.- paint the scene
* 6.- restore world model view matrix.
*/
var matmv= this.modelViewMatrix;
var matwmv= this.worldModelViewMatrix;
this.worldModelViewMatrix= new CAAT.Matrix();
this.modelViewMatrix= this.worldModelViewMatrix;
this.wdirty= true;
scene.animate(this, scene.time);
if ( scene.onRenderStart ) {
scene.onRenderStart(scene.time);
}
scene.paintActor(this, scene.time);
if ( scene.onRenderEnd ) {
scene.onRenderEnd(scene.time);
}
this.worldModelViewMatrix = matwmv;
this.modelViewMatrix= matmv;
ctx.restore();
this.ctx = octx;
this.crc = ocrc;
}
},
/**
* Add a new Scene to Director's Scene list. By adding a Scene to the Director
* does not mean it will be immediately visible, you should explicitly call either
*
*
*
* @param scene {CAAT.Scene} an CAAT.Scene object.
*/
addScene : function(scene) {
scene.setBounds(0, 0, this.width, this.height);
this.scenes.push(scene);
scene.setEaseListener(this);
if (null === this.currentScene) {
this.setScene(0);
}
},
/**
* Get the number of scenes contained in the Director.
* @return {number} the number of scenes contained in the Director.
*/
getNumScenes : function() {
return this.scenes.length;
},
/**
* This method offers full control over the process of switching between any given two Scenes.
* To apply this method, you must specify the type of transition to apply for each Scene and
* the anchor to keep the Scene pinned at.
*
*
*
*
*
*
*
*
*
* imagesCache
* where you can store a JSON of the form
* [ { id: imageId, image: imageObject } ]
.
* This structure will be used as a resources cache.
* There's a CAAT.ImagePreloader class to preload resources and
* generate this structure on loading finalization.
*
* @param sId {object} an String identifying a resource.
*/
getImage : function(sId) {
var ret = this.imagesCache[sId];
if (ret) {
return ret;
}
for (var i = 0; i < this.imagesCache.length; i++) {
if (this.imagesCache[i].id === sId) {
return this.imagesCache[i].image;
}
}
return null;
},
/**
* Adds an audio to the cache.
*
* @see CAAT.AudioManager.addAudio
* @return this
*/
addAudio : function(id, url) {
this.audioManager.addAudio(id, url);
return this;
},
/**
* Plays the audio instance identified by the id.
* @param id {object} the object used to store a sound in the audioCache.
*/
audioPlay : function(id) {
this.audioManager.play(id);
},
/**
* Loops an audio instance identified by the id.
* @param id {object} the object used to store a sound in the audioCache.
*
* @return {HTMLElement|null} the value from audioManager.loop
*/
audioLoop : function(id) {
return this.audioManager.loop(id);
},
endSound : function() {
return this.audioManager.endSound();
},
setSoundEffectsEnabled : function(enabled) {
return this.audioManager.setSoundEffectsEnabled(enabled);
},
setMusicEnabled : function(enabled) {
return this.audioManager.setMusicEnabled(enabled);
},
isMusicEnabled : function() {
return this.audioManager.isMusicEnabled();
},
isSoundEffectsEnabled : function() {
return this.audioManager.isSoundEffectsEnabled();
},
setVolume : function( id, volume ) {
return this.audioManager.setVolume( id, volume );
},
/**
* Removes Director's scenes.
*/
emptyScenes : function() {
this.scenes = [];
},
/**
* Adds an scene to this Director.
* @param scene {CAAT.Scene} a scene object.
*/
addChild : function(scene) {
scene.parent = this;
this.childrenList.push(scene);
},
/**
* @Deprecated use CAAT.loop instead.
* @param fps
* @param callback
* @param callback2
*/
loop : function(fps,callback,callback2) {
if ( callback2 ) {
this.onRenderStart= callback;
this.onRenderEnd= callback2;
} else if (callback) {
this.onRenderEnd= callback;
}
CAAT.loop();
},
/**
* Starts the director animation.If no scene is explicitly selected, the current Scene will
* be the first scene added to the Director.
*
*
*
* CAAT.SpriteActor
instances.
*
* @constructor
*
*/
CAAT.SpriteImage = function() {
this.paint= this.paintN;
this.setAnimationImageIndex([0]);
this.mapInfo= {};
return this;
};
CAAT.SpriteImage.prototype = {
animationImageIndex: null, // an Array defining the sprite frame sequence
prevAnimationTime: -1,
changeFPS: 1000, // how much Scene time to take before changing an Sprite frame.
transformation: 0, // any of the TR_* constants.
spriteIndex: 0, // the current sprite frame
TR_NONE: 0, // constants used to determine how to draw the sprite image,
TR_FLIP_HORIZONTAL: 1,
TR_FLIP_VERTICAL: 2,
TR_FLIP_ALL: 3,
TR_FIXED_TO_SIZE: 4,
TR_TILE: 5,
image: null,
rows: 1,
columns: 1,
width: 0,
height: 0,
singleWidth: 0,
singleHeight: 0,
scaleX: 1,
scaleY: 1,
offsetX: 0,
offsetY: 0,
ownerActor: null,
mapInfo : null,
map : null,
setOwner : function(actor) {
this.ownerActor= actor;
return this;
},
getRows: function() {
return this.rows;
},
getColumns : function() {
return this.columns;
},
getWidth : function() {
var el= this.mapInfo[this.spriteIndex];
return el.width;
},
getHeight : function() {
var el= this.mapInfo[this.spriteIndex];
return el.height;
},
/**
* Get a reference to the same image information (rows, columns, image and uv cache) of this
* SpriteImage. This means that re-initializing this objects image info (that is, calling initialize
* method) will change all reference's image information at the same time.
*/
getRef : function() {
var ret= new CAAT.SpriteImage();
ret.image= this.image;
ret.rows= this.rows;
ret.columns= this.columns;
ret.width= this.width;
ret.height= this.height;
ret.singleWidth= this.singleWidth;
ret.singleHeight= this.singleHeight;
ret.mapInfo= this.mapInfo;
ret.offsetX= this.offsetX;
ret.offsetY= this.offsetY;
ret.scaleX= this.scaleX;
ret.scaleY= this.scaleY;
return ret;
},
/**
* Set horizontal displacement to draw image. Positive values means drawing the image more to the
* right.
* @param x {number}
* @return this
*/
setOffsetX : function(x) {
this.offsetX= x;
return this;
},
/**
* Set vertical displacement to draw image. Positive values means drawing the image more to the
* bottom.
* @param y {number}
* @return this
*/
setOffsetY : function(y) {
this.offsetY= y;
return this;
},
setOffset : function( x,y ) {
this.offsetX= x;
this.offsetY= y;
return this;
},
/**
* Initialize a grid of subimages out of a given image.
* @param image {HTMLImageElement|Image} an image object.
* @param rows {number} number of rows.
* @param columns {number} number of columns
*
* @return this
*/
initialize : function(image, rows, columns) {
this.image = image;
this.rows = rows;
this.columns = columns;
this.width = image.width;
this.height = image.height;
this.singleWidth = Math.floor(this.width / columns);
this.singleHeight = Math.floor(this.height / rows);
this.mapInfo= {};
var i,sx0,sy0;
var helper;
if (image.__texturePage) {
image.__du = this.singleWidth / image.__texturePage.width;
image.__dv = this.singleHeight / image.__texturePage.height;
var w = this.singleWidth;
var h = this.singleHeight;
var mod = this.columns;
if (image.inverted) {
var t = w;
w = h;
h = t;
mod = this.rows;
}
var xt = this.image.__tx;
var yt = this.image.__ty;
var tp = this.image.__texturePage;
for (i = 0; i < rows * columns; i++) {
var c = ((i % mod) >> 0);
var r = ((i / mod) >> 0);
var u = xt + c * w; // esquina izq x
var v = yt + r * h;
var u1 = u + w;
var v1 = v + h;
helper= new CAAT.SpriteImageHelper(u,v,(u1-u),(v1-v),tp.width,tp.height).setGL(
u / tp.width,
v / tp.height,
u1 / tp.width,
v1 / tp.height );
this.mapInfo[i]= helper;
}
} else {
for (i = 0; i < rows * columns; i++) {
sx0 = ((i % this.columns) | 0) * this.singleWidth;
sy0 = ((i / this.columns) | 0) * this.singleHeight;
helper= new CAAT.SpriteImageHelper( sx0, sy0, this.singleWidth, this.singleHeight, image.width, image.height );
this.mapInfo[i]= helper;
}
}
return this;
},
/**
* Must be used to draw actor background and the actor should have setClip(true) so that the image tiles
* properly.
* @param director
* @param time
* @param x
* @param y
*/
paintTiled : function( director, time, x, y ) {
this.setSpriteIndexAtTime(time);
var el= this.mapInfo[this.spriteIndex];
var r= new CAAT.Rectangle();
this.ownerActor.AABB.intersect( director.AABB, r );
var w= this.getWidth();
var h= this.getHeight();
var xoff= (this.offsetX-this.ownerActor.x) % w;
if ( xoff> 0 ) {
xoff= xoff-w;
}
var yoff= (this.offsetY-this.ownerActor.y) % h;
if ( yoff> 0 ) {
yoff= yoff-h;
}
var nw= (((r.width-xoff)/w)>>0)+1;
var nh= (((r.height-yoff)/h)>>0)+1;
var i,j;
var ctx= director.ctx;
for( i=0; i
*
*
* behaviorExpired( caat_behaviour, time, actor);
*/
setEaseListener : function( listener ) {
this.easeContainerBehaviourListener=listener;
},
/**
* Private.
* listener for the Scene's easeContainerBehaviour.
* @param actor
*/
behaviorExpired : function(actor) {
this.easeContainerBehaviourListener.easeEnd(this, this.easeIn);
},
/**
* This method should be overriden in case the developer wants to do some special actions when
* the scene has just been brought in.
*/
activated : function() {
},
/**
* Scenes, do not expire the same way Actors do.
* It simply will be set expired=true, but the frameTime won't be modified.
*/
setExpired : function(bExpired) {
this.expired= bExpired;
},
/**
* An scene by default does not paint anything because has not fillStyle set.
* @param director
* @param time
*/
paint : function(director, time) {
if ( this.fillStyle ) {
var ctx= director.crc;
ctx.fillStyle= this.fillStyle;
ctx.fillRect(0,0,this.width,this.height );
}
},
/**
* Find a pointed actor at position point.
* This method tries lo find the correctly pointed actor in two different ways.
* + first of all, if inputList is defined, it will look for an actor in it.
* + if no inputList is defined, it will traverse the scene graph trying to find a pointed actor.
* @param point
*
*
* @constructor
*/
CAAT.PathSegment = function() {
this.bbox= new CAAT.Rectangle();
return this;
};
CAAT.PathSegment.prototype = {
color: '#000',
length: 0,
bbox: null,
parent: null,
/**
* Set a PathSegment's parent
* @param parent
*/
setParent : function(parent) {
this.parent= parent;
return this;
},
setColor : function(color) {
if ( color ) {
this.color= color;
}
return this;
},
/**
* Get path's last coordinate.
* @return {CAAT.Point}
*/
endCurvePosition : function() { },
/**
* Get path's starting coordinate.
* @return {CAAT.Point}
*/
startCurvePosition : function() { },
/**
* Set this path segment's points information.
* @param points {ArraystartCurvePosition
and one will be endCurvePosition
. Other values
* will be a position on the path relative to the path length. if the value is greater that 1, if will be set
* to modulus 1.
* @param time a float with a value between zero and 1 inclusive both.
*
* @return {CAAT.Point}
*/
getPosition : function(time) { },
/**
* Gets Path length.
* @return {number}
*/
getLength : function() {
return this.length;
},
/**
* Gets the path bounding box (or the rectangle that contains the whole path).
* @param rectangle a CAAT.Rectangle instance with the bounding box.
* @return {CAAT.Rectangle}
*/
getBoundingBox : function() {
return this.bbox;
},
/**
* Gets the number of control points needed to create the path.
* Each PathSegment type can have different control points.
* @return {number} an integer with the number of control points.
*/
numControlPoints : function() { },
/**
* Gets CAAT.Point instance with the 2d position of a control point.
* @param index an integer indicating the desired control point coordinate.
* @return {CAAT.Point}
*/
getControlPoint: function(index) { },
/**
* Instruments the path has finished building, and that no more segments will be added to it.
* You could later add more PathSegments and endPath
must be called again.
*/
endPath : function() {},
/**
* Gets a polyline describing the path contour. The contour will be defined by as mush as iSize segments.
* @param iSize an integer indicating the number of segments of the contour polyline.
*
* @return {[CAAT.Point]}
*/
getContour : function(iSize) {},
/**
* Recalculate internal path structures.
*/
updatePath : function(point) {},
/**
* Draw this path using RenderingContext2D drawing primitives.
* The intention is to set a path or pathsegment as a clipping region.
*
* @param ctx {RenderingContext2D}
*/
applyAsPath : function(director) {},
/**
* Transform this path with the given affinetransform matrix.
* @param matrix
*/
transform : function(matrix) {},
drawHandle : function( ctx, x, y ) {
var w= CAAT.Curve.prototype.HANDLE_SIZE/2;
ctx.fillRect( x-w, y-w, w*2, w*2 );
/*
ctx.arc(
this.points[0].x,
this.points[0].y,
CAAT.Curve.prototype.HANDLE_SIZE/2,
0,
2*Math.PI,
false) ;
*/
}
};
})();
(function() {
/**
* Straight line segment path between two given points.
*
* @constructor
* @extends CAAT.PathSegment
*/
CAAT.LinearPath = function() {
CAAT.LinearPath.superclass.constructor.call(this);
this.points= [];
this.points.push( new CAAT.Point() );
this.points.push( new CAAT.Point() );
this.newPosition= new CAAT.Point(0,0,0);
return this;
};
CAAT.LinearPath.prototype= {
points: null,
newPosition: null, // spare holder for getPosition coordinate return.
applyAsPath : function(director) {
director.ctx.lineTo( this.points[0].x, this.points[1].y );
},
setPoint : function( point, index ) {
if ( index===0 ) {
this.points[0]= point;
} else if ( index===1 ) {
this.points[1]= point;
}
},
/**
* Update this segments length and bounding box info.
*/
updatePath : function(point) {
var x= this.points[1].x - this.points[0].x;
var y= this.points[1].y - this.points[0].y;
this.length= Math.sqrt( x*x+y*y );
this.bbox.setEmpty();
this.bbox.union( this.points[0].x, this.points[0].y );
this.bbox.union( this.points[1].x, this.points[1].y );
return this;
},
setPoints : function( points ) {
this.points[0]= points[0];
this.points[1]= points[1];
this.updatePath();
return this;
},
/**
* Set this path segment's starting position.
* @param x {number}
* @param y {number}
*/
setInitialPosition : function( x, y ) {
this.points[0].x= x;
this.points[0].y= y;
this.newPosition.set(x,y);
return this;
},
/**
* Set this path segment's ending position.
* @param finalX {number}
* @param finalY {number}
*/
setFinalPosition : function( finalX, finalY ) {
this.points[1].x= finalX;
this.points[1].y= finalY;
return this;
},
/**
* @inheritDoc
*/
endCurvePosition : function() {
return this.points[1];
},
/**
* @inheritsDoc
*/
startCurvePosition : function() {
return this.points[0];
},
/**
* @inheritsDoc
*/
getPosition : function(time) {
if ( time>1 || time<0 ) {
time%=1;
}
if ( time<0 ) {
time= 1+time;
}
this.newPosition.set(
(this.points[0].x+(this.points[1].x-this.points[0].x)*time),
(this.points[0].y+(this.points[1].y-this.points[0].y)*time) );
return this.newPosition;
},
getPositionFromLength : function( len ) {
return this.getPosition( len/this.length );
},
/**
* Returns initial path segment point's x coordinate.
* @return {number}
*/
initialPositionX : function() {
return this.points[0].x;
},
/**
* Returns final path segment point's x coordinate.
* @return {number}
*/
finalPositionX : function() {
return this.points[1].x;
},
/**
* Draws this path segment on screen. Optionally it can draw handles for every control point, in
* this case, start and ending path segment points.
* @param director {CAAT.Director}
* @param bDrawHandles {boolean}
*/
paint : function(director, bDrawHandles) {
var ctx= director.ctx;
ctx.save();
ctx.strokeStyle= this.color;
ctx.beginPath();
ctx.moveTo( this.points[0].x, this.points[0].y );
ctx.lineTo( this.points[1].x, this.points[1].y );
ctx.stroke();
if ( bDrawHandles ) {
ctx.globalAlpha=0.5;
ctx.fillStyle='#7f7f00';
ctx.beginPath();
this.drawHandle( ctx, this.points[0].x, this.points[0].y );
this.drawHandle( ctx, this.points[1].x, this.points[1].y );
/*
canvas.arc(
this.points[0].x,
this.points[0].y,
CAAT.Curve.prototype.HANDLE_SIZE/2,
0,
2*Math.PI,
false) ;
canvas.arc(
this.points[1].x,
this.points[1].y,
CAAT.Curve.prototype.HANDLE_SIZE/2,
0,
2*Math.PI,
false) ;
canvas.fill();
*/
}
ctx.restore();
},
/**
* Get the number of control points. For this type of path segment, start and
* ending path segment points. Defaults to 2.
* @return {number}
*/
numControlPoints : function() {
return 2;
},
/**
* @inheritsDoc
*/
getControlPoint: function(index) {
if ( 0===index ) {
return this.points[0];
} else if (1===index) {
return this.points[1];
}
},
/**
* @inheritsDoc
*/
getContour : function(iSize) {
var contour= [];
contour.push( this.getPosition(0).clone() );
contour.push( this.getPosition(1).clone() );
return contour;
}
};
extend( CAAT.LinearPath, CAAT.PathSegment );
})();
(function() {
/**
* This class defines a Bezier cubic or quadric path segment.
*
* @constructor
* @extends CAAT.PathSegment
*/
CAAT.CurvePath = function() {
CAAT.CurvePath.superclass.constructor.call(this);
this.newPosition= new CAAT.Point(0,0,0);
return this;
};
CAAT.CurvePath.prototype= {
curve: null, // a CAAT.Bezier instance.
newPosition: null, // spare holder for getPosition coordinate return.
applyAsPath : function(director) {
this.curve.applyAsPath(director);
return this;
},
setPoint : function( point, index ) {
if ( this.curve ) {
this.curve.setPoint(point,index);
}
},
/**
* Set this curve segment's points.
* @param points {ArraygetLength
will contain the sum of every path segment's length.
*
* path.beginPath(x,y).
*
* addLineTo(x1,y1).
* addLineTo(x2,y2).
* addQuadricTo(...).
* addCubicTo(...).
* endPath();
* setLinear, setCubic and setQuadrid
will make
* a CAAT.Path instance to be defined by just one segment.
*
* @constructor
* @extends CAAT.PathSegment
*/
CAAT.Path= function() {
CAAT.Path.superclass.constructor.call(this);
this.newPosition= new CAAT.Point(0,0,0);
this.pathSegments= [];
this.behaviorList= [];
this.matrix= new CAAT.Matrix();
this.tmpMatrix= new CAAT.Matrix();
return this;
};
CAAT.Path.prototype= {
pathSegments: null, // a collection of CAAT.PathSegment instances.
pathSegmentDurationTime: null, // precomputed segment duration relative to segment legnth/path length
pathSegmentStartTime: null, // precomputed segment start time relative to segment legnth/path length and duration.
newPosition: null, // spare CAAT.Point.
pathLength: -1, // path length (sum of every segment length)
/*
starting path position
*/
beginPathX: -1,
beginPathY: -1,
/*
last path coordinates position (using when building the path).
*/
trackPathX: -1,
trackPathY: -1,
/*
needed to drag control points.
*/
ax: -1,
ay: -1,
point: [],
interactive: true,
behaviorList: null,
/** rotation behavior info **/
rb_angle: 0,
rb_rotateAnchorX: .5,
rb_rotateAnchorY: .5,
/** scale behavior info **/
sb_scaleX: 1,
sb_scaleY: 1,
sb_scaleAnchorX: .5,
sb_scaleAnchorY: .5,
tAnchorX: 0,
tAnchorY: 0,
/** translate behavior info **/
tb_x: 0,
tb_y: 0,
/** behavior affine transformation matrix **/
matrix: null,
tmpMatrix: null,
/** if behaviors are to be applied, save original path points **/
pathPoints: null,
/** path width and height **/
width: 0,
height: 0,
clipOffsetX : 0,
clipOffsetY : 0,
applyAsPath : function(director) {
var ctx= director.ctx;
director.modelViewMatrix.transformRenderingContext( ctx );
ctx.beginPath();
ctx.globalCompositeOperation= 'source-out';
ctx.moveTo(
this.getFirstPathSegment().startCurvePosition().x,
this.getFirstPathSegment().startCurvePosition().y
);
for( var i=0; iadd
is called before this calling
* this method, they will assume to start at -1,-1 and probably you'll get the wrong path.
* @param px0 {number}
* @param py0 {number}
*
* @return this
*/
beginPath : function( px0, py0 ) {
this.trackPathX= px0;
this.trackPathY= py0;
this.beginPathX= px0;
this.beginPathY= py0;
return this;
},
/**
* Close the path by adding a line path segment from the current last path
* coordinate to startCurvePosition coordinate.
*