├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── CSSMatrix.js ├── Makefile ├── README.md ├── Tuple4.js ├── package.json └── test ├── CSSMatrix.js └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | test/CSSMatrix.test.js 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "eqnull": true 4 | } 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | Tuple4.js 2 | test 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | script: "node test/CSSMatrix.js" 5 | -------------------------------------------------------------------------------- /CSSMatrix.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // a CSSMatrix shim 4 | // http://www.w3.org/TR/css3-3d-transforms/#cssmatrix-interface 5 | // http://www.w3.org/TR/css3-2d-transforms/#cssmatrix-interface 6 | 7 | /** 8 | * CSSMatrix Shim 9 | * @constructor 10 | */ 11 | var CSSMatrix = function(){ 12 | var a = [].slice.call(arguments), 13 | m = this; 14 | if (a.length) for (var i = a.length; i--;){ 15 | if (Math.abs(a[i]) < CSSMatrix.SMALL_NUMBER) a[i] = 0; 16 | } 17 | m.setIdentity(); 18 | if (a.length == 16){ 19 | m.m11 = m.a = a[0]; m.m12 = m.b = a[1]; m.m13 = a[2]; m.m14 = a[3]; 20 | m.m21 = m.c = a[4]; m.m22 = m.d = a[5]; m.m23 = a[6]; m.m24 = a[7]; 21 | m.m31 = a[8]; m.m32 = a[9]; m.m33 = a[10]; m.m34 = a[11]; 22 | m.m41 = m.e = a[12]; m.m42 = m.f = a[13]; m.m43 = a[14]; m.m44 = a[15]; 23 | } else if (a.length == 6) { 24 | this.affine = true; 25 | m.m11 = m.a = a[0]; m.m12 = m.b = a[1]; m.m14 = m.e = a[4]; 26 | m.m21 = m.c = a[2]; m.m22 = m.d = a[3]; m.m24 = m.f = a[5]; 27 | } else if (a.length === 1 && typeof a[0] == 'string') { 28 | m.setMatrixValue(a[0]); 29 | } else if (a.length > 0) { 30 | throw new TypeError('Invalid Matrix Value'); 31 | } 32 | }; 33 | 34 | // decimal values in WebKitCSSMatrix.prototype.toString are truncated to 6 digits 35 | CSSMatrix.SMALL_NUMBER = 1e-6; 36 | 37 | // Transformations 38 | 39 | // http://en.wikipedia.org/wiki/Rotation_matrix 40 | CSSMatrix.Rotate = function(rx, ry, rz){ 41 | rx *= Math.PI / 180; 42 | ry *= Math.PI / 180; 43 | rz *= Math.PI / 180; 44 | // minus sin() because of right-handed system 45 | var cosx = Math.cos(rx), sinx = - Math.sin(rx); 46 | var cosy = Math.cos(ry), siny = - Math.sin(ry); 47 | var cosz = Math.cos(rz), sinz = - Math.sin(rz); 48 | var m = new CSSMatrix(); 49 | 50 | m.m11 = m.a = cosy * cosz; 51 | m.m12 = m.b = - cosy * sinz; 52 | m.m13 = siny; 53 | 54 | m.m21 = m.c = sinx * siny * cosz + cosx * sinz; 55 | m.m22 = m.d = cosx * cosz - sinx * siny * sinz; 56 | m.m23 = - sinx * cosy; 57 | 58 | m.m31 = sinx * sinz - cosx * siny * cosz; 59 | m.m32 = sinx * cosz + cosx * siny * sinz; 60 | m.m33 = cosx * cosy; 61 | 62 | return m; 63 | }; 64 | 65 | CSSMatrix.RotateAxisAngle = function(x, y, z, angle){ 66 | angle *= Math.PI / 360; 67 | 68 | var sinA = Math.sin(angle), cosA = Math.cos(angle), sinA2 = sinA * sinA; 69 | var length = Math.sqrt(x * x + y * y + z * z); 70 | 71 | if (length === 0){ 72 | // bad vector length, use something reasonable 73 | x = 0; 74 | y = 0; 75 | z = 1; 76 | } else { 77 | x /= length; 78 | y /= length; 79 | z /= length; 80 | } 81 | 82 | var x2 = x * x, y2 = y * y, z2 = z * z; 83 | 84 | var m = new CSSMatrix(); 85 | m.m11 = m.a = 1 - 2 * (y2 + z2) * sinA2; 86 | m.m12 = m.b = 2 * (x * y * sinA2 + z * sinA * cosA); 87 | m.m13 = 2 * (x * z * sinA2 - y * sinA * cosA); 88 | m.m21 = m.c = 2 * (y * x * sinA2 - z * sinA * cosA); 89 | m.m22 = m.d = 1 - 2 * (z2 + x2) * sinA2; 90 | m.m23 = 2 * (y * z * sinA2 + x * sinA * cosA); 91 | m.m31 = 2 * (z * x * sinA2 + y * sinA * cosA); 92 | m.m32 = 2 * (z * y * sinA2 - x * sinA * cosA); 93 | m.m33 = 1 - 2 * (x2 + y2) * sinA2; 94 | m.m14 = m.m24 = m.m34 = 0; 95 | m.m41 = m.e = m.m42 = m.f = m.m43 = 0; 96 | m.m44 = 1; 97 | 98 | return m; 99 | }; 100 | 101 | CSSMatrix.ScaleX = function(x){ 102 | var m = new CSSMatrix(); 103 | m.m11 = m.a = x; 104 | return m; 105 | }; 106 | 107 | CSSMatrix.ScaleY = function(y){ 108 | var m = new CSSMatrix(); 109 | m.m22 = m.d = y; 110 | return m; 111 | }; 112 | 113 | CSSMatrix.ScaleZ = function(z){ 114 | var m = new CSSMatrix(); 115 | m.m33 = z; 116 | return m; 117 | }; 118 | 119 | CSSMatrix.Scale = function(x, y, z){ 120 | var m = new CSSMatrix(); 121 | m.m11 = m.a = x; 122 | m.m22 = m.d = y; 123 | m.m33 = z; 124 | return m; 125 | }; 126 | 127 | CSSMatrix.SkewX = function(angle){ 128 | angle *= Math.PI / 180; 129 | var m = new CSSMatrix(); 130 | m.m21 = m.c = Math.tan(angle); 131 | return m; 132 | }; 133 | 134 | CSSMatrix.SkewY = function(angle){ 135 | angle *= Math.PI / 180; 136 | var m = new CSSMatrix(); 137 | m.m12 = m.b = Math.tan(angle); 138 | return m; 139 | }; 140 | 141 | CSSMatrix.Translate = function(x, y, z){ 142 | var m = new CSSMatrix(); 143 | m.m41 = m.e = x; 144 | m.m42 = m.f = y; 145 | m.m43 = z; 146 | return m; 147 | }; 148 | 149 | CSSMatrix.multiply = function(m1, m2){ 150 | 151 | var m11 = m2.m11 * m1.m11 + m2.m12 * m1.m21 + m2.m13 * m1.m31 + m2.m14 * m1.m41, 152 | m12 = m2.m11 * m1.m12 + m2.m12 * m1.m22 + m2.m13 * m1.m32 + m2.m14 * m1.m42, 153 | m13 = m2.m11 * m1.m13 + m2.m12 * m1.m23 + m2.m13 * m1.m33 + m2.m14 * m1.m43, 154 | m14 = m2.m11 * m1.m14 + m2.m12 * m1.m24 + m2.m13 * m1.m34 + m2.m14 * m1.m44, 155 | 156 | m21 = m2.m21 * m1.m11 + m2.m22 * m1.m21 + m2.m23 * m1.m31 + m2.m24 * m1.m41, 157 | m22 = m2.m21 * m1.m12 + m2.m22 * m1.m22 + m2.m23 * m1.m32 + m2.m24 * m1.m42, 158 | m23 = m2.m21 * m1.m13 + m2.m22 * m1.m23 + m2.m23 * m1.m33 + m2.m24 * m1.m43, 159 | m24 = m2.m21 * m1.m14 + m2.m22 * m1.m24 + m2.m23 * m1.m34 + m2.m24 * m1.m44, 160 | 161 | m31 = m2.m31 * m1.m11 + m2.m32 * m1.m21 + m2.m33 * m1.m31 + m2.m34 * m1.m41, 162 | m32 = m2.m31 * m1.m12 + m2.m32 * m1.m22 + m2.m33 * m1.m32 + m2.m34 * m1.m42, 163 | m33 = m2.m31 * m1.m13 + m2.m32 * m1.m23 + m2.m33 * m1.m33 + m2.m34 * m1.m43, 164 | m34 = m2.m31 * m1.m14 + m2.m32 * m1.m24 + m2.m33 * m1.m34 + m2.m34 * m1.m44, 165 | 166 | m41 = m2.m41 * m1.m11 + m2.m42 * m1.m21 + m2.m43 * m1.m31 + m2.m44 * m1.m41, 167 | m42 = m2.m41 * m1.m12 + m2.m42 * m1.m22 + m2.m43 * m1.m32 + m2.m44 * m1.m42, 168 | m43 = m2.m41 * m1.m13 + m2.m42 * m1.m23 + m2.m43 * m1.m33 + m2.m44 * m1.m43, 169 | m44 = m2.m41 * m1.m14 + m2.m42 * m1.m24 + m2.m43 * m1.m34 + m2.m44 * m1.m44; 170 | 171 | return new CSSMatrix( 172 | m11, m12, m13, m14, 173 | m21, m22, m23, m24, 174 | m31, m32, m33, m34, 175 | m41, m42, m43, m44 176 | ); 177 | }; 178 | 179 | // w3c defined methods 180 | 181 | /** 182 | * The setMatrixValue method replaces the existing matrix with one computed 183 | * from parsing the passed string as though it had been assigned to the 184 | * transform property in a CSS style rule. 185 | * @param {String} string The string to parse. 186 | */ 187 | CSSMatrix.prototype.setMatrixValue = function(string){ 188 | string = String(string).trim(); 189 | var m = this; 190 | m.setIdentity(); 191 | if (string == 'none') return m; 192 | var type = string.slice(0, string.indexOf('(')), parts, i; 193 | if (type == 'matrix3d'){ 194 | parts = string.slice(9, -1).split(','); 195 | for (i = parts.length; i--;) parts[i] = parseFloat(parts[i]); 196 | m.m11 = m.a = parts[0]; m.m12 = m.b = parts[1]; m.m13 = parts[2]; m.m14 = parts[3]; 197 | m.m21 = m.c = parts[4]; m.m22 = m.d = parts[5]; m.m23 = parts[6]; m.m24 = parts[7]; 198 | m.m31 = parts[8]; m.m32 = parts[9]; m.m33 = parts[10]; m.m34 = parts[11]; 199 | m.m41 = m.e = parts[12]; m.m42 = m.f = parts[13]; m.m43 = parts[14]; m.m44 = parts[15]; 200 | } else if (type == 'matrix'){ 201 | m.affine = true; 202 | parts = string.slice(7, -1).split(','); 203 | for (i = parts.length; i--;) parts[i] = parseFloat(parts[i]); 204 | m.m11 = m.a = parts[0]; m.m12 = m.b = parts[2]; m.m41 = m.e = parts[4]; 205 | m.m21 = m.c = parts[1]; m.m22 = m.d = parts[3]; m.m42 = m.f = parts[5]; 206 | } else { 207 | throw new TypeError('Invalid Matrix Value'); 208 | } 209 | return m; 210 | }; 211 | 212 | /** 213 | * The multiply method returns a new CSSMatrix which is the result of this 214 | * matrix multiplied by the passed matrix, with the passed matrix to the right. 215 | * This matrix is not modified. 216 | * 217 | * @param {CSSMatrix} m2 218 | * @return {CSSMatrix} The result matrix. 219 | */ 220 | CSSMatrix.prototype.multiply = function(m2){ 221 | return CSSMatrix.multiply(this, m2); 222 | }; 223 | 224 | /** 225 | * The inverse method returns a new matrix which is the inverse of this matrix. 226 | * This matrix is not modified. 227 | * 228 | * method not implemented yet 229 | */ 230 | CSSMatrix.prototype.inverse = function(){ 231 | throw new Error('the inverse() method is not implemented (yet).'); 232 | }; 233 | 234 | /** 235 | * The translate method returns a new matrix which is this matrix post 236 | * multiplied by a translation matrix containing the passed values. If the z 237 | * component is undefined, a 0 value is used in its place. This matrix is not 238 | * modified. 239 | * 240 | * @param {number} x X component of the translation value. 241 | * @param {number} y Y component of the translation value. 242 | * @param {number=} z Z component of the translation value. 243 | * @return {CSSMatrix} The result matrix 244 | */ 245 | CSSMatrix.prototype.translate = function(x, y, z){ 246 | if (z == null) z = 0; 247 | return CSSMatrix.multiply(this, CSSMatrix.Translate(x, y, z)); 248 | }; 249 | 250 | /** 251 | * The scale method returns a new matrix which is this matrix post multiplied by 252 | * a scale matrix containing the passed values. If the z component is undefined, 253 | * a 1 value is used in its place. If the y component is undefined, the x 254 | * component value is used in its place. This matrix is not modified. 255 | * 256 | * @param {number} x The X component of the scale value. 257 | * @param {number=} y The Y component of the scale value. 258 | * @param {number=} z The Z component of the scale value. 259 | * @return {CSSMatrix} The result matrix 260 | */ 261 | CSSMatrix.prototype.scale = function(x, y, z){ 262 | if (y == null) y = x; 263 | if (z == null) z = 1; 264 | return CSSMatrix.multiply(this, CSSMatrix.Scale(x, y, z)); 265 | }; 266 | 267 | /** 268 | * The rotate method returns a new matrix which is this matrix post multiplied 269 | * by each of 3 rotation matrices about the major axes, first X, then Y, then Z. 270 | * If the y and z components are undefined, the x value is used to rotate the 271 | * object about the z axis, as though the vector (0,0,x) were passed. All 272 | * rotation values are in degrees. This matrix is not modified. 273 | * 274 | * @param {number} rx The X component of the rotation value, or the Z component if the rotY and rotZ parameters are undefined. 275 | * @param {number=} ry The (optional) Y component of the rotation value. 276 | * @param {number=} rz The (optional) Z component of the rotation value. 277 | * @return {CSSMatrix} The result matrix 278 | */ 279 | CSSMatrix.prototype.rotate = function(rx, ry, rz){ 280 | if (ry == null) ry = rx; 281 | if (rz == null) rz = rx; 282 | return CSSMatrix.multiply(this, CSSMatrix.Rotate(rx, ry, rz)); 283 | }; 284 | 285 | /** 286 | * The rotateAxisAngle method returns a new matrix which is this matrix post 287 | * multiplied by a rotation matrix with the given axis and angle. The right-hand 288 | * rule is used to determine the direction of rotation. All rotation values are 289 | * in degrees. This matrix is not modified. 290 | * 291 | * @param {number} x The X component of the axis vector. 292 | * @param {number=} y The Y component of the axis vector. 293 | * @param {number=} z The Z component of the axis vector. 294 | * @param {number} angle The angle of rotation about the axis vector, in degrees. 295 | * @return {CSSMatrix} The result matrix 296 | */ 297 | CSSMatrix.prototype.rotateAxisAngle = function(x, y, z, angle){ 298 | if (y == null) y = x; 299 | if (z == null) z = x; 300 | return CSSMatrix.multiply(this, CSSMatrix.RotateAxisAngle(x, y, z, angle)); 301 | }; 302 | 303 | // Defined in WebKitCSSMatrix, but not in the w3c draft 304 | 305 | /** 306 | * Specifies a skew transformation along the x-axis by the given angle. 307 | * 308 | * @param {number} angle The angle amount in degrees to skew. 309 | * @return {CSSMatrix} The result matrix 310 | */ 311 | CSSMatrix.prototype.skewX = function(angle){ 312 | return CSSMatrix.multiply(this, CSSMatrix.SkewX(angle)); 313 | }; 314 | 315 | /** 316 | * Specifies a skew transformation along the x-axis by the given angle. 317 | * 318 | * @param {number} angle The angle amount in degrees to skew. 319 | * @return {CSSMatrix} The result matrix 320 | */ 321 | CSSMatrix.prototype.skewY = function(angle){ 322 | return CSSMatrix.multiply(this, CSSMatrix.SkewY(angle)); 323 | }; 324 | 325 | /** 326 | * Returns a string representation of the matrix. 327 | * @return {string} 328 | */ 329 | CSSMatrix.prototype.toString = function(){ 330 | var m = this; 331 | 332 | if (this.affine){ 333 | return 'matrix(' + [ 334 | m.a, m.b, 335 | m.c, m.d, 336 | m.e, m.f 337 | ].join(', ') + ')'; 338 | } 339 | // note: the elements here are transposed 340 | return 'matrix3d(' + [ 341 | m.m11, m.m12, m.m13, m.m14, 342 | m.m21, m.m22, m.m23, m.m24, 343 | m.m31, m.m32, m.m33, m.m34, 344 | m.m41, m.m42, m.m43, m.m44 345 | ].join(', ') + ')'; 346 | }; 347 | 348 | 349 | // Additional methods 350 | 351 | /** 352 | * Set the current matrix to the identity form 353 | * 354 | * @return {CSSMatrix} this matrix 355 | */ 356 | CSSMatrix.prototype.setIdentity = function(){ 357 | var m = this; 358 | m.m11 = m.a = 1; m.m12 = m.b = 0; m.m13 = 0; m.m14 = 0; 359 | m.m21 = m.c = 0; m.m22 = m.d = 1; m.m23 = 0; m.m24 = 0; 360 | m.m31 = 0; m.m32 = 0; m.m33 = 1; m.m34 = 0; 361 | m.m41 = m.e = 0; m.m42 = m.f = 0; m.m43 = 0; m.m44 = 1; 362 | return this; 363 | }; 364 | 365 | /** 366 | * Transform a tuple (3d point) with this CSSMatrix 367 | * 368 | * @param {Tuple} an object with x, y, z and w properties 369 | * @return {Tuple} the passed tuple 370 | */ 371 | CSSMatrix.prototype.transform = function(t /* tuple */ ){ 372 | var m = this; 373 | 374 | var x = m.m11 * t.x + m.m12 * t.y + m.m13 * t.z + m.m14 * t.w, 375 | y = m.m21 * t.x + m.m22 * t.y + m.m23 * t.z + m.m24 * t.w, 376 | z = m.m31 * t.x + m.m32 * t.y + m.m33 * t.z + m.m34 * t.w, 377 | w = m.m41 * t.x + m.m42 * t.y + m.m43 * t.z + m.m44 * t.w; 378 | 379 | t.x = x / w; 380 | t.y = y / w; 381 | t.z = z / w; 382 | 383 | return t; 384 | }; 385 | 386 | CSSMatrix.prototype.toFullString = function(){ 387 | var m = this; 388 | return [ 389 | [m.m11, m.m12, m.m13, m.m14].join(', '), 390 | [m.m21, m.m22, m.m23, m.m24].join(', '), 391 | [m.m31, m.m32, m.m33, m.m34].join(', '), 392 | [m.m41, m.m42, m.m43, m.m44].join(', ') 393 | ].join('\n'); 394 | }; 395 | 396 | module.exports = CSSMatrix; 397 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | test/CSSMatrix.test.js: . test 3 | wrup -r ./test/CSSMatrix.js -o test/CSSMatrix.test.js 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CSSMatrix 2 | ========= 3 | 4 | [](http://travis-ci.org/arian/CSSMatrix) 5 | 6 | This is a CSSMatrix class as defined by the [w3c CSS3 3d Transforms](http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#cssmatrix-interface) specification. 7 | 8 | ### Installation 9 | 10 | npm install CSSMatrix 11 | 12 | ### Usage 13 | 14 | It should be compatible with documentation defined at [w3.org](http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#cssmatrix-interface) and at [WebKitCSSMatrix](https://developer.apple.com/library/iad/documentation/AudioVideo/Reference/WebKitCSSMatrixClassReference/index.html) Safari documentation. 15 | 16 | __Methods:__ 17 | 18 | - translate(x, y, z) 19 | - scale(x, y, z) 20 | - rotate(rx, ry, rz) 21 | - rotateAxisAngle(x, y, z, angle) 22 | - skewX(angle) 23 | - skewY(angle) 24 | - toString() 25 | 26 | __Additional Methods__: 27 | 28 | - transform(tuple) 29 | - toFullString() 30 | 31 | __Properties:__ 32 | 33 | - m11 to m44. m12 is the second element in the first row. 34 | 35 | ## MIT License 36 | 37 | Copyright (c) 2013 Arian Stolwijk 38 | 39 | Permission is hereby granted, free of charge, to any person obtaining a copy of 40 | this software and associated documentation files (the "Software"), to deal in 41 | the Software without restriction, including without limitation the rights to 42 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 43 | of the Software, and to permit persons to whom the Software is furnished to do 44 | so, subject to the following conditions: 45 | 46 | The above copyright notice and this permission notice shall be included in all 47 | copies or substantial portions of the Software. 48 | 49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 50 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 51 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 52 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 53 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 54 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 55 | SOFTWARE. 56 | -------------------------------------------------------------------------------- /Tuple4.js: -------------------------------------------------------------------------------- 1 | 2 | var Tuple = module.exports = function(x, y, z, w){ 3 | this.x = x; 4 | this.y = y; 5 | this.z = z; 6 | this.w = w; 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Arian Stolwijk", 3 | "name": "CSSMatrix", 4 | "description": "A CSSMatrix shim for 3D tranformation matrices", 5 | "version": "0.1.1", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/arian/CSSMatrix.git" 10 | }, 11 | "main": "CSSMatrix", 12 | "scripts": { 13 | "test": "node CSSMatrixTest.js" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/CSSMatrix.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var browser = !!global.window; 4 | 5 | var Matrix = require('../CSSMatrix'); 6 | var Tuple = require('../Tuple4'); 7 | 8 | var assert = { 9 | equal: function(expected, actual){ 10 | if (expected !== actual) 11 | throw new Error("expected " + actual + " to be " + expected); 12 | } 13 | }; 14 | 15 | var assertMatrix = function(expected, actual){ 16 | var i, j, p, 17 | m = new Matrix(), 18 | letters = ['a', 'b', 'c', 'd', 'e', 'f']; 19 | m.setMatrixValue(expected); 20 | expected = m; 21 | for (i = 1; i <= 4; i++) for (j = 1; j <= 4; j++){ 22 | p = 'm' + j + i; 23 | if (Math.abs(expected[p] - actual[p]) > Matrix.SMALL_NUMBER){ 24 | console.log("failing matrix:", actual, "should be ", expected); 25 | throw new Error("Expected " + p + " to be " + expected[p] + " but was " + actual[p]); 26 | } 27 | } 28 | for (i = 0; i < letters.length; i++) { 29 | p = letters[i]; 30 | if (Math.abs(expected[p] - actual[p]) > Matrix.SMALL_NUMBER){ 31 | console.log("failing matrix:", actual, "should be ", expected); 32 | throw new Error("Expected " + p + " to be " + expected[p] + " but was " + actual[p]); 33 | } 34 | } 35 | }; 36 | 37 | 38 | var CSSMatrix = global.CSSMatrix || global.WebKitCSSMatrix || global.MSCSSMatrix; 39 | 40 | var results = browser && global.document.getElementById("results"); 41 | 42 | var success = 0; 43 | var failures = 0; 44 | 45 | function pass(msg){ 46 | success++; 47 | if (!browser) console.log('\x1B[32m✓ ' + msg + '\x1B[0m'); 48 | else results.innerHTML += '
' + msg + '
'; 49 | } 50 | 51 | function fail(msg, err){ 52 | failures++; 53 | if (!browser){ 54 | console.log('\x1B[31m✘ ' + msg + '\x1B[0m'); 55 | if (err) console.error(err); 56 | } else { 57 | results.innerHTML += '' + msg + '
'; 58 | if (err) results.innerHTML += '