/* The MIT License Copyright (c) 2010-2011-2012 Ibon Tolosana [@hyperandroid] 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: 421 Created on: DATE: 2012-09-30 TIME: 17:40:37 */ /** * See LICENSE file. * * Library namespace. * CAAT stands for: Canvas Advanced Animation Tookit. */ /** * @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 CAATObject = function () { }; CAATObject.prototype = superc.prototype; subc.prototype = new CAATObject(); // 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 ( root, obj, path, nns, ns ) { var newC = function () { console.log( "Creating object of type proxy[" + ns + "]" ); var obj = new root[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 = root[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; })( window, 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 *
* H runs from 0 to 360 degrees
* S and V run from 0 to 100
*
* 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, i, 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 ) {
var r, g, b;
if ( step <= 0 ) {
return {
r:r0,
g:g0,
b:b0
};
}
else if ( step >= nsteps ) {
return {
r:r1,
g:g1,
b:b1
};
}
r = (r0 + (r1 - r0) / nsteps * step) >> 0;
g = (g0 + (g1 - g0) / nsteps * step) >> 0;
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 = [], nc = fromColorsArray.length - 1, chunk = rampSize / nc, i, j,
na, nr, ng, nb,
c, a0, r0, g0, b0,
c1, a1, r1, g1, b1,
da, dr, dg, db;
for ( i = 0; i < nc; i += 1 ) {
c = fromColorsArray[i];
a0 = (c >> 24) & 0xff;
r0 = (c & 0xff0000) >> 16;
g0 = (c & 0xff00) >> 8;
b0 = c & 0xff;
c1 = fromColorsArray[i + 1];
a1 = (c1 >> 24) & 0xff;
r1 = (c1 & 0xff0000) >> 16;
g1 = (c1 & 0xff00) >> 8;
b1 = c1 & 0xff;
da = (a1 - a0) / chunk;
dr = (r1 - r0) / chunk;
dg = (g1 - g0) / chunk;
db = (b1 - b0) / chunk;
for ( j = 0; j < chunk; j += 1 ) {
na = (a0 + da * j) >> 0;
nr = (r0 + dr * j) >> 0;
ng = (g0 + dg * j) >> 0;
nb = (b0 + db * j) >> 0;
switch( returnType ) {
case this.RampEnumeration.RAMP_RGBA:
ramp.push( 'argb(' + na + ',' + nr + ',' + ng + ',' + nb + ')' );
break;
case this.RampEnumeration.RAMP_RGB:
ramp.push( 'rgb(' + nr + ',' + ng + ',' + nb + ')' );
break;
case this.RampEnumeration.RAMP_CHANNEL_RGB:
ramp.push( 0xff000000 | nr << 16 | ng << 8 | nb );
break;
case this.RampEnumeration.RAMP_CHANNEL_RGBA:
ramp.push( na << 24 | nr << 16 | ng << 8 | nb );
break;
case this.RampEnumeration.RAMP_CHANNEL_RGBA_ARRAY:
ramp.push( [ nr, ng, nb, na ] );
break;
case this.RampEnumeration.RAMP_CHANNEL_RGB_ARRAY:
ramp.push( [ nr, ng, nb ] );
break;
}
}
}
return ramp;
}
};
})();
(function () {
/**
* RGB color implementation
* @param r {number} an integer in the range 0..255
* @param g {number} an integer in the range 0..255
* @param b {number} an integer in the range 0..255
*
* @constructor
*/
CAAT.Color.RGB = function ( r, g, b ) {
this.r = r || 255;
this.g = g || 255;
this.b = b || 255;
return this;
};
CAAT.Color.RGB.random = function () {
var a = '0123456789abcdef';
var c = '#';
for ( var i = 0; i < 3; i++ ) {
c += a[ (Math.random() * a.length) >> 0 ];
}
return c;
};
CAAT.Color.RGB.prototype = {
r:255,
g:255,
b:255,
/**
* Get color hexadecimal representation.
* @return {string} a string with color hexadecimal representation.
*/
toHex:function () {
// See: http://jsperf.com/rgb-decimal-to-hex/5
return ('000000' + ((this.r << 16) + (this.g << 8) + this.b).toString( 16 )).slice( -6 );
}
};
}());
/**
* See LICENSE file.
*
* Rectangle Class.
* Needed to compute Curve bounding box.
* Needed to compute Actor affected area on change.
*
**/
(function () {
/**
* A Rectangle implementation, which defines an area positioned somewhere.
*
* @constructor
*/
CAAT.Rectangle = function () {
return this;
};
CAAT.Rectangle.prototype = {
x:0,
y:0,
x1:0,
y1:0,
width:-1,
height:-1,
setEmpty:function () {
this.width = -1;
this.height = -1;
this.x = 0;
this.y = 0;
this.x1 = 0;
this.y1 = 0;
return this;
},
/**
* Set this rectangle's location.
* @param x {number}
* @param y {number}
*/
setLocation:function ( x, y ) {
this.x = x;
this.y = y;
this.x1 = this.x + this.width;
this.y1 = this.y + this.height;
return this;
},
/**
* Set this rectangle's dimension.
* @param w {number}
* @param h {number}
*/
setDimension:function ( w, h ) {
this.width = w;
this.height = h;
this.x1 = this.x + this.width;
this.y1 = this.y + this.height;
return this;
},
setBounds:function ( x, y, w, h ) {
this.setLocation( x, y )
this.setDimension( w, h );
return this;
},
/**
* Return whether the coordinate is inside this rectangle.
* @param px {number}
* @param py {number}
*
* @return {boolean}
*/
contains:function ( px, py ) {
//return px>=0 && px
* 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 < this.behaviors.length; i++ ) {
bh = this.behaviors[i];
bh.setFrameTime( bh.getStartTime() * f, bh.getDuration() * f );
}
return this;
},
/**
* Adds a new behavior to the container.
* @param behavior
*
* @override
*/
addBehavior:function ( behavior ) {
this.behaviors.push( behavior );
behavior.addListener( this );
return this;
},
/**
* Applies every contained Behaviors.
* The application time the contained behaviors will receive will be ContainerBehavior related and not the
* received time.
* @param time an integer indicating the time to apply the contained behaviors at.
* @param actor a CAAT.Actor instance indicating the actor to apply the behaviors for.
*/
apply:function ( time, actor ) {
if ( !this.solved ) {
this.behaviorStartTime += time;
this.solved = true;
}
time += this.timeOffset * this.behaviorDuration;
if ( this.isBehaviorInTime( time, actor ) ) {
time -= this.getStartTime();
if ( this.cycleBehavior ) {
time %= this.getDuration();
}
var bh = this.behaviors;
for ( var i = 0; i < bh.length; i++ ) {
bh[i].apply( time, actor );
}
}
},
/**
* This method is the observer implementation for every contained behavior.
* If a container is Cycle=true, won't allow its contained behaviors to be expired.
* @param behavior a CAAT.Behavior instance which has been expired.
* @param time an integer indicating the time at which has become expired.
* @param actor a CAAT.Actor the expired behavior is being applied to.
*/
behaviorExpired:function ( behavior, time, actor ) {
if ( this.cycleBehavior ) {
behavior.setStatus( CAAT.Behavior.Status.STARTED );
}
},
/**
* Implementation method of the behavior.
* Just call implementation method for its contained behaviors.
* @param time an intenger indicating the time the behavior is being applied at.
* @param actor a CAAT.Actor the behavior is being applied to.
*/
setForTime:function ( time, actor ) {
var bh = this.behaviors;
for ( var i = 0; i < bh.length; i++ ) {
bh[i].setForTime( time, actor );
}
return null;
},
setExpired:function ( actor, time ) {
CAAT.ContainerBehavior.superclass.setExpired.call( this, actor, time );
var bh = this.behaviors;
// set for final interpolator value.
for ( var i = 0; i < bh.length; i++ ) {
var bb = bh[i];
if ( /*!bb.expired*/ bb.status !== CAAT.Behavior.Status.EXPIRED ) {
bb.setExpired( actor, time - this.behaviorStartTime );
}
}
// already notified in base class.
// this.fireBehaviorExpiredEvent(actor,time);
return this;
},
setFrameTime:function ( start, duration ) {
CAAT.ContainerBehavior.superclass.setFrameTime.call( this, start, duration );
var bh = this.behaviors;
for ( var i = 0; i < bh.length; i++ ) {
bh[i].setStatus( CAAT.Behavior.Status.NOT_STARTED );
}
return this;
},
setDelayTime:function ( start, duration ) {
CAAT.ContainerBehavior.superclass.setDelayTime.call( this, start, duration );
var bh = this.behaviors;
for ( var i = 0; i < bh.length; i++ ) {
bh[i].setStatus( CAAT.Behavior.Status.NOT_STARTED );
}
return this;
},
calculateKeyFrameData:function ( referenceTime, prefix, prevValues ) {
var i;
var bh;
var retValue = {};
var time;
var cssRuleValue;
var cssProperty;
var property;
for ( i = 0; i < this.behaviors.length; i++ ) {
bh = this.behaviors[i];
if ( /*!bh.expired*/ bh.status !== CAAT.Behavior.Status.EXPIRED && !(bh instanceof CAAT.GenericBehavior) ) {
// ajustar tiempos:
// time es tiempo normalizado a duracion de comportamiento contenedor.
// 1.- desnormalizar
time = referenceTime * this.behaviorDuration;
// 2.- calcular tiempo relativo de comportamiento respecto a contenedor
if ( bh.behaviorStartTime <= time && bh.behaviorStartTime + bh.behaviorDuration >= time ) {
// 3.- renormalizar tiempo reltivo a comportamiento.
time = (time - bh.behaviorStartTime) / bh.behaviorDuration;
// 4.- obtener valor de comportamiento para tiempo normalizado relativo a contenedor
cssRuleValue = bh.calculateKeyFrameData( time );
cssProperty = bh.getPropertyName( prefix );
if ( typeof retValue[cssProperty] === 'undefined' ) {
retValue[cssProperty] = "";
}
// 5.- asignar a objeto, par de propiedad/valor css
retValue[cssProperty] += cssRuleValue + " ";
}
}
}
var tr = "";
var pv;
function xx( pr ) {
if ( retValue[pr] ) {
tr += retValue[pr];
}
else {
if ( prevValues ) {
pv = prevValues[pr];
if ( pv ) {
tr += pv;
retValue[pr] = pv;
}
}
}
}
xx( 'translate' );
xx( 'rotate' );
xx( 'scale' );
var keyFrameRule = "";
if ( tr ) {
keyFrameRule = '-' + prefix + '-transform: ' + tr + ';';
}
tr = "";
xx( 'opacity' );
if ( tr ) {
keyFrameRule += ' opacity: ' + tr + ';';
}
return {
rules:keyFrameRule,
ret:retValue
};
},
/**
*
* @param prefix
* @param name
* @param keyframessize
*/
calculateKeyFramesData:function ( prefix, name, keyframessize ) {
if ( this.duration === Number.MAX_VALUE ) {
return "";
}
if ( typeof keyframessize === 'undefined' ) {
keyframessize = 100;
}
var i;
var prevValues = null;
var kfd = "@-" + prefix + "-keyframes " + name + " {";
var ret;
var time;
var kfr;
for ( i = 0; i <= keyframessize; i++ ) {
time = this.interpolator.getPosition( i / keyframessize ).y;
ret = this.calculateKeyFrameData( time, prefix, prevValues );
kfr = "" +
(i / keyframessize * 100) + "%" + // percentage
"{" + ret.rules + "}\n";
prevValues = ret.ret;
kfd += kfr;
}
kfd += "}";
return kfd;
}
};
extend( CAAT.ContainerBehavior, CAAT.Behavior, null );
})();
(function () {
/**
* This class applies a rotation to a CAAt.Actor instance.
* StartAngle, EndAngle must be supplied. Angles are in radians.
* The RotationAnchor, if not supplied, will be ANCHOR_CENTER.
*
* An example os use will be
*
* var rb= new CAAT.RotateBehavior().
* setValues(0,2*Math.PI).
* setFrameTime(0,2500);
*
* @see CAAT.Actor.
*
* @constructor
* @extends CAAT.Behavior
*
*/
CAAT.RotateBehavior = function () {
CAAT.RotateBehavior.superclass.constructor.call( this );
this.anchor = CAAT.Actor.prototype.ANCHOR_CENTER;
return this;
};
CAAT.RotateBehavior.prototype = {
startAngle:0, // behavior start angle
endAngle:0, // behavior end angle
anchorX:.50, // rotation center x.
anchorY:.50, // rotation center y.
getPropertyName:function () {
return "rotate";
},
/**
* Behavior application function.
* Do not call directly.
* @param time an integer indicating the application time.
* @param actor a CAAT.Actor the behavior will be applied to.
* @return the set angle.
*/
setForTime:function ( time, actor ) {
var angle = this.startAngle + time * (this.endAngle - this.startAngle);
if ( this.doValueApplication ) {
actor.setRotationAnchored( angle, this.anchorX, this.anchorY );
}
return angle;
},
/**
* Set behavior bound values.
* if no anchorx,anchory values are supplied, the behavior will assume
* 50% for both values, that is, the actor's center.
*
* Be aware the anchor values are supplied in RELATIVE PERCENT to
* actor's size.
*
* @param startAngle {float} indicating the starting angle.
* @param endAngle {float} indicating the ending angle.
* @param anchorx {float} the percent position for anchorX
* @param anchory {float} the percent position for anchorY
*/
setValues:function ( startAngle, endAngle, anchorx, anchory ) {
this.startAngle = startAngle;
this.endAngle = endAngle;
if ( typeof anchorx !== 'undefined' && typeof anchory !== 'undefined' ) {
this.anchorX = anchorx;
this.anchorY = anchory;
}
return this;
},
/**
* @deprecated
* Use setValues instead
* @param start
* @param end
*/
setAngles:function ( start, end ) {
return this.setValues( start, end );
},
/**
* Set the behavior rotation anchor. Use this method when setting an exact percent
* by calling setValues is complicated.
* @see CAAT.Actor
* @param anchor any of CAAT.Actor.prototype.ANCHOR_* constants.
*
* These parameters are to set a custom rotation anchor point. if
* 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().
* WARNING: every call to this method calculates
* actor's world model view matrix.
*
* @param point {CAAT.Point} a point in screen space to be transformed to model space.
*
* @return the source point object
*
*
*/
viewToModel:function ( point ) {
if ( this.dirty ) {
this.setModelViewMatrix();
}
this.worldModelViewMatrixI = this.worldModelViewMatrix.getInverse();
this.worldModelViewMatrixI.transformCoord( point );
return point;
},
/**
* Private
* This method does the needed point transformations across an Actor hierarchy to devise
* whether the parameter point coordinate lies inside the Actor.
* @param point {CAAT.Point}
*
* @return null if the point is not inside the Actor. The Actor otherwise.
*/
findActorAtPosition:function ( point ) {
if ( !this.visible || !this.mouseEnabled || !this.isInAnimationFrame( this.time ) ) {
return null;
}
this.modelViewMatrixI = this.modelViewMatrix.getInverse();
this.modelViewMatrixI.transformCoord( point );
return this.contains( point.x, point.y ) ? this : null;
},
/**
* Enables a default dragging routine for the Actor.
* This default dragging routine allows to:
*
* 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.CLASS = CAAT.ActorContainer;
this.childrenList = [];
this.activeChildren = [];
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.
layoutManager:null, // a layout manager instance.
layoutInvalidated:true,
setLayout:function ( layout ) {
this.layoutManager = layout;
return this;
},
setBounds:function ( x, y, w, h ) {
CAAT.ActorContainer.superclass.setBounds.call( this, x, y, w, h );
if ( CAAT.currentDirector && !CAAT.currentDirector.inValidation ) {
this.invalidateLayout();
}
return this;
},
__validateLayout:function () {
this.__validateTree();
this.layoutInvalidated = false;
},
__validateTree:function () {
if ( this.layoutManager && this.layoutManager.isInvalidated() ) {
CAAT.currentDirector.inValidation = true;
this.layoutManager.doLayout( this );
for ( var i = 0; i < this.getNumChildren(); i += 1 ) {
this.getChildAt( i ).__validateLayout();
}
}
},
invalidateLayout:function () {
this.layoutInvalidated = true;
if ( this.layoutManager ) {
this.layoutManager.invalidateLayout( this );
for ( var i = 0; i < this.getNumChildren(); i += 1 ) {
this.getChildAt( i ).invalidateLayout();
}
}
},
getLayout:function () {
return this.layoutManager;
},
/**
* 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 < cl.length; i++ ) {
cl[i].drawScreenBoundingBox( director, time );
}
sc_drawScreenBoundingBox.call( this, director, time );
},
/**
* Removes all children from this ActorContainer.
*
* @return this
*/
emptyChildren:function () {
this.childrenList = [];
return this;
},
/**
* Private
* Paints this container and every contained children.
*
* @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.
*/
paintActor:function ( director, time ) {
if ( !this.visible ) {
return false;
}
var ctx = director.ctx;
ctx.save();
if ( !sc_paintActor.call( this, director, time ) ) {
return false;
}
if ( this.cached === __CD ) {
return false;
}
if ( !this.isGlobalAlpha ) {
this.frameAlpha = this.parent ? this.parent.frameAlpha : 1;
}
for ( var i = 0, l = this.activeChildren.length; i < l; ++i ) {
var actor = this.activeChildren[i];
if ( actor.visible ) {
ctx.save();
actor.paintActor( director, time );
ctx.restore();
}
}
ctx.restore();
return true;
},
__paintActor:function ( director, time ) {
if ( !this.visible ) {
return true;
}
var ctx = director.ctx;
this.frameAlpha = this.parent ? this.parent.frameAlpha * this.alpha : 1;
var m = this.worldModelViewMatrix.matrix;
ctx.setTransform( m[0], m[3], m[1], m[4], m[2], m[5], this.frameAlpha );
this.paint( director, time );
if ( !this.isGlobalAlpha ) {
this.frameAlpha = this.parent ? this.parent.frameAlpha : 1;
}
for ( var i = 0, l = this.activeChildren.length; i < l; ++i ) {
var actor = this.activeChildren[i];
actor.paintActor( director, time );
}
return true;
},
paintActorGL:function ( director, time ) {
var i, l, c;
if ( !this.visible ) {
return true;
}
sc_paintActorGL.call( this, director, time );
if ( !this.isGlobalAlpha ) {
this.frameAlpha = this.parent.frameAlpha;
}
for ( i = 0, l = this.activeChildren.length; i < l; ++i ) {
c = this.activeChildren[i];
c.paintActorGL( director, time );
}
},
/**
* Private.
* Performs the animate method for this ActorContainer and every contained Actor.
*
* @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.
*
* @return {boolean} is this actor in active children list ??
*/
animate:function ( director, time ) {
if ( !this.visible ) {
return false;
}
this.activeChildren = [];
var last = null;
if ( false === sc_animate.call( this, director, time ) ) {
return false;
}
if ( this.cached === __CD ) {
return true;
}
this.__validateLayout();
CAAT.currentDirector.inValidation = false;
var i, l;
/**
* Incluir los actores pendientes.
* El momento es ahora, antes de procesar ninguno del contenedor.
*/
var pcl = this.pendingChildrenList;
for ( i = 0; i < pcl.length; i++ ) {
var child = pcl[i];
this.addChildImmediately( child );
}
this.pendingChildrenList = [];
var markDelete = [];
var cl = this.childrenList;
this.size_active = 1;
this.size_total = 1;
for ( i = 0; i < cl.length; i++ ) {
var actor = cl[i];
actor.time = time;
this.size_total += actor.size_total;
if ( actor.animate( director, time ) ) {
this.activeChildren.push( actor );
this.size_active += actor.size_active;
}
else {
if ( actor.expired && actor.discardable ) {
markDelete.push( actor );
}
}
}
for ( i = 0, l = markDelete.length; i < l; i++ ) {
var md = markDelete[i];
md.destroy( time );
if ( director.dirtyRectsEnabled ) {
director.addDirtyRect( md.AABB );
}
}
return true;
},
/**
* Removes Actors from this ActorContainer which are expired and flagged as Discardable.
*
* @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.
*
* @deprecated
*/
endAnimate:function ( director, time ) {
},
/**
* Adds an Actor to this Container.
* The Actor will be added ON METHOD CALL, despite the rendering pipeline stage being executed at
* the time of method call.
*
* This method is only used by CAAT.Director's transitionScene.
*
* @param child a CAAT.Actor instance.
* @return this.
*/
addChildImmediately:function ( child, constraint ) {
return this.addChild( child );
},
/**
* Adds an Actor to this ActorContainer.
* The Actor will be added to the container AFTER frame animation, and not on method call time.
* Except the Director and in orther to avoid visual artifacts, the developer SHOULD NOT call this
* method directly.
*
* If the container has addingHint as CAAT.ActorContainer.AddHint.CONFORM, new continer size will be
* calculated by summing up the union of every client actor bounding box.
* This method will not take into acount actor's affine transformations, so the bounding box will be
* AABB.
*
* @param child a CAAT.Actor object instance.
* @return this
*/
addChild:function ( child, constraint ) {
if ( child.parent != null ) {
throw('adding to a container an element with parent.');
}
child.parent = this;
this.childrenList.push( child );
child.dirty = true;
if ( this.layoutManager ) {
this.layoutManager.addChild( child, constraint );
this.invalidateLayout();
}
else {
/**
* if Conforming size, recalc new bountainer size.
*/
if ( this.addHint === CAAT.ActorContainer.AddHint.CONFORM ) {
this.recalcSize();
}
}
return this;
},
/**
* Recalc this container size by computing the union of every children bounding box.
*/
recalcSize:function () {
var bb = this.boundingBox;
bb.setEmpty();
var cl = this.childrenList;
var ac;
for ( var i = 0; i < cl.length; i++ ) {
ac = cl[i];
this.runion.setBounds(
ac.x < 0 ? 0 : ac.x,
ac.y < 0 ? 0 : ac.y,
ac.width,
ac.height );
bb.unionRectangle( this.runion );
}
this.setSize( bb.x1, bb.y1 );
return this;
},
/**
* Add a child element and make it active in the next frame.
* @param child {CAAT.Actor}
*/
addChildDelayed:function ( child ) {
this.pendingChildrenList.push( child );
return this;
},
/**
* Adds an Actor to this ActorContainer.
*
* @param child a CAAT.Actor object instance.
*
* @return this
*/
addChildAt:function ( child, index ) {
if ( index <= 0 ) {
child.parent = this;
child.dirty = true;
this.childrenList.splice( 0, 0, child );
this.invalidateLayout();
return this;
}
else {
if ( index >= this.childrenList.length ) {
index = this.childrenList.length;
}
}
child.parent = this;
child.dirty = true;
this.childrenList.splice( index, 0, child );
this.invalidateLayout();
return this;
},
/**
* Find the first actor with the supplied ID.
* This method is not recommended to be used since executes a linear search.
* @param id
*/
findActorById:function ( id ) {
var cl = this.childrenList;
for ( var i = 0, l = cl.length; i < l; i++ ) {
if ( cl[i].id === id ) {
return cl[i];
}
}
return null;
},
/**
* Private
* Gets a contained Actor z-index on this ActorContainer.
*
* @param child a CAAT.Actor object instance.
*
* @return {number}
*/
findChild:function ( child ) {
var cl = this.childrenList;
var i;
var len = cl.length;
for ( i = 0; i < len; i++ ) {
if ( cl[i] === child ) {
return i;
}
}
return -1;
},
removeChildAt:function ( pos ) {
var cl = this.childrenList;
var rm;
if ( -1 !== pos ) {
cl[pos].setParent( null );
rm = cl.splice( pos, 1 );
if ( rm[0].isVisible() && CAAT.currentDirector.dirtyRectsEnabled ) {
CAAT.currentDirector.scheduleDirtyRect( rm[0].AABB );
}
return rm[0];
}
this.invalidateLayout();
return null;
},
/**
* Removed an Actor form this ActorContainer.
* If the Actor is not contained into this Container, nothing happends.
*
* @param child a CAAT.Actor object instance.
*
* @return this
*/
removeChild:function ( child ) {
var pos = this.findChild( child );
var ret = this.removeChildAt( pos );
return ret;
},
removeFirstChild:function () {
var first = this.childrenList.shift();
first.parent = null;
if ( first.isVisible() && CAAT.currentDirector.dirtyRectsEnabled ) {
CAAT.currentDirector.scheduleDirtyRect( first.AABB );
}
this.invalidateLayout();
return first;
},
removeLastChild:function () {
if ( this.childrenList.length ) {
var last = this.childrenList.pop();
last.parent = null;
if ( last.isVisible() && CAAT.currentDirector.dirtyRectsEnabled ) {
CAAT.currentDirector.scheduleDirtyRect( last.AABB );
}
this.invalidateLayout();
return last;
}
return null;
},
/**
* @private
*
* Gets the Actor inside this ActorContainer at a given Screen coordinate.
*
* @param point an object of the form { x: float, y: float }
*
* @return the Actor contained inside this ActorContainer if found, or the ActorContainer itself.
*/
findActorAtPosition:function ( point ) {
if ( null === sc_findActorAtPosition.call( this, point ) ) {
return null;
}
// 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;
},
/**
* Destroys this ActorContainer.
* The process falls down recursively for each contained Actor into this ActorContainer.
*
* @return this
*/
destroy:function () {
var cl = this.childrenList;
for ( var i = cl.length - 1; i >= 0; i-- ) {
cl[i].destroy();
}
sc_destroy.call( this );
return this;
},
/**
* Get number of Actors into this container.
* @return integer indicating the number of children.
*/
getNumChildren:function () {
return this.childrenList.length;
},
getNumActiveChildren:function () {
return this.activeChildren.length;
},
/**
* Returns the Actor at the iPosition(th) position.
* @param iPosition an integer indicating the position array.
* @return the CAAT.Actor object at position.
*/
getChildAt:function ( iPosition ) {
return this.childrenList[ iPosition ];
},
/**
* Changes an actor's ZOrder.
* @param actor the actor to change ZOrder for
* @param index an integer indicating the new ZOrder. a value greater than children list size means to be the
* last ZOrder Actor.
*/
setZOrder:function ( actor, index ) {
var actorPos = this.findChild( actor );
// the actor is present
if ( -1 !== actorPos ) {
var cl = this.childrenList;
// trivial reject.
if ( index === actorPos ) {
return;
}
if ( index >= cl.length ) {
cl.splice( actorPos, 1 );
cl.push( actor );
}
else {
var nActor = cl.splice( actorPos, 1 );
if ( index < 0 ) {
index = 0;
}
else if ( index > cl.length ) {
index = cl.length;
}
cl.splice( index, 0, nActor[0] );
}
this.invalidateLayout();
}
}
};
extend( CAAT.ActorContainer, CAAT.Actor, null );
var sc_drawScreenBoundingBox = CAAT.ActorContainer.superclass.drawScreenBoundingBox;
var sc_paintActor = CAAT.ActorContainer.superclass.paintActor;
var sc_paintActorGL = CAAT.ActorContainer.superclass.paintActorGL;
var sc_animate = CAAT.ActorContainer.superclass.animate;
var sc_findActorAtPosition = CAAT.ActorContainer.superclass.findActorAtPosition;
var sc_destroy = CAAT.ActorContainer.superclass.destroy;
})();
(function () {
/**
* TextActor draws text on screen. The text can be drawn directly on screen or make if follow a
* path defined by an instance of
* 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 < this.nPeaks * 2; i++ ) {
var angleStar = Math.PI / this.nPeaks * i + this.initialAngle;
var rr = (i % 2 === 0) ? r1 : r2;
var x = centerX + rr * Math.cos( angleStar );
var y = centerY + rr * Math.sin( angleStar );
ctx.lineTo( x, y );
}
ctx.lineTo(
centerX + r1 * Math.cos( this.initialAngle ),
centerY + r1 * Math.sin( this.initialAngle ) );
ctx.closePath();
if ( this.fillStyle ) {
ctx.fillStyle = this.fillStyle;
ctx.fill();
}
if ( this.strokeStyle ) {
ctx.strokeStyle = this.strokeStyle;
ctx.stroke();
}
}
};
extend( CAAT.StarActor, CAAT.ActorContainer, null );
})();
/**
* An actor suitable to draw an ImageProcessor instance.
*/
(function () {
/**
* This Actor will show the result of an image processing operation.
*
* @constructor
* @extends CAAT.ActorContainer
*/
CAAT.IMActor = function () {
CAAT.IMActor.superclass.constructor.call( this );
return this;
};
CAAT.IMActor.prototype = {
imageProcessor:null,
changeTime:100,
lastApplicationTime:-1,
/**
* Set the image processor.
*
* @param im {CAAT.ImageProcessor} a CAAT.ImageProcessor instance.
*/
setImageProcessor:function ( im ) {
this.imageProcessor = im;
return this;
},
/**
* Call image processor to update image every time milliseconds.
* @param time an integer indicating milliseconds to elapse before updating the frame.
*/
setImageProcessingTime:function ( time ) {
this.changeTime = time;
return this;
},
paint:function ( director, time ) {
if ( time - this.lastApplicationTime > this.changeTime ) {
this.imageProcessor.apply( director, time );
this.lastApplicationTime = time;
}
var ctx = director.ctx;
this.imageProcessor.paint( director, time );
}
};
extend( CAAT.IMActor, CAAT.ActorContainer, null );
})();
/**
* See LICENSE file.
*
* Sound implementation.
*/
(function () {
/**
* This class is a sound manager implementation which can play at least 'numChannels' sounds at the same time.
* By default, CAAT.Director instances will set eight channels to play sound.
*
* 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 < numChannels; i++ ) {
var channel = document.createElement( 'audio' );
if ( null !== channel ) {
channel.finished = -1;
this.channels.push( channel );
var me = this;
channel.addEventListener(
'ended',
// on sound end, set channel to available channels list.
function ( audioEvent ) {
var target = audioEvent.target;
var i;
// remove from workingChannels
for ( i = 0; i < me.workingChannels.length; i++ ) {
if ( me.workingChannels[i] === target ) {
me.workingChannels.splice( i, 1 );
break;
}
}
if ( target.caat_callback ) {
target.caat_callback( target.caat_id );
}
// set back to channels.
me.channels.push( target );
},
false
);
}
}
return this;
},
/**
* Tries to add an audio tag to the available list of valid audios. The audio is described by a url.
* @param id {object} an object to associate the audio element (if suitable to be played).
* @param url {string} a string describing an url.
* @param endplaying_callback {function} callback to be called upon sound end.
*
* @return {boolean} a boolean indicating whether the browser can play this resource.
*
* @private
*/
addAudioFromURL:function ( id, url, endplaying_callback ) {
var extension = null;
var audio = document.createElement( 'audio' );
if ( null !== audio ) {
if ( !audio.canPlayType ) {
return false;
}
extension = url.substr( url.lastIndexOf( '.' ) + 1 );
var canplay = audio.canPlayType( this.audioTypes[extension] );
if ( canplay !== "" && canplay !== "no" ) {
audio.src = url;
audio.preload = "auto";
audio.load();
if ( endplaying_callback ) {
audio.caat_callback = endplaying_callback;
audio.caat_id = id;
}
this.audioCache.push( { id:id, audio:audio } );
return true;
}
}
return false;
},
/**
* Tries to add an audio tag to the available list of valid audios. The audio element comes from
* an HTMLAudioElement.
* @param id {object} an object to associate the audio element (if suitable to be played).
* @param audio {HTMLAudioElement} a DOM audio node.
* @param endplaying_callback {function} callback to be called upon sound end.
*
* @return {boolean} a boolean indicating whether the browser can play this resource.
*
* @private
*/
addAudioFromDomNode:function ( id, audio, endplaying_callback ) {
var extension = audio.src.substr( audio.src.lastIndexOf( '.' ) + 1 );
if ( audio.canPlayType( this.audioTypes[extension] ) ) {
if ( endplaying_callback ) {
audio.caat_callback = endplaying_callback;
audio.caat_id = id;
}
this.audioCache.push( { id:id, audio:audio } );
return true;
}
return false;
},
/**
* Adds an elements to the audio cache.
* @param id {object} an object to associate the audio element (if suitable to be played).
* @param element {URL|HTMLElement} an url or html audio tag.
* @param endplaying_callback {function} callback to be called upon sound end.
*
* @return {boolean} a boolean indicating whether the browser can play this resource.
*
* @private
*/
addAudioElement:function ( id, element, endplaying_callback ) {
if ( typeof element === "string" ) {
return this.addAudioFromURL( id, element, endplaying_callback );
}
else {
try {
if ( element instanceof HTMLAudioElement ) {
return this.addAudioFromDomNode( id, element, endplaying_callback );
}
}
catch ( e ) {
}
}
return false;
},
/**
* creates an Audio object and adds it to the audio cache.
* This function expects audio data described by two elements, an id and an object which will
* describe an audio element to be associated with the id. The object will be of the form
* array, dom node or a url string.
*
*
* The audio element can be one of the two forms:
*
*
* 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 < this.workingChannels.length; i++ ) {
this.workingChannels[i].pause();
this.channels.push( this.workingChannels[i] );
}
for ( i = 0; i < this.loopingChannels.length; i++ ) {
this.loopingChannels[i].pause();
}
return this;
},
setSoundEffectsEnabled:function ( enable ) {
this.fxEnabled = enable;
return this;
},
isSoundEffectsEnabled:function () {
return this.fxEnabled;
},
setMusicEnabled:function ( enable ) {
this.musicEnabled = enable;
for ( var i = 0; i < this.loopingChannels.length; i++ ) {
if ( enable ) {
this.loopingChannels[i].play();
}
else {
this.loopingChannels[i].pause();
}
}
return this;
},
isMusicEnabled:function () {
return this.musicEnabled;
}
};
})();
/**
* See LICENSE file.
*
* In this file we'll be adding every useful Actor that is specific for certain purpose.
*
* + CAAT.Dock: a docking container that zooms in/out its actors.
*
*/
(function () {
/**
* This actor simulates a mac os-x docking component.
* Every contained actor will be laid out in a row in the desired orientation.
*/
CAAT.Dock = function () {
CAAT.Dock.superclass.constructor.call( this );
return this;
};
CAAT.Dock.prototype = {
scene:null, // scene the actor is in.
ttask:null, // resetting dimension timer.
minSize:0, // min contained actor size
maxSize:0, // max contained actor size
range:2, // aproximated number of elements affected.
layoutOp:0,
OP_LAYOUT_BOTTOM:0,
OP_LAYOUT_TOP:1,
OP_LAYOUT_LEFT:2,
OP_LAYOUT_RIGHT:3,
initialize:function ( scene ) {
this.scene = scene;
return this;
},
/**
* Set the number of elements that will be affected (zoomed) when the mouse is inside the component.
* @param range {number} a number. Defaults to 2.
*/
setApplicationRange:function ( range ) {
this.range = range;
return this;
},
/**
* Set layout orientation. Choose from
*
* 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.sDirtyRects = [];
this.dirtyRects = [];
for ( var i = 0; i < 64; i++ ) {
this.dirtyRects.push( new CAAT.Rectangle() );
}
this.dirtyRectsIndex = 0;
this.touches = {};
this.timerManager = new CAAT.TimerManager();
return this;
};
CAAT.Director.RENDER_MODE_CONTINUOUS = 1; // redraw every frame
CAAT.Director.RENDER_MODE_DIRTY = 2; // suitable for evented CAAT.
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.
renderMode:CAAT.Director.RENDER_MODE_CONTINUOUS,
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.
// @deprecated
crc:null, // 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,
size_discarded_by_dirty_rects: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, // dirty rects cache.
cDirtyRects:null, // current dirty rects.
sDirtyRects:null, // scheduled dirty rects.
dirtyRectsIndex:0,
dirtyRectsEnabled:false,
nDirtyRects:0,
drDiscarded:0, // discarded by dirty rects.
stopped:false, // is stopped, this director will do nothing.
needsRepaint:false, // for rendering mode = dirty, this flags means, paint another frame
touches:null, // Touches information. Associate touch.id with an actor and original touch info.
timerManager:null,
clean:function () {
this.scenes = null;
this.currentScene = null;
this.imagesCache = null;
this.audioManager = null;
this.isMouseDown = false;
this.lastSelectedActor = null;
this.dragging = false;
this.__gestureScale = 0;
this.__gestureRotation = 0;
this.dirty = true;
this.dirtyRects = null;
this.cDirtyRects = null;
this.dirtyRectsIndex = 0;
this.dirtyRectsEnabled = false;
this.nDirtyRects = 0;
this.onResizeCallback = null;
return this;
},
createTimer:function ( startTime, duration, callback_timeout, callback_tick, callback_cancel ) {
this.timerManager.createTimer( startTime, duration, callback_timeout, callback_tick, callback_cancel );
return this;
},
requestRepaint:function () {
this.needsRepaint = true;
},
getCurrentScene:function () {
return this.currentScene;
},
setRenderMode:function ( mode ) {
if ( mode === CAAT.Director.RENDER_MODE_CONTINUOUS || mode === CAAT.Director.RENDER_MODE_DIRTY ) {
this.renderMode = mode;
}
return this;
},
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.setBounds( 0, 0, 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 );
}
},
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;
}
return this;
},
/**
* 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.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.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();
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 < ids.length; i++ ) {
delete this.imagesCache[ ids[i] ];
}
}
this.imagesCache = imagesCache;
if ( imagesCache ) {
for ( i = 0; i < imagesCache.length; i++ ) {
this.imagesCache[ imagesCache[i].id ] = imagesCache[i].image;
}
}
this.tpW = tpW || 2048;
this.tpH = tpH || 2048;
this.updateGLPages();
return this;
},
updateGLPages:function () {
if ( this.glEnabled ) {
this.glTextureManager = new CAAT.GLTexturePageManager();
this.glTextureManager.createPages( this.gl, this.tpW, this.tpH, this.imagesCache );
this.currentTexturePage = this.glTextureManager.pages[0];
this.glTextureProgram.setTexture( this.currentTexturePage.texture );
}
},
setGLTexturePage:function ( tp ) {
this.currentTexturePage = tp;
this.glTextureProgram.setTexture( tp.texture );
return this;
},
/**
* Add a new image to director's image cache. If gl is enabled and the 'noUpdateGL' is not set to true this
* function will try to recreate the whole GL texture pages.
* If many handcrafted images are to be added to the director, some performance can be achieved by calling
*
* 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.
*
*/
renderFrame:function () {
CAAT.currentDirector = this;
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 );
}
this.needsRepaint = false;
},
/**
* If the director has renderingMode: DIRTY, the timeline must be reset to register accurate frame measurement.
*/
resetTimeline:function () {
this.timeline = new Date().getTime();
},
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 < e.targetTouches.length; i++ ) {
var ee = e.targetTouches[i];
var mp = this.mousePoint;
this.getCanvasCoord( mp, ee );
this.__mouseMoveHandler( ee );
}
}
},
__gestureStart:function ( scale, rotation ) {
this.gesturing = true;
this.__gestureRotation = this.lastSelectedActor.rotationAngle;
this.__gestureSX = this.lastSelectedActor.scaleX - 1;
this.__gestureSY = this.lastSelectedActor.scaleY - 1;
},
__gestureChange:function ( scale, rotation ) {
if ( typeof scale === 'undefined' || typeof rotation === 'undefined' ) {
return;
}
if ( this.lastSelectedActor !== null && this.lastSelectedActor.isGestureEnabled() ) {
this.lastSelectedActor.setRotation( rotation * Math.PI / 180 + this.__gestureRotation );
this.lastSelectedActor.setScale(
this.__gestureSX + scale,
this.__gestureSY + scale );
}
},
__gestureEnd:function ( scale, rotation ) {
this.gesturing = false;
this.__gestureRotation = 0;
this.__gestureScale = 0;
},
__touchEndHandlerMT:function ( e ) {
e.preventDefault();
var i, j;
var recent = [];
/**
* extrae actores afectados, y coordenadas relativas para ellos.
* crear una coleccion touch-id : { actor, touch-event }
*/
for ( i = 0; i < e.changedTouches.length; i++ ) {
var _touch = e.changedTouches[i];
var id = _touch.identifier;
recent.push( id );
}
/**
* para los touch identificados, extraer que actores se han afectado.
* crear eventos con la info de touch para cada uno.
*/
var actors = {};
for ( i = 0; i < recent.length; i++ ) {
var touchId = recent[ i ];
if ( this.touches[ touchId ] ) {
var actor = this.touches[ touchId ].actor;
if ( !actors[actor.id] ) {
actors[actor.id] = {
actor:actor,
touch:new CAAT.TouchEvent().init( e, actor, this.currentScene.time )
};
}
var ev = actors[ actor.id ].touch;
ev.addChangedTouch( this.touches[ touchId ].touch );
}
}
/**
* remove ended touch info.
*/
for ( i = 0; i < e.changedTouches.length; i++ ) {
var touch = e.changedTouches[i];
var id = touch.identifier;
delete this.touches[id];
}
/**
* notificar a todos los actores.
*/
for ( var pr in actors ) {
var data = actors[pr];
var actor = data.actor;
var touch = data.touch;
for ( var actorId in this.touches ) {
var tt = this.touches[actorId]
if ( tt.actor.id === actor.id ) {
touch.addTouch( tt.touch );
}
}
actor.touchEnd( touch );
}
},
__touchMoveHandlerMT:function ( e ) {
e.preventDefault();
var i;
var recent = [];
/**
* extrae actores afectados, y coordenadas relativas para ellos.
* crear una coleccion touch-id : { actor, touch-event }
*/
for ( i = 0; i < e.changedTouches.length; i++ ) {
var touch = e.changedTouches[i];
var id = touch.identifier;
if ( this.touches[ id ] ) {
var mp = this.mousePoint;
this.getCanvasCoord( mp, touch );
var actor = this.touches[ id ].actor;
mp = actor.viewToModel( mp );
this.touches[ id ] = {
actor:actor,
touch:new CAAT.TouchInfo( id, mp.x, mp.y, actor )
};
recent.push( id );
}
}
/**
* para los touch identificados, extraer que actores se han afectado.
* crear eventos con la info de touch para cada uno.
*/
var actors = {};
for ( i = 0; i < recent.length; i++ ) {
var touchId = recent[ i ];
var actor = this.touches[ touchId ].actor;
if ( !actors[actor.id] ) {
actors[actor.id] = {
actor:actor,
touch:new CAAT.TouchEvent().init( e, actor, this.currentScene.time )
};
}
var ev = actors[ actor.id ].touch;
ev.addTouch( this.touches[ touchId ].touch );
ev.addChangedTouch( this.touches[ touchId ].touch );
}
/**
* notificar a todos los actores.
*/
for ( var pr in actors ) {
var data = actors[pr];
var actor = data.actor;
var touch = data.touch;
for ( var actorId in this.touches ) {
var tt = this.touches[actorId]
if ( tt.actor.id === actor.id ) {
touch.addTouch( tt.touch );
}
}
actor.touchMove( touch );
}
},
__touchCancelHandleMT:function ( e ) {
this.__touchEndHandlerMT( e );
},
__touchStartHandlerMT:function ( e ) {
e.preventDefault();
var i;
var recent = [];
var allInCanvas = true;
/**
* extrae actores afectados, y coordenadas relativas para ellos.
* crear una coleccion touch-id : { actor, touch-event }
*/
for ( i = 0; i < e.changedTouches.length; i++ ) {
var touch = e.changedTouches[i];
var id = touch.identifier;
var mp = this.mousePoint;
this.getCanvasCoord( mp, touch );
if ( mp.x < 0 || mp.y < 0 || mp.x >= this.width || mp.y >= this.height ) {
allInCanvas = false;
continue;
}
var actor = this.findActorAtPosition( mp );
if ( actor !== null ) {
mp = actor.viewToModel( mp );
if ( !this.touches[ id ] ) {
this.touches[ id ] = {
actor:actor,
touch:new CAAT.TouchInfo( id, mp.x, mp.y, actor )
};
recent.push( id );
}
}
}
/**
* para los touch identificados, extraer que actores se han afectado.
* crear eventos con la info de touch para cada uno.
*/
var actors = {};
for ( i = 0; i < recent.length; i++ ) {
var touchId = recent[ i ];
var actor = this.touches[ touchId ].actor;
if ( !actors[actor.id] ) {
actors[actor.id] = {
actor:actor,
touch:new CAAT.TouchEvent().init( e, actor, this.currentScene.time )
};
}
var ev = actors[ actor.id ].touch;
ev.addTouch( this.touches[ touchId ].touch );
ev.addChangedTouch( this.touches[ touchId ].touch );
}
/**
* notificar a todos los actores.
*/
for ( var pr in actors ) {
var data = actors[pr];
var actor = data.actor;
var touch = data.touch;
for ( var actorId in this.touches ) {
var tt = this.touches[actorId]
if ( tt.actor.id === actor.id ) {
touch.addTouch( tt.touch );
}
}
actor.touchStart( touch );
}
},
__findTouchFirstActor:function () {
var t = Number.MAX_VALUE;
var actor = null;
for ( var pr in this.touches ) {
var touch = this.touches[pr];
if ( touch.touch.time && touch.touch.time < t && touch.actor.isGestureEnabled() ) {
actor = touch.actor;
t = touch.touch.time;
}
}
return actor;
},
__gesturedActor:null,
__touchGestureStartHandleMT:function ( e ) {
var actor = this.__findTouchFirstActor();
if ( actor !== null && actor.isGestureEnabled() ) {
this.__gesturedActor = actor;
this.__gestureRotation = actor.rotationAngle;
this.__gestureSX = actor.scaleX - 1;
this.__gestureSY = actor.scaleY - 1;
actor.gestureStart(
e.rotation * Math.PI / 180,
e.scale + this.__gestureSX,
e.scale + this.__gestureSY );
}
},
__touchGestureEndHandleMT:function ( e ) {
if ( null !== this.__gesturedActor && this.__gesturedActor.isGestureEnabled() ) {
this.__gesturedActor.gestureEnd(
e.rotation * Math.PI / 180,
e.scale + this.__gestureSX,
e.scale + this.__gestureSY );
}
this.__gestureRotation = 0;
this.__gestureScale = 0;
},
__touchGestureChangeHandleMT:function ( e ) {
if ( this.__gesturedActor !== null && this.__gesturedActor.isGestureEnabled() ) {
this.__gesturedActor.gestureChange(
e.rotation * Math.PI / 180,
this.__gestureSX + e.scale,
this.__gestureSY + e.scale );
}
},
addHandlers:function ( canvas ) {
var me = this;
window.addEventListener( 'mouseup', function ( e ) {
if ( me.touching ) {
e.preventDefault();
e.cancelBubble = true;
if ( e.stopPropagation ) {
e.stopPropagation();
}
var mp = me.mousePoint;
me.getCanvasCoord( mp, e );
me.__mouseUpHandler( e );
me.touching = false;
}
}, false );
window.addEventListener( 'mousedown', function ( e ) {
if ( e.target === canvas ) {
e.preventDefault();
e.cancelBubble = true;
if ( e.stopPropagation ) {
e.stopPropagation();
}
var mp = me.mousePoint;
me.getCanvasCoord( mp, e );
if ( mp.x < 0 || mp.y < 0 || mp.x >= me.width || mp.y >= me.height ) {
return;
}
me.touching = true;
me.__mouseDownHandler( e );
}
}, false );
window.addEventListener( 'mouseover', function ( e ) {
if ( e.target === canvas && !me.dragging ) {
e.preventDefault();
e.cancelBubble = true;
if ( e.stopPropagation ) {
e.stopPropagation();
}
var mp = me.mousePoint;
me.getCanvasCoord( mp, e );
if ( mp.x < 0 || mp.y < 0 || mp.x >= me.width || mp.y >= me.height ) {
return;
}
me.__mouseOverHandler( e );
}
}, false );
window.addEventListener( 'mouseout', function ( e ) {
if ( e.target === canvas && !me.dragging ) {
e.preventDefault();
e.cancelBubble = true;
if ( e.stopPropagation ) {
e.stopPropagation();
}
var mp = me.mousePoint;
me.getCanvasCoord( mp, e );
me.__mouseOutHandler( e );
}
}, false );
window.addEventListener( 'mousemove', function ( e ) {
e.preventDefault();
e.cancelBubble = true;
if ( e.stopPropagation ) {
e.stopPropagation();
}
var mp = me.mousePoint;
me.getCanvasCoord( mp, e );
if ( !me.dragging && ( mp.x < 0 || mp.y < 0 || mp.x >= me.width || mp.y >= me.height ) ) {
return;
}
me.__mouseMoveHandler( e );
}, false );
window.addEventListener( "dblclick", function ( e ) {
if ( e.target === canvas ) {
e.preventDefault();
e.cancelBubble = true;
if ( e.stopPropagation ) {
e.stopPropagation();
}
var mp = me.mousePoint;
me.getCanvasCoord( mp, e );
if ( mp.x < 0 || mp.y < 0 || mp.x >= me.width || mp.y >= me.height ) {
return;
}
me.__mouseDBLClickHandler( e );
}
}, false );
if ( CAAT.TOUCH_BEHAVIOR === CAAT.TOUCH_AS_MOUSE ) {
canvas.addEventListener( "touchstart", this.__touchStartHandler.bind( this ), false );
canvas.addEventListener( "touchmove", this.__touchMoveHandler.bind( this ), false );
canvas.addEventListener( "touchend", this.__touchEndHandler.bind( this ), false );
canvas.addEventListener( "gesturestart", function ( e ) {
if ( e.target === canvas ) {
e.preventDefault();
me.__gestureStart( e.scale, e.rotation );
}
}, false );
canvas.addEventListener( "gestureend", function ( e ) {
if ( e.target === canvas ) {
e.preventDefault();
me.__gestureEnd( e.scale, e.rotation );
}
}, false );
canvas.addEventListener( "gesturechange", function ( e ) {
if ( e.target === canvas ) {
e.preventDefault();
me.__gestureChange( e.scale, e.rotation );
}
}, false );
}
else if ( CAAT.TOUCH_BEHAVIOR === CAAT.TOUCH_AS_MULTITOUCH ) {
canvas.addEventListener( "touchstart", this.__touchStartHandlerMT.bind( this ), false );
canvas.addEventListener( "touchmove", this.__touchMoveHandlerMT.bind( this ), false );
canvas.addEventListener( "touchend", this.__touchEndHandlerMT.bind( this ), false );
canvas.addEventListener( "touchcancel", this.__touchCancelHandleMT.bind( this ), false );
canvas.addEventListener( "gesturestart", this.__touchGestureStartHandleMT.bind( this ), false );
canvas.addEventListener( "gestureend", this.__touchGestureEndHandleMT.bind( this ), false );
canvas.addEventListener( "gesturechange", this.__touchGestureChangeHandleMT.bind( this ), false );
}
},
enableEvents:function ( onElement ) {
CAAT.RegisterDirector( this );
this.in_ = false;
this.createEventHandler( onElement );
},
createEventHandler:function ( onElement ) {
//var canvas= this.canvas;
this.in_ = false;
//this.addHandlers(canvas);
this.addHandlers( onElement );
}
};
if ( CAAT.__CSS__ ) {
CAAT.Director.prototype.clip = true;
CAAT.Director.prototype.glEnabled = false;
CAAT.Director.prototype.getRenderType = function () {
return 'CSS';
};
CAAT.Director.prototype.setScaleProportional = function ( w, h ) {
var factor = Math.min( w / this.referenceWidth, h / this.referenceHeight );
this.setScaleAnchored( factor, factor, 0, 0 );
this.eventHandler.style.width = '' + this.referenceWidth + 'px';
this.eventHandler.style.height = '' + this.referenceHeight + 'px';
};
CAAT.Director.prototype.setBounds = function ( x, y, w, h ) {
CAAT.Director.superclass.setBounds.call( this, x, y, w, h );
for ( var i = 0; i < this.scenes.length; i++ ) {
this.scenes[i].setBounds( 0, 0, w, h );
}
this.eventHandler.style.width = w + 'px';
this.eventHandler.style.height = h + 'px';
return this;
};
/**
* In this DOM/CSS implementation, proxy is not taken into account since the event router is a top most
* div in the document hierarchy (z-index 999999).
* @param width
* @param height
* @param domElement
* @param proxy
*/
CAAT.Director.prototype.initialize = function ( width, height, domElement, proxy ) {
this.timeline = new Date().getTime();
this.domElement = domElement;
this.style( 'position', 'absolute' );
this.style( 'width', '' + width + 'px' );
this.style( 'height', '' + height + 'px' );
this.style( 'overflow', 'hidden' );
this.enableEvents( domElement );
this.setBounds( 0, 0, width, height );
this.checkDebug();
return this;
};
CAAT.Director.prototype.render = function ( time ) {
this.time += time;
this.animate( this, time );
/**
* draw director active scenes.
*/
var i, l, tt;
if ( CAAT.DEBUG ) {
this.resetStats();
}
for ( i = 0, l = this.childrenList.length; i < l; i++ ) {
var c = this.childrenList[i];
if ( c.isInAnimationFrame( this.time ) ) {
tt = c.time - c.start_time;
if ( c.onRenderStart ) {
c.onRenderStart( tt );
}
c.paintActor( this, tt );
if ( c.onRenderEnd ) {
c.onRenderEnd( tt );
}
if ( !c.isPaused() ) {
c.time += time;
}
if ( CAAT.DEBUG ) {
this.statistics.size_discarded_by_dirtyRects += this.drDiscarded;
this.statistics.size_total += c.size_total;
this.statistics.size_active += c.size_active;
this.statistics.size_dirtyRects = this.nDirtyRects;
}
}
}
this.frameCounter++;
};
CAAT.Director.prototype.addScene = function ( scene ) {
scene.setVisible( true );
scene.setBounds( 0, 0, this.width, this.height );
this.scenes.push( scene );
scene.setEaseListener( this );
if ( null === this.currentScene ) {
this.setScene( 0 );
}
this.domElement.appendChild( scene.domElement );
};
CAAT.Director.prototype.emptyScenes = function () {
this.scenes = [];
this.domElement.innerHTML = '';
this.createEventHandler();
};
CAAT.Director.prototype.setClear = function ( clear ) {
return this;
};
CAAT.Director.prototype.createEventHandler = function () {
this.eventHandler = document.createElement( 'div' );
this.domElement.appendChild( this.eventHandler );
this.eventHandler.style.position = 'absolute';
this.eventHandler.style.left = '0';
this.eventHandler.style.top = '0';
this.eventHandler.style.zIndex = 999999;
this.eventHandler.style.width = '' + this.width + 'px';
this.eventHandler.style.height = '' + this.height + 'px';
this.canvas = this.eventHandler;
this.in_ = false;
this.addHandlers( this.canvas );
};
CAAT.Director.prototype.inDirtyRect = function () {
return true;
}
}
extend( CAAT.Director, CAAT.ActorContainer, null );
})();
/**
* See LICENSE file.
*
* MouseEvent is a class to hold necessary information of every mouse event related to concrete
* scene graph Actors.
*
* Here it is also the logic to on mouse events, pump the correct event to the appropiate scene
* graph Actor.
*
* TODO: add events for event pumping:
* + cancelBubling
*
**/
CAAT.TouchInfo = function ( id, x, y, target ) {
this.identifier = id;
this.clientX = x;
this.pageX = x;
this.clientY = y;
this.pageY = y;
this.target = target;
this.time = new Date().getTime();
return this;
};
(function () {
/**
* This function creates a mouse event that represents a touch or mouse event.
* @constructor
*/
CAAT.TouchEvent = function () {
this.touches = [];
this.changedTouches = [];
return this;
};
CAAT.TouchEvent.prototype = {
time:0,
source:null,
sourceEvent:null,
shift:false,
control:false,
alt:false,
meta:false,
touches:null,
changedTouches:null,
init:function ( sourceEvent, source, time ) {
this.source = source;
this.alt = sourceEvent.altKey;
this.control = sourceEvent.ctrlKey;
this.shift = sourceEvent.shiftKey;
this.meta = sourceEvent.metaKey;
this.sourceEvent = sourceEvent;
this.time = time;
return this;
},
/**
*
* @param touchInfo
* <{
* id :
* It is able to draw its sub-images in the following ways:
*
* It is supposed to be used in conjunction with
* A TimerTask is defined at least by:
*
* 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;
}
};
})();
(function () {
CAAT.TimerManager = function () {
this.timerList = [];
return this;
};
CAAT.TimerManager.prototype = {
timerList:null, // collection of CAAT.TimerTask objects.
timerSequence:0, // incremental CAAT.TimerTask id.
/**
* 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 < tl.length; i++ ) {
if ( tl[i].remove ) {
tl.splice( i, 1 );
}
}
}
}
})();
/**
* 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.timerManager = new CAAT.TimerManager();
this.fillStyle = null;
this.isGlobalAlpha = true;
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,
paused:false,
timerManager:null,
isPaused:function () {
return this.paused;
},
setPaused:function ( paused ) {
this.paused = paused;
},
createTimer:function ( startTime, duration, callback_timeout, callback_tick, callback_cancel ) {
return this.timerManager.createTimer( startTime, duration, callback_timeout, callback_tick, callback_cancel );
},
/**
* Scene animation method.
* It extends Container's base behavior by adding timer control.
* @param director {CAAT.Director} a CAAT.Director instance.
* @param time {number} an integer indicating the Scene time the animation is being performed at.
*/
animate:function ( director, time ) {
this.timerManager.checkTimers( time );
CAAT.Scene.superclass.animate.call( this, director, time );
this.timerManager.removeExpiredTimers();
},
/**
* Helper method to manage alpha transparency fading on Scene switch by the Director.
* @param time {number} integer indicating the time in milliseconds the fading will take.
* @param isIn {boolean} boolean indicating whether this Scene in the switch process is
* being brought in.
*
* @private
*/
createAlphaBehaviour:function ( time, isIn ) {
var ab = new CAAT.AlphaBehavior();
ab.setFrameTime( 0, time );
ab.startAlpha = isIn ? 0 : 1;
ab.endAlpha = isIn ? 1 : 0;
this.easeContainerBehaviour.addBehavior( ab );
},
/**
* Called from CAAT.Director to bring in an Scene.
* A helper method for easeTranslation.
* @param time integer indicating time in milliseconds for the Scene to be brought in.
* @param alpha boolean indicating whether fading will be applied to the Scene.
* @param anchor integer indicating the Scene switch anchor.
* @param interpolator CAAT.Interpolator to apply to the Scene transition.
*/
easeTranslationIn:function ( time, alpha, anchor, interpolator ) {
this.easeTranslation( time, alpha, anchor, true, interpolator );
},
/**
* Called from CAAT.Director to bring in an Scene.
* A helper method for easeTranslation.
* @param time integer indicating time in milliseconds for the Scene to be taken away.
* @param alpha boolean indicating whether fading will be applied to the Scene.
* @param anchor integer indicating the Scene switch anchor.
* @param interpolator CAAT.Interpolator to apply to the Scene transition.
*/
easeTranslationOut:function ( time, alpha, anchor, interpolator ) {
this.easeTranslation( time, alpha, anchor, false, interpolator );
},
/**
* This method will setup Scene behaviours to switch an Scene via a translation.
* The anchor value can only be
*
* It is implemented by all path segment types, ie:
*
* Every operation of the CAAT.PathSegment interface is performed for every path segment. In example,
* the method
* 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
* If a call to any method of the form
* 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; i
* This class pre-creates a canvas of the given dimensions and extracts an imageData object to
* hold the pixel manipulation.
*
* @constructor
*/
CAAT.ImageProcessor = function () {
return this;
};
CAAT.ImageProcessor.prototype = {
canvas:null,
ctx:null,
width:0,
height:0,
imageData:null,
bufferImage:null,
/**
* Grabs an image pixels.
*
* @param image {HTMLImageElement}
* @return {ImageData} returns an ImageData object with the image representation or null in
* case the pixels can not be grabbed.
*
* @static
*/
grabPixels:function ( image ) {
var canvas = document.createElement( 'canvas' );
if ( canvas !== null ) {
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext( '2d' );
ctx.drawImage( image, 0, 0 );
try {
var imageData = ctx.getImageData( 0, 0, canvas.width, canvas.height );
return imageData;
}
catch ( e ) {
CAAT.log( 'error pixelgrabbing.', image );
return null;
}
}
return null;
},
/**
* Helper method to create an array.
*
* @param size {number} integer number of elements in the array.
* @param initValue {number} initial array values.
*
* @return {[]} an array of 'initialValue' elements.
*
* @static
*/
makeArray:function ( size, initValue ) {
var a = [];
for ( var i = 0; i < size; i++ ) {
a.push( initValue );
}
return a;
},
/**
* Helper method to create a bidimensional array.
*
* @param size {number} number of array rows.
* @param size2 {number} number of array columns.
* @param initvalue array initial values.
*
* @return {[]} a bidimensional array of 'initvalue' elements.
*
* @static
*
*/
makeArray2D:function ( size, size2, initvalue ) {
var a = [];
for ( var i = 0; i < size; i++ ) {
a.push( this.makeArray( size2, initvalue ) );
}
return a;
},
/**
* Initializes and creates an offscreen Canvas object. It also creates an ImageData object and
* initializes the internal bufferImage attribute to imageData's data.
* @param width {number} canvas width.
* @param height {number} canvas height.
* @return this
*/
initialize:function ( width, height ) {
this.width = width;
this.height = height;
this.canvas = document.createElement( 'canvas' );
if ( this.canvas !== null ) {
this.canvas.width = width;
this.canvas.height = height;
this.ctx = this.canvas.getContext( '2d' );
this.imageData = this.ctx.getImageData( 0, 0, width, height );
this.bufferImage = this.imageData.data;
}
return this;
},
/**
* Clear this ImageData object to the given color components.
* @param r {number} red color component 0..255.
* @param g {number} green color component 0..255.
* @param b {number} blue color component 0..255.
* @param a {number} alpha color component 0..255.
* @return this
*/
clear:function ( r, g, b, a ) {
if ( null === this.imageData ) {
return this;
}
var data = this.imageData.data;
for ( var i = 0; i < this.width * this.height; i++ ) {
data[i * 4 + 0] = r;
data[i * 4 + 1] = g;
data[i * 4 + 2] = b;
data[i * 4 + 3] = a;
}
this.imageData.data = data;
return this;
},
/**
* Get this ImageData.
* @return {ImageData}
*/
getImageData:function () {
return this.ctx.getImageData( 0, 0, this.width, this.height );
},
/**
* Sets canvas pixels to be the applied effect. After process pixels, this method must be called
* to show the result of such processing.
* @param director {CAAT.Director}
* @param time {number}
* @return this
*/
apply:function ( director, time ) {
if ( null !== this.imageData ) {
this.imageData.data = this.bufferImage;
this.ctx.putImageData( this.imageData, 0, 0 );
}
return this;
},
/**
* Returns the offscreen canvas.
* @return {HTMLCanvasElement}
*/
getCanvas:function () {
return this.canvas;
},
/**
* Creates a pattern that will make this ImageProcessor object suitable as a fillStyle value.
* This effect can be drawn too as an image by calling: canvas_context.drawImage methods.
* @param type {string} the pattern type. if no value is supplied 'repeat' will be used.
* @return CanvasPattern.
*/
createPattern:function ( type ) {
return this.ctx.createPattern( this.canvas, type ? type : 'repeat' );
},
/**
* Paint this ImageProcessor object result.
* @param director {CAAT.Director}.
* @param time {number} scene time.
*/
paint:function ( director, time ) {
if ( null !== this.canvas ) {
var ctx = director.ctx;
ctx.drawImage( this.getCanvas(), 0, 0 );
}
}
};
})();
(function () {
/**
* Creates an additive plasma wave image.
*
* @constructor
* @extends CAAT.ImageProcessor
*
*/
CAAT.IMPlasma = function () {
CAAT.IMPlasma.superclass.constructor.call( this );
return this;
};
CAAT.IMPlasma.prototype = {
wavetable:null,
m_colorMap:null,
spd1:1,
spd2:2,
spd3:3,
spd4:4,
pos1:0,
pos2:0,
pos3:0,
pos4:0,
tpos1:0,
tpos2:0,
tpos3:0,
tpos4:0,
m_colorMapSize:256,
i1:0,
i2:0,
i3:0,
i4:0,
b1:false,
b2:false,
b3:false,
b4:false,
color:[0xffffffff, 0xffff00ff, 0xffffff00, 0xff00ff00, 0xffff0000, 0xff0000ff, 0xff000000],
/**
* Initialize the plasma image processor.
*
* This image processor creates a color ramp of 256 elements from the colors of the parameter 'colors'.
* Be aware of color definition since the alpha values count to create the ramp.
*
* @param width {number}
* @param height {number}
* @param colors {Array.
*
*
* function 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,
discardable:false, // is true, this behavior will be removed from the this.actor instance when it expires.
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;
};
var AXIS_X = 0;
var AXIS_Y = 1;
CAAT.Scale1Behavior.AXIS_X = AXIS_X;
CAAT.Scale1Behavior.AXIS_Y = AXIS_Y;
CAAT.Scale1Behavior.prototype = {
startScale:1,
endScale:1,
anchorX:.50,
anchorY:.50,
sx:1,
sy:1,
applyOnX:true,
applyOnAxis:function ( axis ) {
if ( axis === AXIS_Y ) {
this.applyOnX = false;
}
else {
this.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 < data.length; i++ ) {
var dataString = data[i].string;
var dataProp = data[i].prop;
this.versionSearchString = data[i].versionSearch || data[i].identity;
if ( dataString ) {
if ( dataString.indexOf( data[i].subString ) !== -1 ) {
return data[i].identity;
}
}
else if ( dataProp ) {
return data[i].identity;
}
}
},
searchVersion:function ( dataString ) {
var index = dataString.indexOf( this.versionSearchString );
if ( index === -1 ) {
return;
}
return parseFloat( dataString.substring( index + this.versionSearchString.length + 1 ) );
},
dataBrowser:[
{
string:navigator.userAgent,
subString:"Chrome",
identity:"Chrome"
},
{ string:navigator.userAgent,
subString:"OmniWeb",
versionSearch:"OmniWeb/",
identity:"OmniWeb"
},
{
string:navigator.vendor,
subString:"Apple",
identity:"Safari",
versionSearch:"Version"
},
{
prop:window.opera,
identity:"Opera"
},
{
string:navigator.vendor,
subString:"iCab",
identity:"iCab"
},
{
string:navigator.vendor,
subString:"KDE",
identity:"Konqueror"
},
{
string:navigator.userAgent,
subString:"Firefox",
identity:"Firefox"
},
{
string:navigator.vendor,
subString:"Camino",
identity:"Camino"
},
{ // for newer Netscapes (6+)
string:navigator.userAgent,
subString:"Netscape",
identity:"Netscape"
},
{
string:navigator.userAgent,
subString:"MSIE",
identity:"Explorer",
versionSearch:"MSIE"
},
{
string:navigator.userAgent,
subString:"Explorer",
identity:"Explorer",
versionSearch:"Explorer"
},
{
string:navigator.userAgent,
subString:"Gecko",
identity:"Mozilla",
versionSearch:"rv"
},
{ // for older Netscapes (4-)
string:navigator.userAgent,
subString:"Mozilla",
identity:"Netscape",
versionSearch:"Mozilla"
}
],
dataOS:[
{
string:navigator.platform,
subString:"Win",
identity:"Windows"
},
{
string:navigator.platform,
subString:"Mac",
identity:"Mac"
},
{
string:navigator.userAgent,
subString:"iPhone",
identity:"iPhone/iPod"
},
{
string:navigator.platform,
subString:"Linux",
identity:"Linux"
}
]
};
})();
/**
* See LICENSE file.
*
* Get realtime Debug information of CAAT's activity.
* Set CAAT.DEBUG=1 before any CAAT.Director object creation.
* This class creates a DOM node called 'caat-debug' and associated styles
* The debug panel is minimized by default and shows short information. It can be expanded and minimized again by clicking on it
*
*/
(function () {
CAAT.Debug = function () {
return this;
};
CAAT.Debug.prototype = {
width:0,
height:0,
canvas:null,
ctx:null,
statistics:null,
framerate:null,
textContainer:null,
textFPS:null,
textEntitiesTotal:null,
textEntitiesActive:null,
textDraws:null,
textDrawTime:null,
textRAFTime:null,
textDirtyRects:null,
textDiscardDR:null,
frameTimeAcc:0,
frameRAFAcc:0,
canDebug:false,
SCALE:60,
debugTpl:" " +
"
*
*
* @constructor
*/
CAAT.Actor = function () {
this.CLASS = CAAT.Actor;
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 );
this.id = __index++;
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":"property:x, type:number",
"y":"property:y, type:number",
"scaleX":"property:scaleX, type:number",
"scaleY":"property:scaleY, type:number",
"cached":"get:isCached(), type:boolean"
};
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.
preferredSize:null, // actor's preferred size for layout. {CAAT.Dimension}
minimumSize:null, // actor's minimum size for layout. {CAAT.Dimension},
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
rotationX:.50, // transformation. rotation center x
alpha:1, // alpha transparency value
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,
__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.
invalidateLayout:function () {
if ( this.parent && !this.parent.layoutInvalidated ) {
this.parent.invalidateLayout();
}
return this;
},
__validateLayout:function () {
},
/**
* Set this actors preferred layout size.
*
* @param pw {number}
* @param ph {number}
* @return {*}
*/
setPreferredSize:function ( pw, ph ) {
if ( !this.preferredSize ) {
this.preferredSize = new CAAT.Dimension();
}
this.preferredSize.width = pw;
this.preferredSize.height = ph;
return this;
},
getPreferredSize:function () {
return this.preferredSize ? this.preferredSize :
this.getMinimumSize();
},
/**
* Set this actors minimum layout size.
*
* @param pw {number}
* @param ph {number}
* @return {*}
*/
setMinimumSize:function ( pw, ph ) {
if ( !this.minimumSize ) {
this.minimumSize = new CAAT.Dimension();
}
this.minimumSize.width = pw;
this.minimumSize.height = ph;
return this;
},
getMinimumSize:function () {
return this.minimumSize ? this.minimumSize :
new CAAT.Dimension( this.width, this.height );
},
/**
* @deprecated
* @return {*}
*/
create:function () {
return this;
},
/**
* Move this actor to a position.
* It creates and adds a new PathBehavior.
* @param x {number} new x position
* @param y {number} new y position
* @param duration {number} time to take to get to new position
* @param delay {=number} time to wait before start moving
* @param interpolator {=CAAT.Interpolator} a CAAT.Interpolator instance
*/
moveTo:function ( x, y, duration, delay, interpolator, callback ) {
if ( x === this.x && y === this.y ) {
return;
}
var id = '__moveTo';
var b = this.getBehavior( id );
if ( !b ) {
b = new CAAT.PathBehavior().
setId( id ).
setValues( new CAAT.LinearPath() );
this.addBehavior( b );
}
b.path.setInitialPosition( this.x, this.y ).setFinalPosition( x, y );
b.setDelayTime( delay ? delay : 0, duration );
if ( interpolator ) {
b.setInterpolator( interpolator );
}
if ( callback ) {
b.lifecycleListenerList = [];
b.addListener( {
behaviorExpired:function ( behavior, time, actor ) {
callback( behavior, time, actor );
}
} );
}
return this;
},
/**
*
* @param angle {number} new rotation angle
* @param duration {number} time to rotate
* @param delay {number=} millis to start rotation
* @param anchorX {number=} rotation anchor x
* @param anchorY {number=} rotation anchor y
* @param interpolator {CAAT.Interpolator=}
* @return {*}
*/
rotateTo:function ( angle, duration, delay, anchorX, anchorY, interpolator ) {
if ( angle === this.rotationAngle ) {
return;
}
var id = '__rotateTo';
var b = this.getBehavior( id );
if ( !b ) {
b = new CAAT.RotateBehavior().
setId( id ).
setValues( 0, 0, .5, .5 );
this.addBehavior( b );
}
b.setValues( this.rotationAngle, angle, anchorX, anchorY ).
setDelayTime( delay ? delay : 0, duration );
if ( interpolator ) {
b.setInterpolator( interpolator );
}
return this;
},
/**
*
* @param scaleX {number} new X scale
* @param scaleY {number} new Y scale
* @param duration {number} time to rotate
* @param delay {=number} millis to start rotation
* @param anchorX {=number} rotation anchor x
* @param anchorY {=number} rotation anchor y
* @param interpolator {=CAAT.Interpolator}
* @return {*}
*/
scaleTo:function ( scaleX, scaleY, duration, delay, anchorX, anchorY, interpolator ) {
if ( this.scaleX === scaleX && this.scaleY === scaleY ) {
return;
}
var id = '__scaleTo';
var b = this.getBehavior( id );
if ( !b ) {
b = new CAAT.ScaleBehavior().
setId( id ).
setValues( 1, 1, 1, 1, .5, .5 );
this.addBehavior( b );
}
b.setValues( this.scaleX, scaleX, this.scaleY, scaleY, anchorX, anchorY ).
setDelayTime( delay ? delay : 0, duration );
if ( interpolator ) {
b.setInterpolator( interpolator );
}
return this;
},
/**
*
* @param scaleX {number} new X scale
* @param duration {number} time to rotate
* @param delay {=number} millis to start rotation
* @param anchorX {=number} rotation anchor x
* @param anchorY {=number} rotation anchor y
* @param interpolator {=CAAT.Interpolator}
* @return {*}
*/
scaleXTo:function ( scaleX, duration, delay, anchorX, anchorY, interpolator ) {
return this.__scale1To(
CAAT.Scale1Behavior.AXIS_X,
scaleX,
duration,
delay,
anchorX,
anchorY,
interpolator
);
},
/**
*
* @param scaleY {number} new Y scale
* @param duration {number} time to rotate
* @param delay {=number} millis to start rotation
* @param anchorX {=number} rotation anchor x
* @param anchorY {=number} rotation anchor y
* @param interpolator {=CAAT.Interpolator}
* @return {*}
*/
scaleYTo:function ( scaleY, duration, delay, anchorX, anchorY, interpolator ) {
return this.__scale1To(
CAAT.Scale1Behavior.AXIS_Y,
scaleY,
duration,
delay,
anchorX,
anchorY,
interpolator
);
},
/**
* @param axis {CAAT.Scale1Behavior.AXIS_X|CAAT.Scale1Behavior.AXIS_Y} scale application axis
* @param scale {number} new Y scale
* @param duration {number} time to rotate
* @param delay {=number} millis to start rotation
* @param anchorX {=number} rotation anchor x
* @param anchorY {=number} rotation anchor y
* @param interpolator {=CAAT.Interpolator}
* @return {*}
*/
__scale1To:function ( axis, scale, duration, delay, anchorX, anchorY, interpolator ) {
if ( ( axis === CAAT.Scale1Behavior.AXIS_X && scale === this.scaleX) ||
( axis === CAAT.Scale1Behavior.AXIS_Y && scale === this.scaleY) ) {
return;
}
var id = '__scaleXTo';
var b = this.getBehavior( id );
if ( !b ) {
b = new CAAT.Scale1Behavior().
setId( id ).
setValues( 1, 1, axis === CAAT.Scale1Behavior.AXIS_X, .5, .5 );
this.addBehavior( b );
}
b.setValues(
axis ? this.scaleX : this.scaleY,
scale,
anchorX,
anchorY ).
setDelayTime( delay ? delay : 0, duration );
if ( interpolator ) {
b.setInterpolator( interpolator );
}
return this;
},
/**
* Touch Start only received when CAAT.TOUCH_BEHAVIOR= CAAT.TOUCH_AS_MULTITOUCH
* @param e actorLyfeCycleEvent( 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; i < this.lifecycleListenerList.length; i++ ) {
this.lifecycleListenerList[i].actorLifeCycleEvent( this, sEventType, time );
}
},
/**
* Sets this Actor as Expired.
* If this is a Container, all the contained Actors won't be nor drawn nor will receive
* any event. That is, expiring an Actor means totally taking it out the Scene's timeline.
* @param time {number} an integer indicating the time the Actor was expired at.
* @return this.
*/
setExpired:function ( time ) {
this.expired = true;
this.fireEvent( 'expired', time );
return this;
},
/**
* Enable or disable the event bubbling for this Actor.
* @param enable {boolean} a boolean indicating whether the event bubbling is enabled.
* @return this
*/
enableEvents:function ( enable ) {
this.mouseEnabled = enable;
return this;
},
/**
* Removes all behaviors from an Actor.
* @return this
*/
emptyBehaviorList:function () {
this.behaviorList = [];
return this;
},
/**
* Caches a fillStyle in the Actor.
* @param style a valid Canvas rendering context fillStyle.
* @return this
*/
setFillStyle:function ( style ) {
this.fillStyle = style;
this.invalidate();
return this;
},
/**
* Caches a stroke style in the Actor.
* @param style a valid canvas rendering context stroke style.
* @return this
*/
setStrokeStyle:function ( style ) {
this.strokeStyle = style;
this.invalidate();
return this;
},
/**
* @deprecated
* @param paint
*/
setPaint:function ( paint ) {
return this.setFillStyle( paint );
},
/**
* Stablishes the Alpha transparency for the Actor.
* If it globalAlpha enabled, this alpha will the maximum alpha for every contained actors.
* The alpha must be between 0 and 1.
* @param alpha a float indicating the alpha value.
* @return this
*/
setAlpha:function ( alpha ) {
this.alpha = alpha;
this.invalidate();
return this;
},
/**
* Remove all transformation values for the Actor.
* @return this
*/
resetTransform:function () {
this.rotationAngle = 0;
this.rotationX = .5;
this.rotationY = .5;
this.scaleX = 1;
this.scaleY = 1;
this.scaleTX = .5;
this.scaleTY = .5;
this.scaleAnchor = 0;
this.oldX = -1;
this.oldY = -1;
this.dirty = true;
return this;
},
/**
* Sets the time life cycle for an Actor.
* These values are related to Scene time.
* @param startTime an integer indicating the time until which the Actor won't be visible on the Scene.
* @param duration an integer indicating how much the Actor will last once visible.
* @return this
*/
setFrameTime:function ( startTime, duration ) {
this.start_time = startTime;
this.duration = duration;
this.expired = false;
this.dirty = true;
return this;
},
/**
* This method should me overriden by every custom Actor.
* It will be the drawing routine called by the Director to show every Actor.
* @param director the CAAT.Director instance that contains the Scene the Actor is in.
* @param time an integer indicating the Scene time in which the drawing is performed.
*/
paint:function ( director, time ) {
if ( this.backgroundImage ) {
this.backgroundImage.paint( director, time, 0, 0 );
}
else if ( this.fillStyle ) {
var ctx = director.crc;
ctx.fillStyle = this.fillStyle;
ctx.fillRect( 0, 0, this.width, this.height );
}
},
/**
* A helper method to setScaleAnchored with an anchor of ANCHOR_CENTER
*
* @see setScaleAnchored
*
* @param sx a float indicating a width size multiplier.
* @param sy a float indicating a height size multiplier.
* @return this
*/
setScale:function ( sx, sy ) {
this.scaleX = sx;
this.scaleY = sy;
this.dirty = true;
return this;
},
getAnchorPercent:function ( anchor ) {
var anchors = [
.50, .50, .50, 0, .50, 1.00,
0, .50, 1.00, .50, 0, 0,
1.00, 0, 0, 1.00, 1.00, 1.00
];
return { x:anchors[anchor * 2], y:anchors[anchor * 2 + 1] };
},
/**
* Private.
* Gets a given anchor position referred to the Actor.
* @param anchor
* @return an object of the form { x: float, y: float }
*/
getAnchor:function ( anchor ) {
var tx = 0, ty = 0;
switch( anchor ) {
case this.ANCHOR_CENTER:
tx = .5;
ty = .5;
break;
case this.ANCHOR_TOP:
tx = .5;
ty = 0;
break;
case this.ANCHOR_BOTTOM:
tx = .5;
ty = 1;
break;
case this.ANCHOR_LEFT:
tx = 0;
ty = .5;
break;
case this.ANCHOR_RIGHT:
tx = 1;
ty = .5;
break;
case this.ANCHOR_TOP_RIGHT:
tx = 1;
ty = 0;
break;
case this.ANCHOR_BOTTOM_LEFT:
tx = 0;
ty = 1;
break;
case this.ANCHOR_BOTTOM_RIGHT:
tx = 1;
ty = 1;
break;
case this.ANCHOR_TOP_LEFT:
tx = 0;
ty = 0;
break;
}
return {x:tx, y:ty};
},
setGlobalAnchor:function ( ax, ay ) {
this.tAnchorX = ax;
this.rotationX = ax;
this.scaleTX = ax;
this.tAnchorY = ay;
this.rotationY = ay;
this.scaleTY = ay;
this.dirty = true;
return this;
},
setScaleAnchor:function ( sax, say ) {
this.scaleTX = sax;
this.scaleTY = say;
this.dirty = true;
return this;
},
/**
* Modify the dimensions on an Actor.
* The dimension will not affect the local coordinates system in opposition
* to setSize or setBounds.
*
* @param sx {number} width scale.
* @param sy {number} height scale.
* @param anchorx {number} x anchor to perform the Scale operation.
* @param anchory {number} y anchor to perform the Scale operation.
*
* @return this;
*/
setScaleAnchored:function ( sx, sy, anchorx, anchory ) {
this.scaleTX = anchorx;
this.scaleTY = anchory;
this.scaleX = sx;
this.scaleY = sy;
this.dirty = true;
return this;
},
setRotationAnchor:function ( rax, ray ) {
this.rotationX = ray;
this.rotationY = rax;
this.dirty = true;
return this;
},
/**
* A helper method for setRotationAnchored. This methods stablishes the center
* of rotation to be the center of the Actor.
*
* @param angle a float indicating the angle in radians to rotate the Actor.
* @return this
*/
setRotation:function ( angle ) {
this.rotationAngle = angle;
this.dirty = true;
return this;
},
/**
* This method sets Actor rotation around a given position.
* @param angle {number} indicating the angle in radians to rotate the Actor.
* @param rx {number} value in the range 0..1
* @param ry {number} value in the range 0..1
* @return this;
*/
setRotationAnchored:function ( angle, rx, ry ) {
this.rotationAngle = angle;
this.rotationX = rx;
this.rotationY = ry;
this.dirty = true;
return this;
},
/**
* Sets an Actor's dimension
* @param w a float indicating Actor's width.
* @param h a float indicating Actor's height.
* @return this
*/
setSize:function ( w, h ) {
this.width = w;
this.height = h;
this.dirty = true;
return this;
},
/**
* Set location and dimension of an Actor at once.
*
* @param x{number} a float indicating Actor's x position.
* @param y{number} a float indicating Actor's y position
* @param w{number} a float indicating Actor's width
* @param h{number} a float indicating Actor's height
* @return this
*/
setBounds:function ( x, y, w, h ) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.dirty = true;
return this;
},
/**
* This method sets the position of an Actor inside its parent.
*
* @param x{number} a float indicating Actor's x position
* @param y{number} a float indicating Actor's y position
* @return this
*
* @deprecated
*/
setLocation:function ( x, y ) {
this.x = x;
this.y = y;
this.oldX = x;
this.oldY = y;
this.dirty = true;
return this;
},
setPosition:function ( x, y ) {
return this.setLocation( x, y );
},
setPositionAnchor:function ( pax, pay ) {
this.tAnchorX = pax;
this.tAnchorY = pay;
return this;
},
setPositionAnchored:function ( x, y, pax, pay ) {
this.setLocation( x, y );
this.tAnchorX = pax;
this.tAnchorY = pay;
return this;
},
/**
* This method is called by the Director to know whether the actor is on Scene time.
* In case it was necessary, this method will notify any life cycle behaviors about
* an Actor expiration.
* @param time {number} time indicating the Scene time.
*
* @private
*
*/
isInAnimationFrame:function ( time ) {
if ( this.expired ) {
return false;
}
if ( this.duration === Number.MAX_VALUE ) {
return this.start_time <= time;
}
if ( time >= this.start_time + this.duration ) {
if ( !this.expired ) {
this.setExpired( time );
}
return false;
}
return this.start_time <= time && time < this.start_time + this.duration;
},
/**
* Checks whether a coordinate is inside the Actor's bounding box.
* @param x {number} a float
* @param y {number} a float
*
* @return boolean indicating whether it is inside.
*/
contains:function ( x, y ) {
return x >= 0 && y >= 0 && x < this.width && y < this.height;
},
/**
* Add a Behavior to the Actor.
* An Actor accepts an undefined number of Behaviors.
*
* @param behavior {CAAT.Behavior} a CAAT.Behavior instance
* @return this
*/
addBehavior:function ( behavior ) {
this.behaviorList.push( behavior );
return this;
},
/**
* Remove a Behavior from the Actor.
* If the Behavior is not present at the actor behavior collection nothing happends.
*
* @param behavior {CAAT.Behavior} a CAAT.Behavior instance.
*/
removeBehaviour:function ( behavior ) {
var c = this.behaviorList;
var n = c.length - 1;
while ( n ) {
if ( c[n] === behavior ) {
c.splice( n, 1 );
return this;
}
}
return this;
},
/**
* Remove a Behavior with id param as behavior identifier from this actor.
* This function will remove ALL behavior instances with the given id.
*
* @param id {number} an integer.
* return this;
*/
removeBehaviorById:function ( id ) {
var c = this.behaviorList;
for ( var n = 0; n < c.length; n++ ) {
if ( c[n].id === id ) {
c.splice( n, 1 );
}
}
return this;
},
getBehavior:function ( id ) {
var c = this.behaviorList;
for ( var n = 0; n < c.length; n++ ) {
var cc = c[n];
if ( cc.id === id ) {
return cc;
}
}
return null;
},
/**
* Set discardable property. If an actor is discardable, upon expiration will be removed from
* scene graph and hence deleted.
* @param discardable {boolean} a boolean indicating whether the Actor is discardable.
* @return this
*/
setDiscardable:function ( discardable ) {
this.discardable = discardable;
return this;
},
/**
* This method will be called internally by CAAT when an Actor is expired, and at the
* same time, is flagged as discardable.
* It notifies the Actor life cycle listeners about the destruction event.
*
* @param time an integer indicating the time at wich the Actor has been destroyed.
*
* @private
*
*/
destroy:function ( time ) {
if ( this.parent ) {
this.parent.removeChild( this );
}
this.fireEvent( 'destroyed', time );
},
/**
* Transform a point or array of points in model space to view space.
*
* @param point {CAAT.Point|Array} an object of the form {x : float, y: float}
*
* @return the source transformed elements.
*
* @private
*
*/
modelToView:function ( point ) {
var x, y, pt, tm;
if ( this.dirty ) {
this.setModelViewMatrix();
}
tm = this.worldModelViewMatrix.matrix;
if ( point instanceof Array ) {
for ( var i = 0; i < point.length; i++ ) {
//this.worldModelViewMatrix.transformCoord(point[i]);
pt = point[i];
x = pt.x;
y = pt.y;
pt.x = x * tm[0] + y * tm[1] + tm[2];
pt.y = x * tm[3] + y * tm[4] + tm[5];
}
}
else {
// this.worldModelViewMatrix.transformCoord(point);
x = point.x;
y = point.y;
point.x = x * tm[0] + y * tm[1] + tm[2];
point.y = x * tm[3] + y * tm[4] + tm[5];
}
return point;
},
/**
* Transform a local coordinate point on this Actor's coordinate system into
* another point in otherActor's coordinate system.
* @param point {CAAT.Point}
* @param otherActor {CAAT.Actor}
*/
modelToModel:function ( point, otherActor ) {
if ( this.dirty ) {
this.setModelViewMatrix();
}
return otherActor.viewToModel( this.modelToView( point ) );
},
/**
* Transform a point from model to view space.
* CAAT.Path
.
*
* @constructor
* @extends CAAT.ActorContainer
*
*/
CAAT.TextActor = function () {
CAAT.TextActor.superclass.constructor.call( this );
this.font = "10px sans-serif";
this.textAlign = "left";
this.outlineColor = "black";
this.clip = false;
this.__calcFontData();
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".
fontData:null,
textAlign:null, // a valid canvas rendering context textAlign string. Any of:
// start, end, left, right, center.
// defaults to "left".
textBaseline:"top", // 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.
lineWidth:1, // text's stroke line width.
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.stopCacheAsBitmap();
this.fill = fill;
return this;
},
setLineWidth:function ( lw ) {
this.stopCacheAsBitmap();
this.lineWidth = lw;
return this;
},
setTextFillStyle:function ( style ) {
this.stopCacheAsBitmap();
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.stopCacheAsBitmap();
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.stopCacheAsBitmap();
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.stopCacheAsBitmap();
this.text = sText;
if ( null === this.text || this.text === "" ) {
this.width = this.height = 0;
}
this.calcTextSize( CAAT.currentDirector );
this.invalidate();
return this;
},
setTextAlign:function ( align ) {
this.textAlign = align;
this.__setLocation();
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.stopCacheAsBitmap();
this.textBaseline = baseline;
return this;
},
setBaseline:function ( baseline ) {
this.stopCacheAsBitmap();
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 ) {
this.stopCacheAsBitmap();
if ( !font ) {
font = "10px sans-serif";
}
if ( font instanceof CAAT.Font ) {
font.setAsSpriteImage();
}
else if ( font instanceof CAAT.SpriteImage ) {
CAAT.log( "WARN: setFont will no more accept a CAAT.SpriteImage as argument." );
}
this.font = font;
this.__calcFontData();
this.calcTextSize( CAAT.director[0] );
return this;
},
setLocation:function ( x, y ) {
this.lx = x;
this.ly = y;
this.__setLocation();
return this;
},
setPosition:function ( x, y ) {
this.lx = x;
this.ly = y;
this.__setLocation();
return this;
},
setBounds:function ( x, y, w, h ) {
this.lx = x;
this.ly = y;
this.setSize( w, h );
this.__setLocation();
return this;
},
setSize:function ( w, h ) {
CAAT.TextActor.superclass.setSize.call( this, w, h );
this.__setLocation();
return this;
},
/**
* @private
*/
__setLocation:function () {
var nx, ny;
if ( this.textAlign === "center" ) {
nx = this.lx - this.width / 2;
}
else if ( this.textAlign === "right" || this.textAlign === "end" ) {
nx = this.lx - this.width;
}
else {
nx = this.lx;
}
if ( this.textBaseline === "bottom" ) {
ny = this.ly - this.height;
}
else if ( this.textBaseline === "middle" ) {
ny = this.ly - this.height / 2;
}
else if ( this.textBaseline === "alphabetic" ) {
ny = this.ly - this.fontData.ascent;
}
else {
ny = this.ly;
}
CAAT.TextActor.superclass.setLocation.call( this, nx, ny );
},
centerAt:function ( x, y ) {
this.textAlign = "left";
return CAAT.TextActor.superclass.centerAt.call( this, x, y );
},
/**
* 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;
var as = (this.font.singleHeight * .8) >> 0;
this.fontData = {
height:this.font.singleHeight,
ascent:as,
descent:this.font.singleHeight - as
};
return this;
}
if ( this.font instanceof CAAT.Font ) {
this.textWidth = this.font.stringWidth( this.text );
this.textHeight = this.font.stringHeight();
this.width = this.textWidth;
this.height = this.textHeight;
this.fontData = this.font.getFontData();
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;
}
/*
var pos= this.font.indexOf("px");
if (-1===pos) {
pos= this.font.indexOf("pt");
}
if ( -1===pos ) {
// no pt or px, so guess a size: 32. why not ?
this.textHeight= 32;
} else {
var s = this.font.substring(0, pos );
this.textHeight= parseInt(s,10);
}
*/
this.textHeight = this.fontData.height;
this.setSize( this.textWidth, this.textHeight );
ctx.restore();
return this;
},
__calcFontData:function () {
this.fontData = CAAT.Font.getFontMetrics( this.font );
},
/**
* 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.Font || this.font instanceof CAAT.SpriteImage ) {
return this.drawSpriteText( director, time );
}
if ( null !== this.font ) {
ctx.font = this.font;
}
/**
* always draw text with middle or bottom, top is buggy in FF.
* @type {String}
*/
ctx.textBaseline = "alphabetic";
if ( null === this.path ) {
if ( null !== this.textAlign ) {
ctx.textAlign = this.textAlign;
}
var tx = 0;
if ( this.textAlign === 'center' ) {
tx = (this.width / 2) | 0;
}
else if ( this.textAlign === 'right' ) {
tx = this.width;
}
if ( this.fill ) {
if ( null !== this.textFillStyle ) {
ctx.fillStyle = this.textFillStyle;
}
ctx.fillText( this.text, tx, this.fontData.ascent );
}
if ( this.outline ) {
if ( null !== this.outlineColor ) {
ctx.strokeStyle = this.outlineColor;
}
ctx.lineWidth = this.lineWidth;
ctx.beginPath();
ctx.strokeText( this.text, tx, this.fontData.ascent );
}
}
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;
if ( this.fill && null !== this.textFillStyle ) {
ctx.fillStyle = this.textFillStyle;
}
if ( this.outline && null !== this.outlineColor ) {
ctx.strokeStyle = this.outlineColor;
}
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; i < this.text.length; i++ ) {
var caracter = this.text[i].toString();
var charWidth = ctx.measureText( caracter ).width;
// guonjien: remove "+charWidth/2" since it destroys the kerning. and he's right!!!. thanks.
var currentCurveLength = textWidth;
p0 = this.path.getPositionFromLength( currentCurveLength ).clone();
p1 = this.path.getPositionFromLength( currentCurveLength - 0.1 ).clone();
var angle = Math.atan2( p0.y - p1.y, p0.x - p1.x );
ctx.save();
ctx.translate( p0.x >> 0, p0.y >> 0 );
ctx.rotate( angle );
if ( this.fill ) {
ctx.fillText( caracter, 0, 0 );
}
if ( this.outline ) {
ctx.beginPath();
ctx.lineWidth = this.lineWidth;
ctx.strokeText( caracter, 0, 0 );
}
ctx.restore();
textWidth += charWidth;
}
},
/**
* Private.
* Draw the text using a sprited font instead of a canvas font.
* @param director a valid CAAT.Director instance.
* @param time an integer with the Scene time the Actor is being drawn.
*/
drawSpriteText:function ( director, time ) {
if ( null === this.path ) {
this.font.drawText( this.text, director.ctx, 0, 0 );
}
else {
this.drawSpriteTextOnPath( director, time );
}
},
/**
* Private.
* Draw the text traversing a path using a sprited font.
* @param director a valid CAAT.Director instance.
* @param time an integer with the Scene time the Actor is being drawn.
*/
drawSpriteTextOnPath:function ( director, time ) {
var context = 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; i < this.text.length; i++ ) {
var character = this.text[i].toString();
var charWidth = this.font.stringWidth( character );
var pathLength = this.path.getLength();
var currentCurveLength = charWidth / 2 + textWidth;
p0 = this.path.getPositionFromLength( currentCurveLength ).clone();
p1 = this.path.getPositionFromLength( currentCurveLength - 0.1 ).clone();
var angle = Math.atan2( p0.y - p1.y, p0.x - p1.x );
context.save();
context.translate( p0.x | 0, p0.y | 0 );
context.rotate( angle );
var y = this.textBaseline === "bottom" ? 0 - this.font.getHeight() : 0;
this.font.drawString( context, character, 0, y );
context.restore();
textWidth += charWidth;
}
},
/**
* Set the path, interpolator and duration to draw the text on.
* @param path a valid CAAT.Path instance.
* @param interpolator a CAAT.Interpolator object. If not set, a Linear Interpolator will be used.
* @param duration an integer indicating the time to take to traverse the path. Optional. 10000 ms
* by default.
*/
setPath:function ( path, interpolator, duration ) {
this.path = path;
this.pathInterpolator = interpolator || new CAAT.Interpolator().createLinearInterpolator();
this.pathDuration = duration || 10000;
/*
parent could not be set by the time this method is called.
so the actors bounds set is removed.
the developer must ensure to call setbounds properly on actor.
*/
this.mouseEnabled = false;
return this;
}
};
extend( CAAT.TextActor, CAAT.Actor, null );
})();
(function () {
/**
* This Actor draws common shapes, concretely Circles and rectangles.
*
* @constructor
* @extends CAAT.ActorContainer
*/
CAAT.ShapeActor = function () {
CAAT.ShapeActor.superclass.constructor.call( this );
this.compositeOp = 'source-over';
/**
* Thanks Svend Dutz and Thomas Karolski for noticing this call was not performed by default,
* so if no explicit call to setShape was made, nothing would be drawn.
*/
this.setShape( this.SHAPE_CIRCLE );
return this;
};
CAAT.ShapeActor.prototype = {
shape:0, // shape type. One of the constant SHAPE_* values
compositeOp:null, // a valid canvas rendering context string describing compositeOps.
lineWidth:1,
lineCap:null,
lineJoin:null,
miterLimit:null,
SHAPE_CIRCLE:0, // Constants to describe different shapes.
SHAPE_RECTANGLE:1,
/**
*
* @param l {number>0}
*/
setLineWidth:function ( l ) {
this.lineWidth = l;
return this;
},
/**
*
* @param lc {string{butt|round|square}}
*/
setLineCap:function ( lc ) {
this.lineCap = lc;
return this;
},
/**
*
* @param lj {string{bevel|round|miter}}
*/
setLineJoin:function ( lj ) {
this.lineJoin = lj;
return this;
},
/**
*
* @param ml {integer>0}
*/
setMiterLimit:function ( ml ) {
this.miterLimit = ml;
return this;
},
getLineCap:function () {
return this.lineCap;
},
getLineJoin:function () {
return this.lineJoin;
},
getMiterLimit:function () {
return this.miterLimit;
},
getLineWidth:function () {
return this.lineWidth;
},
/**
* Sets shape type.
* No check for parameter validity is performed.
* Set paint method according to the shape.
* @param iShape an integer with any of the SHAPE_* constants.
* @return this
*/
setShape:function ( iShape ) {
this.shape = iShape;
this.paint = this.shape === this.SHAPE_CIRCLE ?
this.paintCircle :
this.paintRectangle;
return this;
},
/**
* Sets the composite operation to apply on shape drawing.
* @param compositeOp an string with a valid canvas rendering context string describing compositeOps.
* @return this
*/
setCompositeOp:function ( compositeOp ) {
this.compositeOp = compositeOp;
return this;
},
/**
* Draws the shape.
* Applies the values of fillStype, strokeStyle, compositeOp, etc.
*
* @param director a valid CAAT.Director instance.
* @param time an integer with the Scene time the Actor is being drawn.
*/
paint:function ( director, time ) {
},
/**
* @private
* Draws a circle.
* @param director a valid CAAT.Director instance.
* @param time an integer with the Scene time the Actor is being drawn.
*/
paintCircle:function ( director, time ) {
if ( this.cached ) {
CAAT.Actor.prototype.paint.call( this, director, time );
return;
}
var ctx = director.crc;
ctx.lineWidth = this.lineWidth;
ctx.globalCompositeOperation = this.compositeOp;
if ( null !== this.fillStyle ) {
ctx.fillStyle = this.fillStyle;
ctx.beginPath();
ctx.arc( this.width / 2, this.height / 2, Math.min( this.width, this.height ) / 2 - this.lineWidth / 2, 0, 2 * Math.PI, false );
ctx.fill();
}
if ( null !== this.strokeStyle ) {
ctx.strokeStyle = this.strokeStyle;
ctx.beginPath();
ctx.arc( this.width / 2, this.height / 2, Math.min( this.width, this.height ) / 2 - this.lineWidth / 2, 0, 2 * Math.PI, false );
ctx.stroke();
}
},
/**
*
* Private
* Draws a Rectangle.
*
* @param director a valid CAAT.Director instance.
* @param time an integer with the Scene time the Actor is being drawn.
*/
paintRectangle:function ( director, time ) {
if ( this.cached ) {
CAAT.Actor.prototype.paint.call( this, director, time );
return;
}
var ctx = director.crc;
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;
if ( null !== this.fillStyle ) {
ctx.fillStyle = this.fillStyle;
ctx.beginPath();
ctx.fillRect( 0, 0, this.width, this.height );
ctx.fill();
}
if ( null !== this.strokeStyle ) {
ctx.strokeStyle = this.strokeStyle;
ctx.beginPath();
ctx.strokeRect( 0, 0, this.width, this.height );
ctx.stroke();
}
}
};
extend( CAAT.ShapeActor, CAAT.ActorContainer, null );
})();
(function () {
/**
* This actor draws stars.
*
* @constructor
* @extends CAAT.ActorContainer
*/
CAAT.StarActor = function () {
CAAT.StarActor.superclass.constructor.call( this );
this.compositeOp = 'source-over';
return this;
};
CAAT.StarActor.prototype = {
nPeaks:0,
maxRadius:0,
minRadius:0,
initialAngle:0,
compositeOp:null,
lineWidth:1,
lineCap:null,
lineJoin:null,
miterLimit:null,
/**
*
* @param l {number>0}
*/
setLineWidth:function ( l ) {
this.lineWidth = l;
return this;
},
/**
*
* @param lc {string{butt|round|square}}
*/
setLineCap:function ( lc ) {
this.lineCap = lc;
return this;
},
/**
*
* @param lj {string{bevel|round|miter}}
*/
setLineJoin:function ( lj ) {
this.lineJoin = lj;
return this;
},
/**
*
* @param ml {integer>0}
*/
setMiterLimit:function ( ml ) {
this.miterLimit = ml;
return this;
},
getLineCap:function () {
return this.lineCap;
},
getLineJoin:function () {
return this.lineJoin;
},
getMiterLimit:function () {
return this.miterLimit;
},
getLineWidth:function () {
return this.lineWidth;
},
/**
* Sets whether the star will be color filled.
* @param filled {boolean}
* @deprecated
*/
setFilled:function ( filled ) {
return this;
},
/**
* Sets whether the star will be outlined.
* @param outlined {boolean}
* @deprecated
*/
setOutlined:function ( outlined ) {
return this;
},
/**
* Sets the composite operation to apply on shape drawing.
* @param compositeOp an string with a valid canvas rendering context string describing compositeOps.
* @return this
*/
setCompositeOp:function ( compositeOp ) {
this.compositeOp = compositeOp;
return this;
},
/**
*
* @param angle {number} number in radians.
*/
setInitialAngle:function ( angle ) {
this.initialAngle = angle;
return this;
},
/**
* Initialize the star values.
* addSound
* 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; i < array_of_url_or_domnodes.length; i++ ) {
if ( this.addAudioElement( id, array_of_url_or_domnodes[i], endplaying_callback ) ) {
break;
}
}
}
else {
this.addAudioElement( id, array_of_url_or_domnodes, endplaying_callback );
}
return this;
},
/**
* Returns an audio object.
* @param aId {object} the id associated to the target Audio object.
* @return {object} the HTMLAudioElement addociated to the given id.
*/
getAudio:function ( aId ) {
for ( var i = 0; i < this.audioCache.length; i++ ) {
if ( this.audioCache[i].id === aId ) {
return this.audioCache[i].audio;
}
}
return null;
},
/**
* Set an audio object volume.
* @param id {object} an audio Id
* @param volume {number} volume to set. The volume value is not checked.
*
* @return this
*/
setVolume:function ( id, volume ) {
var audio = this.getAudio( id );
if ( null != audio ) {
audio.volume = volume;
}
return this;
},
/**
* Plays an audio file from the cache if any sound channel is available.
* The playing sound will occupy a sound channel and when ends playing will leave
* the channel free for any other sound to be played in.
* @param id {object} an object identifying a sound in the sound cache.
* @return this.
*/
play:function ( id ) {
if ( !this.fxEnabled ) {
return this;
}
var audio = this.getAudio( id );
// existe el audio, y ademas hay un canal de audio disponible.
if ( null !== audio && this.channels.length > 0 ) {
var channel = this.channels.shift();
channel.src = audio.src;
channel.load();
channel.volume = audio.volume;
channel.play();
this.workingChannels.push( channel );
}
return this;
},
/**
* This method creates a new AudioChannel to loop the sound with.
* It returns an Audio object so that the developer can cancel the sound loop at will.
* The user must call pause()
method to stop playing a loop.
*
*
* By default, the layou operation is OP_LAYOUT_BOTTOM, that is, elements zoom bottom anchored.
*
* @param lo {number} one of CAAT.Dock.OP_LAYOUT_BOTTOM, CAAT.Dock.OP_LAYOUT_TOP,
* CAAT.Dock.OP_LAYOUT_BOTTOM, CAAT.Dock.OP_LAYOUT_RIGHT.
*
* @return this
*/
setLayoutOp:function ( lo ) {
this.layoutOp = lo;
return this;
},
/**
*
* Set maximum and minimum size of docked elements. By default, every contained actor will be
* of 'min' size, and will be scaled up to 'max' size.
*
* @param min {number}
* @param max {number}
* @return this
*/
setSizes:function ( min, max ) {
this.minSize = min;
this.maxSize = max;
for ( var i = 0; i < this.childrenList.length; i++ ) {
this.childrenList[i].width = min;
this.childrenList[i].height = min;
}
return this;
},
/**
* Lay out the docking elements. The lay out will be a row with the orientation set by calling
* the method setLayoutOp
.
*
* @private
*/
layout:function () {
var i, actor;
if ( this.layoutOp === this.OP_LAYOUT_BOTTOM || this.layoutOp === this.OP_LAYOUT_TOP ) {
var currentWidth = 0, currentX = 0;
for ( i = 0; i < this.getNumChildren(); i++ ) {
currentWidth += this.getChildAt( i ).width;
}
currentX = (this.width - currentWidth) / 2;
for ( i = 0; i < this.getNumChildren(); i++ ) {
actor = this.getChildAt( i );
actor.x = currentX;
currentX += actor.width;
if ( this.layoutOp === this.OP_LAYOUT_BOTTOM ) {
actor.y = this.maxSize - actor.height;
}
else {
actor.y = 0;
}
}
}
else {
var currentHeight = 0, currentY = 0;
for ( i = 0; i < this.getNumChildren(); i++ ) {
currentHeight += this.getChildAt( i ).height;
}
currentY = (this.height - currentHeight) / 2;
for ( i = 0; i < this.getNumChildren(); i++ ) {
actor = this.getChildAt( i );
actor.y = currentY;
currentY += actor.height;
if ( this.layoutOp === this.OP_LAYOUT_LEFT ) {
actor.x = 0;
}
else {
actor.x = this.width - actor.width;
}
}
}
},
mouseMove:function ( mouseEvent ) {
this.actorNotPointed();
},
mouseExit:function ( mouseEvent ) {
this.actorNotPointed();
},
/**
* Performs operation when the mouse is not in the dock element.
*
* @private
*/
actorNotPointed:function () {
var i;
var me = this;
for ( i = 0; i < this.getNumChildren(); i++ ) {
var actor = this.getChildAt( i );
actor.emptyBehaviorList();
actor.addBehavior(
new CAAT.GenericBehavior().
setValues( actor.width, this.minSize, actor, 'width' ).
setFrameTime( this.scene.time, 250 ) ).
addBehavior(
new CAAT.GenericBehavior().
setValues( actor.height, this.minSize, actor, 'height' ).
setFrameTime( this.scene.time, 250 ) );
if ( i === this.getNumChildren() - 1 ) {
actor.behaviorList[0].addListener(
{
behaviorApplied:function ( behavior, time, normalizedTime, targetActor, value ) {
targetActor.parent.layout();
},
behaviorExpired:function ( behavior, time, targetActor ) {
for ( i = 0; i < me.getNumChildren(); i++ ) {
actor = me.getChildAt( i );
actor.width = me.minSize;
actor.height = me.minSize;
}
targetActor.parent.layout();
}
} );
}
}
},
/**
*
* Perform the process of pointing a docking actor.
*
* @param x {number}
* @param y {number}
* @param pointedActor {CAAT.Actor}
*
* @private
*/
actorPointed:function ( x, y, pointedActor ) {
var index = this.findChild( pointedActor );
var across = 0;
if ( this.layoutOp === this.OP_LAYOUT_BOTTOM || this.layoutOp === this.OP_LAYOUT_TOP ) {
across = x / pointedActor.width;
}
else {
across = y / pointedActor.height;
}
var i;
for ( i = 0; i < this.childrenList.length; i++ ) {
var actor = this.childrenList[i];
actor.emptyBehaviorList();
var wwidth = 0;
if ( i < index - this.range || i > index + this.range ) {
wwidth = this.minSize;
}
else if ( i === index ) {
wwidth = this.maxSize;
}
else if ( i < index ) {
wwidth =
this.minSize +
(this.maxSize - this.minSize) *
(Math.cos( (i - index - across + 1) / this.range * Math.PI ) + 1) /
2;
}
else {
wwidth =
this.minSize +
(this.maxSize - this.minSize) *
(Math.cos( (i - index - across) / this.range * Math.PI ) + 1) /
2;
}
actor.height = wwidth;
actor.width = wwidth;
}
this.layout();
},
/**
* Perform the process of exiting the docking element, that is, animate elements to the minimum
* size.
*
* @param mouseEvent {CAAT.MouseEvent} a CAAT.MouseEvent object.
*
* @private
*/
actorMouseExit:function ( mouseEvent ) {
if ( null !== this.ttask ) {
this.ttask.cancel();
}
var me = this;
this.ttask = this.scene.createTimer(
this.scene.time,
100,
function timeout( sceneTime, time, timerTask ) {
me.actorNotPointed();
},
null,
null );
},
/**
* Perform the beginning of docking elements.
* @param mouseEvent {CAAT.MouseEvent} a CAAT.MouseEvent object.
*
* @private
*/
actorMouseEnter:function ( mouseEvent ) {
if ( null !== this.ttask ) {
this.ttask.cancel();
this.ttask = null;
}
},
/**
* Adds an actor to Dock.
*
*
*
* director.addImage(id,image,false)
many times and a final call with
* 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.statistics.size_discarded_by_dirty_rects = 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 ) {
if ( this.currentScene && this.currentScene.isPaused() ) {
return;
}
this.time += time;
this.animate( this, 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; i < dr.length; i++ ) {
var drr = dr[i];
if ( !drr.isEmpty() ) {
ctx.rect( drr.x | 0, drr.y | 0, 1 + (drr.width | 0), 1 + (drr.height | 0) );
this.nDirtyRects++;
}
}
ctx.clip();
}
else {
ctx.clearRect( 0, 0, this.width, this.height );
}
}
else if ( this.clear === true ) {
ctx.clearRect( 0, 0, this.width, this.height );
}
for ( i = 0; i < ne; i++ ) {
c = this.childrenList[i];
if ( c.isInAnimationFrame( this.time ) ) {
tt = c.time - c.start_time;
ctx.save();
if ( c.onRenderStart ) {
c.onRenderStart( tt );
}
if ( !CAAT.DEBUG_DIRTYRECTS && this.dirtyRectsEnabled ) {
if ( this.nDirtyRects ) {
c.paintActor( this, tt );
}
}
else {
c.paintActor( this, tt );
}
if ( c.onRenderEnd ) {
c.onRenderEnd( tt );
}
ctx.restore();
if ( CAAT.DEBUGAABB ) {
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = 'source-over';
this.modelViewMatrix.transformRenderingContextSet( ctx );
c.drawScreenBoundingBox( this, 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.statistics.size_dirtyRects = this.nDirtyRects;
}
}
}
if ( this.nDirtyRects > 0 && CAAT.DEBUG && CAAT.DEBUG_DIRTYRECTS ) {
ctx.beginPath();
this.nDirtyRects = 0;
var dr = this.cDirtyRects;
for ( i = 0; i < dr.length; i++ ) {
var drr = dr[i];
if ( !drr.isEmpty() ) {
ctx.rect( drr.x | 0, drr.y | 0, 1 + (drr.width | 0), 1 + (drr.height | 0) );
this.nDirtyRects++;
}
}
ctx.clip();
ctx.fillStyle = 'rgba(160,255,150,.4)';
ctx.fillRect( 0, 0, this.width, this.height );
}
ctx.restore();
}
this.frameCounter++;
},
inDirtyRect:function ( actor ) {
if ( !this.dirtyRectsEnabled || CAAT.DEBUG_DIRTYRECTS ) {
return true;
}
var dr = this.cDirtyRects;
var i;
var aabb = actor.AABB;
for ( i = 0; i < dr.length; i++ ) {
if ( dr[i].intersects( aabb ) ) {
return true;
}
}
this.statistics.size_discarded_by_dirty_rects += actor.size_total;
return false;
},
/**
* A director is a very special kind of actor.
* Its animation routine simple sets its modelViewMatrix in case some transformation's been
* applied.
* No behaviors are allowed for Director instances.
* @param director {CAAT.Director} redundant reference to CAAT.Director itself
* @param time {number} director time.
*/
animate:function ( director, time ) {
this.timerManager.checkTimers( time );
this.setModelViewMatrix( this );
this.modelViewMatrixI = this.modelViewMatrix.getInverse();
this.setScreenBounds();
this.dirty = false;
this.invalid = false;
this.dirtyRectsIndex = -1;
this.cDirtyRects = [];
var cl = this.childrenList;
var cli;
var i, l;
if ( this.dirtyRectsEnabled ) {
var sdr = this.sDirtyRects;
if ( sdr.length ) {
for ( i = 0, l = sdr.length; i < l; i++ ) {
this.addDirtyRect( sdr[i] );
}
this.sDirtyRects = [];
}
}
for ( i = 0; i < cl.length; i++ ) {
cli = cl[i];
var tt = cli.time - cli.start_time;
cli.animate( this, tt );
}
this.timerManager.removeExpiredTimers();
return this;
},
/**
* This method is used when asynchronous operations must produce some dirty rectangle painting.
* This means that every operation out of the regular CAAT loop must add dirty rect operations
* by calling this method.
* For example setVisible() and remove.
* @param rectangle
*/
scheduleDirtyRect:function ( rectangle ) {
this.sDirtyRects.push( rectangle );
},
/**
* Add a rectangle to the list of dirty screen areas which should be redrawn.
* This is the opposite method to clear the whole screen and repaint everything again.
* Despite i'm not very fond of dirty rectangles because it needs some extra calculations, this
* procedure has shown to be speeding things up under certain situations. Nevertheless it doesn't or
* even lowers performance under others, so it is a developer choice to activate them via a call to
* setClear( CAAT.Director.CLEAR_DIRTY_RECTS ).
*
* This function, not only tracks a list of dirty rectangles, but tries to optimize the list. Overlapping
* rectangles will be removed and intersecting ones will be unioned.
*
* Before calling this method, check if this.dirtyRectsEnabled is true.
*
* @param rectangle {CAAT.Rectangle}
*/
addDirtyRect:function ( rectangle ) {
if ( rectangle.isEmpty() ) {
return;
}
var i, dr, j, drj;
var cdr = this.cDirtyRects;
for ( i = 0; i < cdr.length; i++ ) {
dr = cdr[i];
if ( !dr.isEmpty() && dr.intersects( rectangle ) ) {
var intersected = true;
while ( intersected ) {
dr.unionRectangle( rectangle );
for ( j = 0; j < cdr.length; j++ ) {
if ( j !== i ) {
drj = cdr[j];
if ( !drj.isEmpty() && drj.intersects( dr ) ) {
dr.unionRectangle( drj );
drj.setEmpty();
break;
}
}
}
if ( j == cdr.length ) {
intersected = false;
}
}
for ( j = 0; j < cdr.length; j++ ) {
if ( cdr[j].isEmpty() ) {
cdr.splice( j, 1 );
}
}
return;
}
}
this.dirtyRectsIndex++;
if ( this.dirtyRectsIndex >= this.dirtyRects.length ) {
for ( i = 0; i < 32; i++ ) {
this.dirtyRects.push( new CAAT.Rectangle() );
}
}
var r = this.dirtyRects[ this.dirtyRectsIndex ];
r.x = rectangle.x;
r.y = rectangle.y;
r.x1 = rectangle.x1;
r.y1 = rectangle.y1;
r.width = rectangle.width;
r.height = rectangle.height;
this.cDirtyRects.push( r );
},
/**
* This method draws an Scene to an offscreen canvas. This offscreen canvas is also a child of
* another Scene (transitionScene). So instead of drawing two scenes while transitioning from
* one to another, first of all an scene is drawn to offscreen, and that image is translated.
* canvas.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.
*
* var si= new CAAT.SpriteImage().
* initialize( an_image_instance, rows, columns ).
* setAnimationImageIndex( [2,1,0,1] ). // cycle throwout image with these indexes
* setChangeFPS( 200 ). // change sprite every 200 ms.
* setSpriteTransformation( CAAT.SpriteImage.TR_xx); // optionally draw images inverted, ...
*
*
* A SpriteImage is an sprite sheet. It encapsulates an Image and treates and references it as a two
* dimensional array of row by columns sub-images. The access form will be sequential so if defined a
* CompoundImage of more than one row, the subimages will be referenced by an index ranging from 0 to
* rows*columns-1. Each sumimage will be of size (image.width/columns) by (image.height/rows).
*
*
*
*
* 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;
},
getWrappedImageWidth:function () {
return this.image.width;
},
getWrappedImageHeight:function () {
return this.image.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 < nh; i++ ) {
for ( j = 0; j < nw; j++ ) {
ctx.drawImage(
this.image,
el.x, el.y,
el.width, el.height,
(r.x - this.ownerActor.x + xoff + j * el.width) >> 0, (r.y - this.ownerActor.y + yoff + i * el.height) >> 0,
el.width, el.height );
}
}
},
/**
* Draws the subimage pointed by imageIndex horizontally inverted.
* @param canvas a canvas context.
* @param imageIndex {number} a subimage index.
* @param x {number} x position in canvas to draw the image.
* @param y {number} y position in canvas to draw the image.
*
* @return this
*/
paintInvertedH:function ( director, time, x, y ) {
this.setSpriteIndexAtTime( time );
var el = this.mapInfo[this.spriteIndex];
var ctx = director.ctx;
ctx.save();
//ctx.translate(((0.5 + x) | 0) + el.width, (0.5 + y) | 0);
ctx.translate( (x | 0) + el.width, y | 0 );
ctx.scale( -1, 1 );
ctx.drawImage(
this.image,
el.x, el.y,
el.width, el.height,
this.offsetX >> 0, this.offsetY >> 0,
el.width, el.height );
ctx.restore();
return this;
},
/**
* Draws the subimage pointed by imageIndex vertically inverted.
* @param canvas a canvas context.
* @param imageIndex {number} a subimage index.
* @param x {number} x position in canvas to draw the image.
* @param y {number} y position in canvas to draw the image.
*
* @return this
*/
paintInvertedV:function ( director, time, x, y ) {
this.setSpriteIndexAtTime( time );
var el = this.mapInfo[this.spriteIndex];
var ctx = director.ctx;
ctx.save();
//ctx.translate((x + 0.5) | 0, (0.5 + y + el.height) | 0);
ctx.translate( x | 0, (y + el.height) | 0 );
ctx.scale( 1, -1 );
ctx.drawImage(
this.image,
el.x, el.y,
el.width, el.height,
this.offsetX >> 0, this.offsetY >> 0,
el.width, el.height );
ctx.restore();
return this;
},
/**
* Draws the subimage pointed by imageIndex both horizontal and vertically inverted.
* @param canvas a canvas context.
* @param imageIndex {number} a subimage index.
* @param x {number} x position in canvas to draw the image.
* @param y {number} y position in canvas to draw the image.
*
* @return this
*/
paintInvertedHV:function ( director, time, x, y ) {
this.setSpriteIndexAtTime( time );
var el = this.mapInfo[this.spriteIndex];
var ctx = director.ctx;
ctx.save();
//ctx.translate((x + 0.5) | 0, (0.5 + y + el.height) | 0);
ctx.translate( x | 0, (y + el.height) | 0 );
ctx.scale( 1, -1 );
ctx.translate( el.width, 0 );
ctx.scale( -1, 1 );
ctx.drawImage(
this.image,
el.x, el.y,
el.width, el.height,
this.offsetX >> 0, this.offsetY >> 0,
el.width, el.height );
ctx.restore();
return this;
},
/**
* Draws the subimage pointed by imageIndex.
* @param canvas a canvas context.
* @param imageIndex {number} a subimage index.
* @param x {number} x position in canvas to draw the image.
* @param y {number} y position in canvas to draw the image.
*
* @return this
*/
paintN:function ( director, time, x, y ) {
this.setSpriteIndexAtTime( time );
var el = this.mapInfo[this.spriteIndex];
director.ctx.drawImage(
this.image,
el.x, el.y,
el.width, el.height,
(this.offsetX + x) >> 0, (this.offsetY + y) >> 0,
el.width, el.height );
return this;
},
paintChunk:function ( ctx, dx, dy, x, y, w, h ) {
ctx.drawImage( this.image, x, y, w, h, dx, dy, w, h );
},
paintTile:function ( ctx, index, x, y ) {
var el = this.mapInfo[index];
ctx.drawImage(
this.image,
el.x, el.y,
el.width, el.height,
(this.offsetX + x) >> 0, (this.offsetY + y) >> 0,
el.width, el.height );
return this;
},
/**
* Draws the subimage pointed by imageIndex scaled to the size of w and h.
* @param canvas a canvas context.
* @param imageIndex {number} a subimage index.
* @param x {number} x position in canvas to draw the image.
* @param y {number} y position in canvas to draw the image.
* @param w {number} new width of the subimage.
* @param h {number} new height of the subimage.
*
* @return this
*/
paintScaled:function ( director, time, x, y ) {
this.setSpriteIndexAtTime( time );
var el = this.mapInfo[this.spriteIndex];
director.ctx.drawImage(
this.image,
el.x, el.y,
el.width, el.height,
(this.offsetX + x) >> 0, (this.offsetY + y) >> 0,
this.ownerActor.width, this.ownerActor.height );
return this;
},
getCurrentSpriteImageCSSPosition:function () {
var el = this.mapInfo[this.spriteIndex];
var x = -(el.x - this.offsetX);
var y = -(el.y - this.offsetY);
return '' + x + 'px ' +
y + 'px ' +
(this.ownerActor.transformation === this.TR_TILE ? 'repeat' : 'no-repeat');
},
/**
* Get the number of subimages in this compoundImage
* @return {number}
*/
getNumImages:function () {
return this.rows * this.columns;
},
/**
* TODO: set mapping coordinates for different transformations.
* @param imageIndex
* @param uvBuffer
* @param uvIndex
*/
setUV:function ( uvBuffer, uvIndex ) {
var im = this.image;
if ( !im.__texturePage ) {
return;
}
var index = uvIndex;
var sIndex = this.spriteIndex;
var el = this.mapInfo[this.spriteIndex];
var u = el.u;
var v = el.v;
var u1 = el.u1;
var v1 = el.v1;
if ( this.offsetX || this.offsetY ) {
var w = this.ownerActor.width;
var h = this.ownerActor.height;
var tp = im.__texturePage;
var _u = -this.offsetX / tp.width;
var _v = -this.offsetY / tp.height;
var _u1 = (w - this.offsetX) / tp.width;
var _v1 = (h - this.offsetY) / tp.height;
u = _u + im.__u;
v = _v + im.__v;
u1 = _u1 + im.__u;
v1 = _v1 + im.__v;
}
if ( im.inverted ) {
uvBuffer[index++] = u1;
uvBuffer[index++] = v;
uvBuffer[index++] = u1;
uvBuffer[index++] = v1;
uvBuffer[index++] = u;
uvBuffer[index++] = v1;
uvBuffer[index++] = u;
uvBuffer[index++] = v;
}
else {
uvBuffer[index++] = u;
uvBuffer[index++] = v;
uvBuffer[index++] = u1;
uvBuffer[index++] = v;
uvBuffer[index++] = u1;
uvBuffer[index++] = v1;
uvBuffer[index++] = u;
uvBuffer[index++] = v1;
}
},
/**
* Set the elapsed time needed to change the image index.
* @param fps an integer indicating the time in milliseconds to change.
* @return this
*/
setChangeFPS:function ( fps ) {
this.changeFPS = fps;
return this;
},
/**
* Set the transformation to apply to the Sprite image.
* Any value of
*
*
*
* 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.beginPath();
ctx.arc(
x,
y,
CAAT.Curve.prototype.HANDLE_SIZE / 2,
0,
2 * Math.PI,
false );
ctx.fill();
}
};
})();
/**
* CAAT.LinearPath
*/
(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 ) {
// Fixed: Thanks https://github.com/roed
director.ctx.lineTo( this.points[1].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 );
})();
/**
* CAAT.CurvePath
*/
(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; i < this.pathSegments.length; i++ ) {
this.pathSegments[i].applyAsPath( director );
}
ctx.globalCompositeOperation = 'source-over';
return this;
},
/**
* Set whether this path should paint handles for every control point.
* @param interactive {boolean}.
*/
setInteractive:function ( interactive ) {
this.interactive = interactive;
return this;
},
getFirstPathSegment:function () {
return this.pathSegments.length ?
this.pathSegments[0] :
null;
},
getLastPathSegment:function () {
return this.pathSegments.length ?
this.pathSegments[ this.pathSegments.length - 1 ] :
null;
},
/**
* Return the last point of the last path segment of this compound path.
* @return {CAAT.Point}
*/
endCurvePosition:function () {
if ( this.pathSegments.length ) {
return this.pathSegments[ this.pathSegments.length - 1 ].endCurvePosition();
}
else {
return new CAAT.Point().set( this.beginPathX, this.beginPathY );
}
},
/**
* Return the first point of the first path segment of this compound path.
* @return {CAAT.Point}
*/
startCurvePosition:function () {
return this.pathSegments[ 0 ].startCurvePosition();
},
/**
* Return the last path segment added to this path.
* @return {CAAT.PathSegment}
*/
getCurrentPathSegment:function () {
return this.pathSegments[ this.pathSegments.length - 1 ];
},
/**
* Set the path to be composed by a single LinearPath segment.
* @param x0 {number}
* @param y0 {number}
* @param x1 {number}
* @param y1 {number}
* @return this
*/
setLinear:function ( x0, y0, x1, y1 ) {
this.beginPath( x0, y0 );
this.addLineTo( x1, y1 );
this.endPath();
return this;
},
/**
* Set this path to be composed by a single Quadric Bezier path segment.
* @param x0 {number}
* @param y0 {number}
* @param x1 {number}
* @param y1 {number}
* @param x2 {number}
* @param y2 {number}
* @return this
*/
setQuadric:function ( x0, y0, x1, y1, x2, y2 ) {
this.beginPath( x0, y0 );
this.addQuadricTo( x1, y1, x2, y2 );
this.endPath();
return this;
},
/**
* Sets this path to be composed by a single Cubic Bezier path segment.
* @param x0 {number}
* @param y0 {number}
* @param x1 {number}
* @param y1 {number}
* @param x2 {number}
* @param y2 {number}
* @param x3 {number}
* @param y3 {number}
*
* @return this
*/
setCubic:function ( x0, y0, x1, y1, x2, y2, x3, y3 ) {
this.beginPath( x0, y0 );
this.addCubicTo( x1, y1, x2, y2, x3, y3 );
this.endPath();
return this;
},
setRectangle:function ( x0, y0, x1, y1 ) {
this.beginPath( x0, y0 );
this.addRectangleTo( x1, y1 );
this.endPath();
return this;
},
setCatmullRom:function ( points, closed ) {
if ( closed ) {
points = points.slice( 0 )
points.unshift( points[points.length - 1] )
points.push( points[1] )
points.push( points[2] )
}
for ( var i = 1; i < points.length - 2; i++ ) {
var segment = new CAAT.CurvePath().setColor( "#000" ).setParent( this );
var cm = new CAAT.CatmullRom().setCurve(
points[ i - 1 ],
points[ i ],
points[ i + 1 ],
points[ i + 2 ]
);
segment.curve = cm;
this.pathSegments.push( segment );
}
return this;
},
/**
* Add a CAAT.PathSegment instance to this path.
* @param pathSegment {CAAT.PathSegment}
* @return this
*
*/
addSegment:function ( pathSegment ) {
pathSegment.setParent( this );
this.pathSegments.push( pathSegment );
return this;
},
addArcTo:function ( x1, y1, x2, y2, radius, cw, color ) {
var r = new CAAT.ArcPath();
r.setArcTo( true );
r.setRadius( radius );
r.setInitialPosition( x1, y1 ).
setFinalPosition( x2, y2 );
r.setParent( this );
r.setColor( color );
this.pathSegments.push( r );
return this;
},
addRectangleTo:function ( x1, y1, cw, color ) {
var r = new CAAT.RectPath();
r.setPoints( [
this.endCurvePosition(),
new CAAT.Point().set( x1, y1 )
] );
r.setClockWise( cw );
r.setColor( color );
r.setParent( this );
this.pathSegments.push( r );
return this;
},
/**
* Add a Quadric Bezier path segment to this path.
* The segment starts in the current last path coordinate.
* @param px1 {number}
* @param py1 {number}
* @param px2 {number}
* @param py2 {number}
* @param color {color=}. optional parameter. determines the color to draw the segment with (if
* being drawn by a CAAT.PathActor).
*
* @return this
*/
addQuadricTo:function ( px1, py1, px2, py2, color ) {
var bezier = new CAAT.Bezier();
bezier.setPoints(
[
this.endCurvePosition(),
new CAAT.Point().set( px1, py1 ),
new CAAT.Point().set( px2, py2 )
] );
this.trackPathX = px2;
this.trackPathY = py2;
var segment = new CAAT.CurvePath().setColor( color ).setParent( this );
segment.curve = bezier;
this.pathSegments.push( segment );
return this;
},
/**
* Add a Cubic Bezier segment to this path.
* The segment starts in the current last path coordinate.
* @param px1 {number}
* @param py1 {number}
* @param px2 {number}
* @param py2 {number}
* @param px3 {number}
* @param py3 {number}
* @param color {color=}. optional parameter. determines the color to draw the segment with (if
* being drawn by a CAAT.PathActor).
*
* @return this
*/
addCubicTo:function ( px1, py1, px2, py2, px3, py3, color ) {
var bezier = new CAAT.Bezier();
bezier.setPoints(
[
this.endCurvePosition(),
new CAAT.Point().set( px1, py1 ),
new CAAT.Point().set( px2, py2 ),
new CAAT.Point().set( px3, py3 )
] );
this.trackPathX = px3;
this.trackPathY = py3;
var segment = new CAAT.CurvePath().setColor( color ).setParent( this );
segment.curve = bezier;
this.pathSegments.push( segment );
return this;
},
/**
* Add a Catmull-Rom segment to this path.
* The segment starts in the current last path coordinate.
* @param px1 {number}
* @param py1 {number}
* @param px2 {number}
* @param py2 {number}
* @param px3 {number}
* @param py3 {number}
* @param color {color=}. optional parameter. determines the color to draw the segment with (if
* being drawn by a CAAT.PathActor).
*
* @return this
*/
addCatmullTo:function ( px1, py1, px2, py2, px3, py3, color ) {
var curve = new CAAT.CatmullRom().setColor( color );
curve.setCurve( this.trackPathX, this.trackPathY, px1, py1, px2, py2, px3, py3 );
this.trackPathX = px3;
this.trackPathY = py3;
var segment = new CAAT.CurvePath().setParent( this );
segment.curve = curve;
this.pathSegments.push( segment );
return this;
},
/**
* Adds a line segment to this path.
* The segment starts in the current last path coordinate.
* @param px1 {number}
* @param py1 {number}
* @param color {color=}. optional parameter. determines the color to draw the segment with (if
* being drawn by a CAAT.PathActor).
*
* @return this
*/
addLineTo:function ( px1, py1, color ) {
var segment = new CAAT.LinearPath().setColor( color );
segment.setPoints( [
this.endCurvePosition(),
new CAAT.Point().set( px1, py1 )
] );
segment.setParent( this );
this.trackPathX = px1;
this.trackPathY = py1;
this.pathSegments.push( segment );
return this;
},
/**
* Set the path's starting point. The method startCurvePosition will return this coordinate.
* add
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.
*