├── .gitignore ├── .jshintrc ├── .npmignore ├── Gruntfile.coffee ├── LICENSE.md ├── README.md ├── demo └── index.html ├── dist ├── css-to-matrix.js ├── css-to-matrix.min.js ├── css-to-matrix.standalone.js └── css-to-matrix.standalone.min.js ├── grunt-umd-template.hbs ├── package.json ├── src └── css-to-matrix.js └── test ├── .DS_Store ├── chai.js ├── index.html ├── mocha.css ├── mocha.js ├── test.coffee.md └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | Gruntfile.js 3 | .DS_Store 4 | npm-debug.log -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "eqnull": true, 3 | "esnext": true, 4 | "laxbreak": true, 5 | "laxcomma": true, 6 | "asi": true, 7 | "smarttabs": true 8 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | demo/ 2 | test/ 3 | grunt-umd-template.hbs 4 | Gruntfile.coffee -------------------------------------------------------------------------------- /Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | changeCase = require 'change-case' 2 | 3 | module.exports = (grunt) -> 4 | 5 | nameParts = __dirname.split '/' 6 | name = nameParts[nameParts.length - 1] 7 | pkg = grunt.file.readJSON 'package.json' 8 | deps = grunt.util._.keys pkg.dependencies 9 | 10 | build = ['concat', 'regex-replace', 'umd', 'uglify'] 11 | test = ['coffee:test'] 12 | 13 | grunt.config.init 14 | 15 | pkg: pkg 16 | 17 | coffee: 18 | 19 | test: 20 | files: 21 | 'test/test.js': 'test/test.coffee.md' 22 | 23 | uglify: 24 | 25 | options: 26 | mangle: 27 | toplevel: true 28 | compress: 29 | dead_code: true 30 | unused: true 31 | join_vars: true 32 | comments: false 33 | 34 | standalone: 35 | files: 36 | 'dist/<%= pkg.name %>.standalone.min.js': [ 37 | 'dist/<%= pkg.name %>.standalone.js' 38 | ] 39 | 'dist/<%= pkg.name %>.min.js': [ 40 | 'src/<%= pkg.name %>.js' 41 | ] 42 | 43 | concat: 44 | 45 | standard: 46 | src: 'src/<%= pkg.name %>.js' 47 | dest: 'dist/<%= pkg.name %>.js' 48 | 49 | standalone: 50 | src: [ 51 | 'node_modules/matrix-utilities/matrix-utilities.js' 52 | 'node_modules/transform-to-matrix/transform-to-matrix.js' 53 | 'node_modules/umodel/umodel.js' 54 | 'src/<%= pkg.name %>.js' 55 | ] 56 | dest: 'dist/<%= pkg.name %>.standalone.js' 57 | 58 | 'regex-replace': 59 | 60 | min: 61 | src: ['dist/<%= pkg.name %>.min.js', 'dist/<%= pkg.name %>.standalone.min.js'], 62 | actions: [ 63 | { 64 | name: 'remove debug checks' 65 | search: '////DEV(.+)////END DEV' 66 | replace: '' 67 | flags: 'gim' 68 | } 69 | ] 70 | 71 | watch: 72 | 73 | test: 74 | files: './test/*' 75 | tasks: test 76 | options: 77 | interrupt: true 78 | spawn: false 79 | 80 | umd: 81 | all: 82 | src: './dist/' + name + '.js' 83 | objectToExport: changeCase.pascalCase name 84 | amdModuleId: name 85 | globalAlias: name 86 | deps: 87 | default: deps 88 | template: 'grunt-umd-template.hbs' 89 | 90 | 91 | [ 92 | 'grunt-contrib-coffee' 93 | 'grunt-contrib-concat' 94 | 'grunt-contrib-watch' 95 | 'grunt-contrib-uglify' 96 | 'grunt-regex-replace' 97 | 'grunt-umd' 98 | ].forEach grunt.loadNpmTasks 99 | 100 | grunt.registerTask 'default', build 101 | grunt.registerTask 'test', test -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2013 Boris Cherny 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # css-to-matrix 2 | 3 | A little library for converting compound CSS transforms into their matrix equivalents. 4 | 5 | ## usage 6 | 7 | ```coffee 8 | 9 | # using the default matrix 10 | new CssToMatrix 11 | 12 | # or using a custom 4x4 matrix (meaning some transformations are already applied) 13 | matrix = [ 14 | [1, 2, 3, 4] 15 | [5, 6, 7, 8] 16 | [9, 0, 1, 2] 17 | [3, 4, 5, 6] 18 | ] 19 | new CssToMatrix matrix 20 | 21 | ``` 22 | 23 | ## example 24 | 25 | ```coffee 26 | cssToMatrix = new CssToMatrix 27 | 28 | cssToMatrix 29 | 30 | # set some transforms 31 | .rotate '90deg' 32 | .translate3d 50, 100, 200 33 | 34 | # get a matrix back 35 | .getMatrix() 36 | 37 | 38 | #=> [ 39 | # [9.870993963020204, 0.7, 0, 0], 40 | # [-0.5, 0, 0, 0], 41 | # [0, 0, 1, 0], 42 | # [443.54969815101026, 35, 200, 1] 43 | # ] 44 | 45 | # .. or as a CSS property 46 | cssToMatrix.getMatrixCSS() 47 | 48 | #=> "matrix3d(9.870993963020204, 0.7, 0, 0, -0.5, 0, 0, 0, 0, 0, 1, 0, 443.54969815101026, 35, 200, 1)" 49 | 50 | # set a new base matrix (on the basis of which transforms are applied) 51 | cssToMatrix.matrix [ 52 | [3, 1, 4, 1] 53 | [5, 9, 2, 6] 54 | [5, 3, 5, 8] 55 | [9, 7, 9, 3] 56 | ] 57 | 58 | ``` 59 | 60 | ## supported transforms 61 | 62 | - perspective 63 | - rotate 64 | - rotateX 65 | - rotateY 66 | - rotateZ 67 | - rotate3d 68 | - scale 69 | - scaleX 70 | - scaleY 71 | - scaleZ 72 | - scale3d 73 | - skew 74 | - skewX 75 | - skewY 76 | - translate 77 | - translateX 78 | - translateY 79 | - translateZ 80 | - translate3d 81 | 82 | ## why not use actual matricies? 83 | 84 | If you're performing caculations dozens of times per second (in the case of animations. 60 times per second), performance is essential. Not only do we need to be able to perform calculations for complex compound 3D transforms at 60FPS, but we need to leave head room for any precursor computations, as well as compositing and painting. Performing the calculations ourselves (instead of leaving it to the CSS engine) gives us finer control over where (CPU vs. GPU, this thread vs. a WebWorker, sync vs. async) and when (precomputed, partially applied, realtime) computation occurs. 85 | 86 | ## see also 87 | 88 | - http://www.w3.org/TR/css3-transforms/#transform-functions 89 | - http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined 90 | - http://dev.opera.com/articles/view/understanding-the-css-transforms-matrix/ 91 | - http://dev.opera.com/articles/view/understanding-3d-transforms/ 92 | - http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ 93 | - http://stackoverflow.com/a/15208858/435124 94 | - http://desandro.github.io/3dtransforms/docs/perspective.html 95 | 96 | ## todo 97 | 98 | - accept alternative units for scalars (%, em, pt, etc.) 99 | 100 | ## running the tests 101 | 102 | ```bash 103 | npm i 104 | grunt test 105 | ``` 106 | 107 | then, open test/index.html in a browser 108 | 109 | ## license 110 | 111 | MIT -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 34 | 35 | 36 | 37 | 38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 61 | 62 | -------------------------------------------------------------------------------- /dist/css-to-matrix.js: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | if(typeof exports === 'object') { 3 | module.exports = factory(require('transform-to-matrix'), require('matrix-utilities'), require('umodel')); 4 | } 5 | else if(typeof define === 'function' && define.amd) { 6 | define('css-to-matrix', ['transform-to-matrix', 'matrix-utilities', 'umodel'], factory); 7 | } 8 | else { 9 | root['css-to-matrix'] = factory(root['transform-to-matrix'], root['matrix-utilities'], root.umodel); 10 | } 11 | }(this, function(transformToMatrix, matrixUtilities, umodel) { 12 | var _ = { 13 | 14 | // convert strings like "55deg" or ".75rad" to floats (in radians) 15 | rad: function (string) { 16 | 17 | if (typeof string === 'string') { 18 | 19 | var angle = parseFloat(string, 10), 20 | isDegrees = string.indexOf('deg') > -1 21 | 22 | // convert deg -> rad? 23 | if (isDegrees) angle *= Math.PI / 180 24 | 25 | return angle 26 | 27 | } 28 | 29 | return string 30 | 31 | }, 32 | 33 | // shallow object extend 34 | extend: function (a, b) { 35 | 36 | for (var key in b) { 37 | a[key] = b[key] 38 | } 39 | 40 | return a 41 | 42 | }, 43 | 44 | // make functions return `this`, for easy chaining 45 | fluent: function (fn) { 46 | 47 | return function() { 48 | fn.apply(this, arguments) 49 | return this 50 | } 51 | 52 | }, 53 | 54 | isNumber: function (a) { 55 | return typeof a === 'number' 56 | } 57 | 58 | }; 59 | 60 | function CssToMatrix (data) { 61 | 62 | // default options 63 | this.model = new umodel({ 64 | matrix: new matrixUtilities.Identity(), 65 | transformations: { 66 | perspective: new matrixUtilities.Identity(), 67 | rotate: new matrixUtilities.Identity(), 68 | scale: new matrixUtilities.Identity(), 69 | skew: new matrixUtilities.Identity(), 70 | translate: new matrixUtilities.Identity() 71 | } 72 | }) 73 | 74 | // set data? 75 | if (data) { 76 | this.matrix(data) 77 | } 78 | 79 | } 80 | 81 | _.extend(CssToMatrix.prototype, { 82 | 83 | // set matrix in model 84 | matrix: function (data) { 85 | 86 | ////DEV 87 | if (data.length == null) 88 | throw new TypeError('expected parameter `data` to be an Array, but was given a ' + typeof data) 89 | 90 | var rows = data.length, 91 | columns = rows > 0 ? rows : 0 92 | 93 | if (rows !== 4 || columns !== 4) 94 | throw new Error('expected parameter `data` to be a 4x4 matrix of arrays, but was given a ' + rows + 'x' + columns + ' matrix') 95 | ////END DEV 96 | 97 | this.model.set('matrix', data) 98 | 99 | }, 100 | 101 | // apply transformations as defined in the model, and get back get calculated matrix 102 | getMatrix: function() { 103 | 104 | var matrix = this.model.get('matrix'), 105 | t = this.model.get('transformations') 106 | 107 | // perspective 108 | matrix = matrixUtilities.multiply(matrix, t.perspective) 109 | 110 | // translate 111 | matrix = matrixUtilities.multiply(matrix, t.translate) 112 | 113 | // rotate 114 | matrix = matrixUtilities.multiply(matrix, t.rotate) 115 | 116 | // skew 117 | matrix = matrixUtilities.multiply(matrix, t.skew) 118 | 119 | // scale 120 | matrix = matrixUtilities.multiply(matrix, t.scale) 121 | 122 | return matrixUtilities.flip(matrix) 123 | 124 | }, 125 | 126 | // get matrix formatted as a string that can be plugged right into CSS's `transform` function 127 | getMatrixCSS: function() { 128 | 129 | return 'matrix3d(' 130 | + this 131 | .getMatrix() 132 | .reduce(function (flat, row) { 133 | flat.push.apply(flat, row) 134 | return flat 135 | }, []) 136 | .join(',') 137 | + ')' 138 | 139 | }, 140 | 141 | // transform functions 142 | // 1-to-1 with their CSS equivalents 143 | rotate: function (a) { return this.rotateZ(a) }, 144 | rotateX: function (a) { return this.rotate3d(1, 0, 0, a) }, 145 | rotateY: function (a) { return this.rotate3d(0, 1, 0, a) }, 146 | rotateZ: function (a) { return this.rotate3d(0, 0, 1, a) }, 147 | scale: function (x, y) { return this.scale3d(x, y) }, 148 | scaleX: function (x) { return this.scale3d(x) }, 149 | scaleY: function (y) { return this.scale3d(null, y) }, 150 | scaleZ: function (z) { return this.scale3d(null, null, z) }, 151 | skewX: function (x) { return this.skew(x) }, 152 | skewY: function (y) { return this.skew(null, y) }, 153 | translate: function (x, y) { return this.translate3d(x, y) }, 154 | translateX: function (x) { return this.translate3d(x) }, 155 | translateY: function (y) { return this.translate3d(null, y) }, 156 | translateZ: function (z) { return this.translate3d(null, null, z) }, 157 | 158 | perspective: _.fluent(function (x) { 159 | 160 | if (x == null) { x = 0 } 161 | 162 | ////DEV 163 | if (!_.isNumber(x)) 164 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 165 | ////END DEV 166 | 167 | this.model.set('transformations/perspective', transformToMatrix.perspective(x)) 168 | 169 | }), 170 | 171 | rotate3d: _.fluent(function (x, y, z, a) { 172 | 173 | if (x == null) { x = 0 } 174 | if (y == null) { y = 0 } 175 | if (z == null) { z = 0 } 176 | if (a == null) { a = 0 } 177 | 178 | ////DEV 179 | if (!_.isNumber(x)) 180 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 181 | if (!_.isNumber(y)) 182 | throw new TypeError('expected parameter `y` to be a Number, but was given a ' + typeof y) 183 | if (!_.isNumber(z)) 184 | throw new TypeError('expected parameter `z` to be a Number, but was given a ' + typeof z) 185 | ////END DEV 186 | 187 | // if angle was passed as a string, convert it to a float first 188 | this.model.set('transformations/rotate', transformToMatrix.rotate3d(x, y, z, _.rad(a))) 189 | 190 | }), 191 | 192 | scale3d: _.fluent(function (x, y, z) { 193 | 194 | if (x == null) { x = 1 } 195 | if (y == null) { y = 1 } 196 | if (z == null) { z = 1 } 197 | 198 | ////DEV 199 | if (!_.isNumber(x)) 200 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 201 | if (!_.isNumber(y)) 202 | throw new TypeError('expected parameter `y` to be a Number, but was given a ' + typeof y) 203 | if (!_.isNumber(z)) 204 | throw new TypeError('expected parameter `z` to be a Number, but was given a ' + typeof z) 205 | ////END DEV 206 | 207 | this.model.set('transformations/scale', transformToMatrix.scale3d(x, y, z)) 208 | 209 | }), 210 | 211 | skew: _.fluent(function (x, y) { 212 | 213 | if (x == null) { x = 0 } 214 | if (y == null) { y = 0 } 215 | 216 | this.model.set('transformations/skew', matrixUtilities.to3d(transformToMatrix.skew(_.rad(x), _.rad(y)))) 217 | 218 | }), 219 | 220 | translate3d: _.fluent(function(x, y, z) { 221 | 222 | if (x == null) { x = 0 } 223 | if (y == null) { y = 0 } 224 | if (z == null) { z = 0 } 225 | 226 | ////DEV 227 | if (!_.isNumber(x)) 228 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 229 | if (!_.isNumber(y)) 230 | throw new TypeError('expected parameter `y` to be a Number, but was given a ' + typeof y) 231 | if (!_.isNumber(z)) 232 | throw new TypeError('expected parameter `z` to be a Number, but was given a ' + typeof z) 233 | ////END DEV 234 | 235 | this.model.set('transformations/translate', transformToMatrix.translate3d(x, y, z)) 236 | 237 | }) 238 | 239 | }) 240 | return CssToMatrix; 241 | })); -------------------------------------------------------------------------------- /dist/css-to-matrix.min.js: -------------------------------------------------------------------------------- 1 | function a(a){this.model=new umodel({matrix:new matrixUtilities.Identity,transformations:{perspective:new matrixUtilities.Identity,rotate:new matrixUtilities.Identity,scale:new matrixUtilities.Identity,skew:new matrixUtilities.Identity,translate:new matrixUtilities.Identity}}),a&&this.matrix(a)}var b={rad:function(a){if("string"==typeof a){var b=parseFloat(a,10),c=a.indexOf("deg")>-1;return c&&(b*=Math.PI/180),b}return a},extend:function(a,b){for(var c in b)a[c]=b[c];return a},fluent:function(a){return function(){return a.apply(this,arguments),this}},isNumber:function(a){return"number"==typeof a}};b.extend(a.prototype,{matrix:function(a){if(null==a.length)throw new TypeError("expected parameter `data` to be an Array, but was given a "+typeof a);var b=a.length,c=b>0?b:0;if(4!==b||4!==c)throw new Error("expected parameter `data` to be a 4x4 matrix of arrays, but was given a "+b+"x"+c+" matrix");this.model.set("matrix",a)},getMatrix:function(){var a=this.model.get("matrix"),b=this.model.get("transformations");return a=matrixUtilities.multiply(a,b.perspective),a=matrixUtilities.multiply(a,b.translate),a=matrixUtilities.multiply(a,b.rotate),a=matrixUtilities.multiply(a,b.skew),a=matrixUtilities.multiply(a,b.scale),matrixUtilities.flip(a)},getMatrixCSS:function(){return"matrix3d("+this.getMatrix().reduce(function(a,b){return a.push.apply(a,b),a},[]).join(",")+")"},rotate:function(a){return this.rotateZ(a)},rotateX:function(a){return this.rotate3d(1,0,0,a)},rotateY:function(a){return this.rotate3d(0,1,0,a)},rotateZ:function(a){return this.rotate3d(0,0,1,a)},scale:function(a,b){return this.scale3d(a,b)},scaleX:function(a){return this.scale3d(a)},scaleY:function(a){return this.scale3d(null,a)},scaleZ:function(a){return this.scale3d(null,null,a)},skewX:function(a){return this.skew(a)},skewY:function(a){return this.skew(null,a)},translate:function(a,b){return this.translate3d(a,b)},translateX:function(a){return this.translate3d(a)},translateY:function(a){return this.translate3d(null,a)},translateZ:function(a){return this.translate3d(null,null,a)},perspective:b.fluent(function(a){if(null==a&&(a=0),!b.isNumber(a))throw new TypeError("expected parameter `x` to be a Number, but was given a "+typeof a);this.model.set("transformations/perspective",transformToMatrix.perspective(a))}),rotate3d:b.fluent(function(a,c,d,e){if(null==a&&(a=0),null==c&&(c=0),null==d&&(d=0),null==e&&(e=0),!b.isNumber(a))throw new TypeError("expected parameter `x` to be a Number, but was given a "+typeof a);if(!b.isNumber(c))throw new TypeError("expected parameter `y` to be a Number, but was given a "+typeof c);if(!b.isNumber(d))throw new TypeError("expected parameter `z` to be a Number, but was given a "+typeof d);this.model.set("transformations/rotate",transformToMatrix.rotate3d(a,c,d,b.rad(e)))}),scale3d:b.fluent(function(a,c,d){if(null==a&&(a=1),null==c&&(c=1),null==d&&(d=1),!b.isNumber(a))throw new TypeError("expected parameter `x` to be a Number, but was given a "+typeof a);if(!b.isNumber(c))throw new TypeError("expected parameter `y` to be a Number, but was given a "+typeof c);if(!b.isNumber(d))throw new TypeError("expected parameter `z` to be a Number, but was given a "+typeof d);this.model.set("transformations/scale",transformToMatrix.scale3d(a,c,d))}),skew:b.fluent(function(a,c){null==a&&(a=0),null==c&&(c=0),this.model.set("transformations/skew",matrixUtilities.to3d(transformToMatrix.skew(b.rad(a),b.rad(c))))}),translate3d:b.fluent(function(a,c,d){if(null==a&&(a=0),null==c&&(c=0),null==d&&(d=0),!b.isNumber(a))throw new TypeError("expected parameter `x` to be a Number, but was given a "+typeof a);if(!b.isNumber(c))throw new TypeError("expected parameter `y` to be a Number, but was given a "+typeof c);if(!b.isNumber(d))throw new TypeError("expected parameter `z` to be a Number, but was given a "+typeof d);this.model.set("transformations/translate",transformToMatrix.translate3d(a,c,d))})}); -------------------------------------------------------------------------------- /dist/css-to-matrix.standalone.js: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | if(typeof exports === 'object') { 3 | module.exports = factory(); 4 | } 5 | else if(typeof define === 'function' && define.amd) { 6 | define('matrix-utilities', [], factory); 7 | } 8 | else { 9 | root['matrix-utilities'] = factory(); 10 | } 11 | }(this, function() { 12 | var matrixutilities; 13 | 14 | matrixutilities = (function() { 15 | var util; 16 | return util = { 17 | add: function(one, two) { 18 | var i, j, result, row, value, _i, _j, _len, _len1; 19 | if (one.length !== two.length) { 20 | throw new Error('Matrix y dimensions do not match'); 21 | } 22 | result = []; 23 | for (i = _i = 0, _len = one.length; _i < _len; i = ++_i) { 24 | row = one[i]; 25 | if (row.length !== two[i].length) { 26 | throw new Error("Matrix x dimensions do not match on row " + (i + 1)); 27 | } 28 | result[i] = []; 29 | for (j = _j = 0, _len1 = row.length; _j < _len1; j = ++_j) { 30 | value = row[j]; 31 | result[i][j] = value + two[i][j]; 32 | } 33 | } 34 | return result; 35 | }, 36 | multiply: function(one, two) { 37 | var j, k, l, result, row, size, sum, value, _i, _j, _len, _len1; 38 | if (one[0].length !== two.length) { 39 | throw new Error('Matrix 1\'s row count should equal matrix 2\'s column count'); 40 | } 41 | size = one[0].length; 42 | result = []; 43 | for (j = _i = 0, _len = two.length; _i < _len; j = ++_i) { 44 | row = two[j]; 45 | result[j] = []; 46 | for (k = _j = 0, _len1 = row.length; _j < _len1; k = ++_j) { 47 | value = row[k]; 48 | l = size; 49 | sum = 0; 50 | while (l--) { 51 | sum += one[j][l] * two[l][k]; 52 | } 53 | result[j][k] = sum; 54 | } 55 | } 56 | return result; 57 | }, 58 | flip: function(matrix) { 59 | var j, k, result, row, value, _i, _j, _len, _len1; 60 | result = []; 61 | for (j = _i = 0, _len = matrix.length; _i < _len; j = ++_i) { 62 | row = matrix[j]; 63 | for (k = _j = 0, _len1 = row.length; _j < _len1; k = ++_j) { 64 | value = row[k]; 65 | (result[k] || (result[k] = []))[j] = value; 66 | } 67 | } 68 | return result; 69 | }, 70 | to2d: function(matrix) { 71 | return [[matrix[0][0] || 1, matrix[0][1] || 0, matrix[0][3] || 0], [matrix[1][0] || 0, matrix[1][1] || 1, matrix[1][3] || 0]]; 72 | }, 73 | to3d: function(matrix) { 74 | return [[matrix[0][0] || 1, matrix[0][1] || 0, 0, matrix[0][2] || 0], [matrix[1][0] || 0, matrix[1][1] || 1, 0, matrix[1][2] || 0], [0, 0, 1, 0], [0, 0, 0, 1]]; 75 | }, 76 | Identity: function() { 77 | return [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; 78 | } 79 | }; 80 | })(); 81 | 82 | return matrixutilities; 83 | })); 84 | (function() { 85 | var fns, umd; 86 | 87 | umd = function(factory) { 88 | if (typeof exports === 'object') { 89 | return module.exports = factory; 90 | } else if (typeof define === 'function' && define.amd) { 91 | return define([], factory); 92 | } else { 93 | return this['transform-to-matrix'] = factory; 94 | } 95 | }; 96 | 97 | fns = { 98 | perspective: function(d) { 99 | return [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, -1 / d, 1]]; 100 | }, 101 | rotate: function(a) { 102 | return fns.rotateZ(a); 103 | }, 104 | rotateX: function(a) { 105 | return fns.rotate3d(1, 0, 0, a); 106 | }, 107 | rotateY: function(a) { 108 | return fns.rotate3d(0, 1, 0, a); 109 | }, 110 | rotateZ: function(a) { 111 | var c, n; 112 | c = Math.cos(a); 113 | n = Math.sin(a); 114 | return [[c, -n, 0], [n, c, 0]]; 115 | }, 116 | rotate3d: function(x, y, z, a) { 117 | var c, i, n, rs, s; 118 | s = x * x + y * y + z * z; 119 | c = Math.cos(a); 120 | n = Math.sin(a); 121 | i = 1 - c; 122 | rs = Math.sqrt(s) * n; 123 | return [[(x * x + (y * y + z * z) * c) / s, (x * y * i - z * rs) / s, (x * z * i + y * rs) / s, 0], [(x * y * i + z * rs) / s, (y * y + (x * x + z * z) * c) / s, (y * z * i - x * rs) / s, 0], [(x * z * i - y * rs) / s, (y * z * i + x * rs) / s, (z * z + (x * x + y * y) * c) / s, 0], [0, 0, 0, 1]]; 124 | }, 125 | scale: function(x, y) { 126 | return [[x, 0, 0], [0, y, 0]]; 127 | }, 128 | scaleX: function(x) { 129 | return fns.scale(x, 1); 130 | }, 131 | scaleY: function(y) { 132 | return fns.scale(1, y); 133 | }, 134 | scaleZ: function(z) { 135 | return fns.scale3d(1, 1, z); 136 | }, 137 | scale3d: function(x, y, z) { 138 | return [[x, 0, 0, 0], [0, y, 0, 0], [0, 0, z, 0], [0, 0, 0, 1]]; 139 | }, 140 | skew: function(x, y) { 141 | return [[1, Math.tan(x), 0], [Math.tan(y), 1, 0]]; 142 | }, 143 | skewX: function(x) { 144 | return [[1, Math.tan(x), 0], [0, 1, 0]]; 145 | }, 146 | skewY: function(y) { 147 | return [[1, 0, 0], [Math.tan(y), 1, 0]]; 148 | }, 149 | translate: function(x, y) { 150 | return [[1, 0, x], [0, 1, y]]; 151 | }, 152 | translateX: function(x) { 153 | return fns.translate(x, 0); 154 | }, 155 | translateY: function(y) { 156 | return fns.translate(0, y); 157 | }, 158 | translateZ: function(z) { 159 | return fns.translate3d(0, 0, z); 160 | }, 161 | translate3d: function(x, y, z) { 162 | return [[1, 0, 0, x], [0, 1, 0, y], [0, 0, 1, z], [0, 0, 0, 1]]; 163 | } 164 | }; 165 | 166 | umd(fns); 167 | 168 | }).call(this); 169 | 170 | (function(root, factory) { 171 | if(typeof exports === 'object') { 172 | module.exports = factory(); 173 | } 174 | else if(typeof define === 'function' && define.amd) { 175 | define('umodel', [], factory); 176 | } 177 | else { 178 | root.umodel = factory(); 179 | } 180 | }(this, function() { 181 | var umodel, _, 182 | __hasProp = {}.hasOwnProperty; 183 | 184 | _ = { 185 | extend: function(a, b) { 186 | var key; 187 | for (key in b) { 188 | if (!__hasProp.call(b, key)) continue; 189 | a[key] = b[key]; 190 | } 191 | return a; 192 | }, 193 | trim: (function() { 194 | var head, tail; 195 | if (''.trim) { 196 | return function(string) { 197 | return string.trim(); 198 | }; 199 | } else { 200 | head = /^\s\s*/; 201 | tail = /\s\s*$/; 202 | return function(string) { 203 | return string.replace(head, '').replace(tail, ''); 204 | }; 205 | } 206 | })() 207 | }; 208 | 209 | umodel = (function() { 210 | function umodel(_data, options) { 211 | this._data = _data != null ? _data : {}; 212 | this.options = { 213 | separator: '/' 214 | }; 215 | if (options) { 216 | _.extend(this.options, options); 217 | } 218 | this.events = {}; 219 | } 220 | 221 | umodel.prototype.get = function(key) { 222 | this.trigger('get', key); 223 | return this._get(this._split(key), this._data); 224 | }; 225 | 226 | umodel.prototype.set = function(key, value) { 227 | var old; 228 | old = this._get(this._split(key), this._data); 229 | this._set(this._split(key), value, false, this._data); 230 | return this.trigger('set', key, value, old); 231 | }; 232 | 233 | umodel.prototype.setnx = function(key, value) { 234 | var old; 235 | old = this._get(this._split(key), this._data); 236 | this._set(this._split(key), value, true, this._data); 237 | return this.trigger('setnx', key, value, old); 238 | }; 239 | 240 | umodel.prototype.on = function(eventAndProperty, fn) { 241 | var e, _results; 242 | if (fn) { 243 | return this._on(eventAndProperty, fn); 244 | } else { 245 | _results = []; 246 | for (e in eventAndProperty) { 247 | fn = eventAndProperty[e]; 248 | _results.push(this._on(e, fn)); 249 | } 250 | return _results; 251 | } 252 | }; 253 | 254 | umodel.prototype.trigger = function(event, path, value, oldValue) { 255 | var e, fn, fns, _ref, _results; 256 | if (path == null) { 257 | path = '*'; 258 | } 259 | path = this._normalize(path); 260 | if (event in this.events) { 261 | _ref = this.events[event]; 262 | _results = []; 263 | for (e in _ref) { 264 | fns = _ref[e]; 265 | if (e === '*' || (path + '/').indexOf(e + '/') === 0) { 266 | _results.push((function() { 267 | var _i, _len, _results1; 268 | _results1 = []; 269 | for (_i = 0, _len = fns.length; _i < _len; _i++) { 270 | fn = fns[_i]; 271 | if (oldValue != null) { 272 | _results1.push(fn.call(this, path, value, oldValue)); 273 | } else { 274 | _results1.push(fn.call(this, path, value)); 275 | } 276 | } 277 | return _results1; 278 | }).call(this)); 279 | } else { 280 | _results.push(void 0); 281 | } 282 | } 283 | return _results; 284 | } 285 | }; 286 | 287 | umodel.prototype._get = function(key, parent, accumulator) { 288 | var head; 289 | if (accumulator == null) { 290 | accumulator = []; 291 | } 292 | head = key.shift(); 293 | if (head) { 294 | if (!(head in parent)) { 295 | return void 0; 296 | } 297 | accumulator.push(head); 298 | return this._get(key, parent[head], accumulator); 299 | } 300 | return parent; 301 | }; 302 | 303 | umodel.prototype._set = function(key, value, nx, parent, accumulator) { 304 | var head; 305 | if (nx == null) { 306 | nx = false; 307 | } 308 | if (accumulator == null) { 309 | accumulator = []; 310 | } 311 | head = key.shift(); 312 | if (key.length) { 313 | if (!(head in parent)) { 314 | parent[head] = {}; 315 | } 316 | accumulator.push(head); 317 | return this._set(key, value, nx, parent[head], accumulator); 318 | } 319 | if (!(nx && head in parent)) { 320 | return parent[head] = value; 321 | } 322 | }; 323 | 324 | umodel.prototype._on = function(eventAndProperty, fn) { 325 | var event, events, parts, property, _i, _len, _results; 326 | parts = eventAndProperty.split(':'); 327 | events = parts[0].split(' '); 328 | property = this._normalize(parts[1] || '*'); 329 | _results = []; 330 | for (_i = 0, _len = events.length; _i < _len; _i++) { 331 | event = events[_i]; 332 | event = _.trim(event); 333 | if (!(event in this.events)) { 334 | this.events[event] = {}; 335 | } 336 | if (!(property in this.events[event])) { 337 | this.events[event][property] = []; 338 | } 339 | _results.push(this.events[event][property].push(fn)); 340 | } 341 | return _results; 342 | }; 343 | 344 | umodel.prototype._normalize = function(key) { 345 | var separator; 346 | separator = this.options.separator; 347 | key = _.trim(key); 348 | if (key.charAt(0) === separator) { 349 | key = key.slice(1); 350 | } 351 | if (key.charAt(key.length - 1) === separator) { 352 | key = key.slice(0, -1); 353 | } 354 | return key; 355 | }; 356 | 357 | umodel.prototype._split = function(key) { 358 | return (this._normalize(key)).split(this.options.separator); 359 | }; 360 | 361 | return umodel; 362 | 363 | })(); 364 | 365 | return umodel; 366 | })); 367 | 368 | var _ = { 369 | 370 | // convert strings like "55deg" or ".75rad" to floats (in radians) 371 | rad: function (string) { 372 | 373 | if (typeof string === 'string') { 374 | 375 | var angle = parseFloat(string, 10), 376 | isDegrees = string.indexOf('deg') > -1 377 | 378 | // convert deg -> rad? 379 | if (isDegrees) angle *= Math.PI / 180 380 | 381 | return angle 382 | 383 | } 384 | 385 | return string 386 | 387 | }, 388 | 389 | // shallow object extend 390 | extend: function (a, b) { 391 | 392 | for (var key in b) { 393 | a[key] = b[key] 394 | } 395 | 396 | return a 397 | 398 | }, 399 | 400 | // make functions return `this`, for easy chaining 401 | fluent: function (fn) { 402 | 403 | return function() { 404 | fn.apply(this, arguments) 405 | return this 406 | } 407 | 408 | }, 409 | 410 | isNumber: function (a) { 411 | return typeof a === 'number' 412 | } 413 | 414 | }; 415 | 416 | function CssToMatrix (data) { 417 | 418 | // default options 419 | this.model = new umodel({ 420 | matrix: new matrixUtilities.Identity(), 421 | transformations: { 422 | perspective: new matrixUtilities.Identity(), 423 | rotate: new matrixUtilities.Identity(), 424 | scale: new matrixUtilities.Identity(), 425 | skew: new matrixUtilities.Identity(), 426 | translate: new matrixUtilities.Identity() 427 | } 428 | }) 429 | 430 | // set data? 431 | if (data) { 432 | this.matrix(data) 433 | } 434 | 435 | } 436 | 437 | _.extend(CssToMatrix.prototype, { 438 | 439 | // set matrix in model 440 | matrix: function (data) { 441 | 442 | ////DEV 443 | if (data.length == null) 444 | throw new TypeError('expected parameter `data` to be an Array, but was given a ' + typeof data) 445 | 446 | var rows = data.length, 447 | columns = rows > 0 ? rows : 0 448 | 449 | if (rows !== 4 || columns !== 4) 450 | throw new Error('expected parameter `data` to be a 4x4 matrix of arrays, but was given a ' + rows + 'x' + columns + ' matrix') 451 | ////END DEV 452 | 453 | this.model.set('matrix', data) 454 | 455 | }, 456 | 457 | // apply transformations as defined in the model, and get back get calculated matrix 458 | getMatrix: function() { 459 | 460 | var matrix = this.model.get('matrix'), 461 | t = this.model.get('transformations') 462 | 463 | // perspective 464 | matrix = matrixUtilities.multiply(matrix, t.perspective) 465 | 466 | // translate 467 | matrix = matrixUtilities.multiply(matrix, t.translate) 468 | 469 | // rotate 470 | matrix = matrixUtilities.multiply(matrix, t.rotate) 471 | 472 | // skew 473 | matrix = matrixUtilities.multiply(matrix, t.skew) 474 | 475 | // scale 476 | matrix = matrixUtilities.multiply(matrix, t.scale) 477 | 478 | return matrixUtilities.flip(matrix) 479 | 480 | }, 481 | 482 | // get matrix formatted as a string that can be plugged right into CSS's `transform` function 483 | getMatrixCSS: function() { 484 | 485 | return 'matrix3d(' 486 | + this 487 | .getMatrix() 488 | .reduce(function (flat, row) { 489 | flat.push.apply(flat, row) 490 | return flat 491 | }, []) 492 | .join(',') 493 | + ')' 494 | 495 | }, 496 | 497 | // transform functions 498 | // 1-to-1 with their CSS equivalents 499 | rotate: function (a) { return this.rotateZ(a) }, 500 | rotateX: function (a) { return this.rotate3d(1, 0, 0, a) }, 501 | rotateY: function (a) { return this.rotate3d(0, 1, 0, a) }, 502 | rotateZ: function (a) { return this.rotate3d(0, 0, 1, a) }, 503 | scale: function (x, y) { return this.scale3d(x, y) }, 504 | scaleX: function (x) { return this.scale3d(x) }, 505 | scaleY: function (y) { return this.scale3d(null, y) }, 506 | scaleZ: function (z) { return this.scale3d(null, null, z) }, 507 | skewX: function (x) { return this.skew(x) }, 508 | skewY: function (y) { return this.skew(null, y) }, 509 | translate: function (x, y) { return this.translate3d(x, y) }, 510 | translateX: function (x) { return this.translate3d(x) }, 511 | translateY: function (y) { return this.translate3d(null, y) }, 512 | translateZ: function (z) { return this.translate3d(null, null, z) }, 513 | 514 | perspective: _.fluent(function (x) { 515 | 516 | if (x == null) { x = 0 } 517 | 518 | ////DEV 519 | if (!_.isNumber(x)) 520 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 521 | ////END DEV 522 | 523 | this.model.set('transformations/perspective', transformToMatrix.perspective(x)) 524 | 525 | }), 526 | 527 | rotate3d: _.fluent(function (x, y, z, a) { 528 | 529 | if (x == null) { x = 0 } 530 | if (y == null) { y = 0 } 531 | if (z == null) { z = 0 } 532 | if (a == null) { a = 0 } 533 | 534 | ////DEV 535 | if (!_.isNumber(x)) 536 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 537 | if (!_.isNumber(y)) 538 | throw new TypeError('expected parameter `y` to be a Number, but was given a ' + typeof y) 539 | if (!_.isNumber(z)) 540 | throw new TypeError('expected parameter `z` to be a Number, but was given a ' + typeof z) 541 | ////END DEV 542 | 543 | // if angle was passed as a string, convert it to a float first 544 | this.model.set('transformations/rotate', transformToMatrix.rotate3d(x, y, z, _.rad(a))) 545 | 546 | }), 547 | 548 | scale3d: _.fluent(function (x, y, z) { 549 | 550 | if (x == null) { x = 1 } 551 | if (y == null) { y = 1 } 552 | if (z == null) { z = 1 } 553 | 554 | ////DEV 555 | if (!_.isNumber(x)) 556 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 557 | if (!_.isNumber(y)) 558 | throw new TypeError('expected parameter `y` to be a Number, but was given a ' + typeof y) 559 | if (!_.isNumber(z)) 560 | throw new TypeError('expected parameter `z` to be a Number, but was given a ' + typeof z) 561 | ////END DEV 562 | 563 | this.model.set('transformations/scale', transformToMatrix.scale3d(x, y, z)) 564 | 565 | }), 566 | 567 | skew: _.fluent(function (x, y) { 568 | 569 | if (x == null) { x = 0 } 570 | if (y == null) { y = 0 } 571 | 572 | this.model.set('transformations/skew', matrixUtilities.to3d(transformToMatrix.skew(_.rad(x), _.rad(y)))) 573 | 574 | }), 575 | 576 | translate3d: _.fluent(function(x, y, z) { 577 | 578 | if (x == null) { x = 0 } 579 | if (y == null) { y = 0 } 580 | if (z == null) { z = 0 } 581 | 582 | ////DEV 583 | if (!_.isNumber(x)) 584 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 585 | if (!_.isNumber(y)) 586 | throw new TypeError('expected parameter `y` to be a Number, but was given a ' + typeof y) 587 | if (!_.isNumber(z)) 588 | throw new TypeError('expected parameter `z` to be a Number, but was given a ' + typeof z) 589 | ////END DEV 590 | 591 | this.model.set('transformations/translate', transformToMatrix.translate3d(x, y, z)) 592 | 593 | }) 594 | 595 | }) -------------------------------------------------------------------------------- /dist/css-to-matrix.standalone.min.js: -------------------------------------------------------------------------------- 1 | function a(a){this.model=new umodel({matrix:new matrixUtilities.Identity,transformations:{perspective:new matrixUtilities.Identity,rotate:new matrixUtilities.Identity,scale:new matrixUtilities.Identity,skew:new matrixUtilities.Identity,translate:new matrixUtilities.Identity}}),a&&this.matrix(a)}!function(a,b){"object"==typeof exports?module.exports=b():"function"==typeof define&&define.amd?define("matrix-utilities",[],b):a["matrix-utilities"]=b()}(this,function(){var a;return a=function(){var a;return a={add:function(a,b){var c,d,e,f,g,h,i,j,k;if(a.length!==b.length)throw new Error("Matrix y dimensions do not match");for(e=[],c=h=0,j=a.length;j>h;c=++h){if(f=a[c],f.length!==b[c].length)throw new Error("Matrix x dimensions do not match on row "+(c+1));for(e[c]=[],d=i=0,k=f.length;k>i;d=++i)g=f[d],e[c][d]=g+b[c][d]}return e},multiply:function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n;if(a[0].length!==b.length)throw new Error("Matrix 1's row count should equal matrix 2's column count");for(h=a[0].length,f=[],c=k=0,m=b.length;m>k;c=++k)for(g=b[c],f[c]=[],d=l=0,n=g.length;n>l;d=++l){for(j=g[d],e=h,i=0;e--;)i+=a[c][e]*b[e][d];f[c][d]=i}return f},flip:function(a){var b,c,d,e,f,g,h,i,j;for(d=[],b=g=0,i=a.length;i>g;b=++g)for(e=a[b],c=h=0,j=e.length;j>h;c=++h)f=e[c],(d[c]||(d[c]=[]))[b]=f;return d},to2d:function(a){return[[a[0][0]||1,a[0][1]||0,a[0][3]||0],[a[1][0]||0,a[1][1]||1,a[1][3]||0]]},to3d:function(a){return[[a[0][0]||1,a[0][1]||0,0,a[0][2]||0],[a[1][0]||0,a[1][1]||1,0,a[1][2]||0],[0,0,1,0],[0,0,0,1]]},Identity:function(){return[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]}}}()}),function(){var a,b;b=function(a){return"object"==typeof exports?module.exports=a:"function"==typeof define&&define.amd?define([],a):this["transform-to-matrix"]=a},a={perspective:function(a){return[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,-1/a,1]]},rotate:function(b){return a.rotateZ(b)},rotateX:function(b){return a.rotate3d(1,0,0,b)},rotateY:function(b){return a.rotate3d(0,1,0,b)},rotateZ:function(a){var b,c;return b=Math.cos(a),c=Math.sin(a),[[b,-c,0],[c,b,0]]},rotate3d:function(a,b,c,d){var e,f,g,h,i;return i=a*a+b*b+c*c,e=Math.cos(d),g=Math.sin(d),f=1-e,h=Math.sqrt(i)*g,[[(a*a+(b*b+c*c)*e)/i,(a*b*f-c*h)/i,(a*c*f+b*h)/i,0],[(a*b*f+c*h)/i,(b*b+(a*a+c*c)*e)/i,(b*c*f-a*h)/i,0],[(a*c*f-b*h)/i,(b*c*f+a*h)/i,(c*c+(a*a+b*b)*e)/i,0],[0,0,0,1]]},scale:function(a,b){return[[a,0,0],[0,b,0]]},scaleX:function(b){return a.scale(b,1)},scaleY:function(b){return a.scale(1,b)},scaleZ:function(b){return a.scale3d(1,1,b)},scale3d:function(a,b,c){return[[a,0,0,0],[0,b,0,0],[0,0,c,0],[0,0,0,1]]},skew:function(a,b){return[[1,Math.tan(a),0],[Math.tan(b),1,0]]},skewX:function(a){return[[1,Math.tan(a),0],[0,1,0]]},skewY:function(a){return[[1,0,0],[Math.tan(a),1,0]]},translate:function(a,b){return[[1,0,a],[0,1,b]]},translateX:function(b){return a.translate(b,0)},translateY:function(b){return a.translate(0,b)},translateZ:function(b){return a.translate3d(0,0,b)},translate3d:function(a,b,c){return[[1,0,0,a],[0,1,0,b],[0,0,1,c],[0,0,0,1]]}},b(a)}.call(this),function(a,b){"object"==typeof exports?module.exports=b():"function"==typeof define&&define.amd?define("umodel",[],b):a.umodel=b()}(this,function(){var a,b,c={}.hasOwnProperty;return b={extend:function(a,b){var d;for(d in b)c.call(b,d)&&(a[d]=b[d]);return a},trim:function(){var a,b;return"".trim?function(a){return a.trim()}:(a=/^\s\s*/,b=/\s\s*$/,function(c){return c.replace(a,"").replace(b,"")})}()},a=function(){function a(a,c){this._data=null!=a?a:{},this.options={separator:"/"},c&&b.extend(this.options,c),this.events={}}return a.prototype.get=function(a){return this.trigger("get",a),this._get(this._split(a),this._data)},a.prototype.set=function(a,b){var c;return c=this._get(this._split(a),this._data),this._set(this._split(a),b,!1,this._data),this.trigger("set",a,b,c)},a.prototype.setnx=function(a,b){var c;return c=this._get(this._split(a),this._data),this._set(this._split(a),b,!0,this._data),this.trigger("setnx",a,b,c)},a.prototype.on=function(a,b){var c,d;if(b)return this._on(a,b);d=[];for(c in a)b=a[c],d.push(this._on(c,b));return d},a.prototype.trigger=function(a,b,c,d){var e,f,g,h,i;if(null==b&&(b="*"),b=this._normalize(b),a in this.events){h=this.events[a],i=[];for(e in h)g=h[e],i.push("*"===e||0===(b+"/").indexOf(e+"/")?function(){var a,e,h;for(h=[],a=0,e=g.length;e>a;a++)f=g[a],h.push(null!=d?f.call(this,b,c,d):f.call(this,b,c));return h}.call(this):void 0);return i}},a.prototype._get=function(a,b,c){var d;return null==c&&(c=[]),d=a.shift(),d?d in b?(c.push(d),this._get(a,b[d],c)):void 0:b},a.prototype._set=function(a,b,c,d,e){var f;return null==c&&(c=!1),null==e&&(e=[]),f=a.shift(),a.length?(f in d||(d[f]={}),e.push(f),this._set(a,b,c,d[f],e)):c&&f in d?void 0:d[f]=b},a.prototype._on=function(a,c){var d,e,f,g,h,i,j;for(f=a.split(":"),e=f[0].split(" "),g=this._normalize(f[1]||"*"),j=[],h=0,i=e.length;i>h;h++)d=e[h],d=b.trim(d),d in this.events||(this.events[d]={}),g in this.events[d]||(this.events[d][g]=[]),j.push(this.events[d][g].push(c));return j},a.prototype._normalize=function(a){var c;return c=this.options.separator,a=b.trim(a),a.charAt(0)===c&&(a=a.slice(1)),a.charAt(a.length-1)===c&&(a=a.slice(0,-1)),a},a.prototype._split=function(a){return this._normalize(a).split(this.options.separator)},a}()});var b={rad:function(a){if("string"==typeof a){var b=parseFloat(a,10),c=a.indexOf("deg")>-1;return c&&(b*=Math.PI/180),b}return a},extend:function(a,b){for(var c in b)a[c]=b[c];return a},fluent:function(a){return function(){return a.apply(this,arguments),this}},isNumber:function(a){return"number"==typeof a}};b.extend(a.prototype,{matrix:function(a){if(null==a.length)throw new TypeError("expected parameter `data` to be an Array, but was given a "+typeof a);var b=a.length,c=b>0?b:0;if(4!==b||4!==c)throw new Error("expected parameter `data` to be a 4x4 matrix of arrays, but was given a "+b+"x"+c+" matrix");this.model.set("matrix",a)},getMatrix:function(){var a=this.model.get("matrix"),b=this.model.get("transformations");return a=matrixUtilities.multiply(a,b.perspective),a=matrixUtilities.multiply(a,b.translate),a=matrixUtilities.multiply(a,b.rotate),a=matrixUtilities.multiply(a,b.skew),a=matrixUtilities.multiply(a,b.scale),matrixUtilities.flip(a)},getMatrixCSS:function(){return"matrix3d("+this.getMatrix().reduce(function(a,b){return a.push.apply(a,b),a},[]).join(",")+")"},rotate:function(a){return this.rotateZ(a)},rotateX:function(a){return this.rotate3d(1,0,0,a)},rotateY:function(a){return this.rotate3d(0,1,0,a)},rotateZ:function(a){return this.rotate3d(0,0,1,a)},scale:function(a,b){return this.scale3d(a,b)},scaleX:function(a){return this.scale3d(a)},scaleY:function(a){return this.scale3d(null,a)},scaleZ:function(a){return this.scale3d(null,null,a)},skewX:function(a){return this.skew(a)},skewY:function(a){return this.skew(null,a)},translate:function(a,b){return this.translate3d(a,b)},translateX:function(a){return this.translate3d(a)},translateY:function(a){return this.translate3d(null,a)},translateZ:function(a){return this.translate3d(null,null,a)},perspective:b.fluent(function(a){if(null==a&&(a=0),!b.isNumber(a))throw new TypeError("expected parameter `x` to be a Number, but was given a "+typeof a);this.model.set("transformations/perspective",transformToMatrix.perspective(a))}),rotate3d:b.fluent(function(a,c,d,e){if(null==a&&(a=0),null==c&&(c=0),null==d&&(d=0),null==e&&(e=0),!b.isNumber(a))throw new TypeError("expected parameter `x` to be a Number, but was given a "+typeof a);if(!b.isNumber(c))throw new TypeError("expected parameter `y` to be a Number, but was given a "+typeof c);if(!b.isNumber(d))throw new TypeError("expected parameter `z` to be a Number, but was given a "+typeof d);this.model.set("transformations/rotate",transformToMatrix.rotate3d(a,c,d,b.rad(e)))}),scale3d:b.fluent(function(a,c,d){if(null==a&&(a=1),null==c&&(c=1),null==d&&(d=1),!b.isNumber(a))throw new TypeError("expected parameter `x` to be a Number, but was given a "+typeof a);if(!b.isNumber(c))throw new TypeError("expected parameter `y` to be a Number, but was given a "+typeof c);if(!b.isNumber(d))throw new TypeError("expected parameter `z` to be a Number, but was given a "+typeof d);this.model.set("transformations/scale",transformToMatrix.scale3d(a,c,d))}),skew:b.fluent(function(a,c){null==a&&(a=0),null==c&&(c=0),this.model.set("transformations/skew",matrixUtilities.to3d(transformToMatrix.skew(b.rad(a),b.rad(c))))}),translate3d:b.fluent(function(a,c,d){if(null==a&&(a=0),null==c&&(c=0),null==d&&(d=0),!b.isNumber(a))throw new TypeError("expected parameter `x` to be a Number, but was given a "+typeof a);if(!b.isNumber(c))throw new TypeError("expected parameter `y` to be a Number, but was given a "+typeof c);if(!b.isNumber(d))throw new TypeError("expected parameter `z` to be a Number, but was given a "+typeof d);this.model.set("transformations/translate",transformToMatrix.translate3d(a,c,d))})}); -------------------------------------------------------------------------------- /grunt-umd-template.hbs: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | if(typeof exports === 'object') { 3 | module.exports = factory(require('transform-to-matrix'), require('matrix-utilities'), require('umodel')); 4 | } 5 | else if(typeof define === 'function' && define.amd) { 6 | define('css-to-matrix', ['transform-to-matrix', 'matrix-utilities', 'umodel'], factory); 7 | } 8 | else { 9 | root['css-to-matrix'] = factory(root['transform-to-matrix'], root['matrix-utilities'], root.umodel); 10 | } 11 | }(this, function(transformToMatrix, matrixUtilities, umodel) { 12 | {{{code}}} 13 | return {{objectToExport}}; 14 | })); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-to-matrix", 3 | "version": "1.0.0", 4 | "description": "A little library for converting compound CSS transforms into their matrix equivalents", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/eighttrackmind/css-to-matrix.git" 8 | }, 9 | "author": "Boris Cherny ", 10 | "contributors": [ 11 | { 12 | "name": "Boris Cherny", 13 | "email": "boris@performancejs.com" 14 | } 15 | ], 16 | "devDependencies": { 17 | "annie": "~0.1.3", 18 | "change-case": "^2.1.1", 19 | "grunt": "~0.4.1", 20 | "grunt-contrib-coffee": "~0.7.0", 21 | "grunt-contrib-concat": "^0.4.0", 22 | "grunt-contrib-uglify": "~0.2.4", 23 | "grunt-contrib-watch": "^0.6.1", 24 | "grunt-regex-replace": "~0.2.5", 25 | "grunt-umd": "^1.7.4" 26 | }, 27 | "license": "MIT", 28 | "readmeFilename": "README.md", 29 | "dependencies": { 30 | "transform-to-matrix": "~0", 31 | "matrix-utilities": "^1.2", 32 | "umodel": "~0" 33 | }, 34 | "scripts": { 35 | "test": "grunt test" 36 | }, 37 | "keywords": [ 38 | "transform", 39 | "3d", 40 | "css", 41 | "matrix", 42 | "identity", 43 | "multiply", 44 | "css3" 45 | ], 46 | "main": "dist/css-to-matrix.js", 47 | "testling": { 48 | "files": "test/test.js", 49 | "browsers": { 50 | "ie": [ 51 | 6, 52 | 7, 53 | 8, 54 | 9 55 | ], 56 | "firefox": [ 57 | "nightly", 58 | 24, 59 | 23, 60 | 22, 61 | 21, 62 | 20 63 | ], 64 | "chrome": [ 65 | "canary", 66 | 30, 67 | 25, 68 | 20, 69 | 15, 70 | 10 71 | ], 72 | "safari": [ 73 | 6, 74 | 5, 75 | 4, 76 | 3 77 | ], 78 | "opera": [ 79 | "next", 80 | 16, 81 | 15, 82 | 14, 83 | 13, 84 | 12 85 | ], 86 | "iphone": [ 87 | 6 88 | ], 89 | "ipad": [ 90 | 6 91 | ], 92 | "android": [ 93 | 4 94 | ] 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/css-to-matrix.js: -------------------------------------------------------------------------------- 1 | var _ = { 2 | 3 | // convert strings like "55deg" or ".75rad" to floats (in radians) 4 | rad: function (string) { 5 | 6 | if (typeof string === 'string') { 7 | 8 | var angle = parseFloat(string, 10), 9 | isDegrees = string.indexOf('deg') > -1 10 | 11 | // convert deg -> rad? 12 | if (isDegrees) angle *= Math.PI / 180 13 | 14 | return angle 15 | 16 | } 17 | 18 | return string 19 | 20 | }, 21 | 22 | // shallow object extend 23 | extend: function (a, b) { 24 | 25 | for (var key in b) { 26 | a[key] = b[key] 27 | } 28 | 29 | return a 30 | 31 | }, 32 | 33 | // make functions return `this`, for easy chaining 34 | fluent: function (fn) { 35 | 36 | return function() { 37 | fn.apply(this, arguments) 38 | return this 39 | } 40 | 41 | }, 42 | 43 | isNumber: function (a) { 44 | return typeof a === 'number' 45 | } 46 | 47 | }; 48 | 49 | function CssToMatrix (data) { 50 | 51 | // default options 52 | this.model = new umodel({ 53 | matrix: new matrixUtilities.Identity(), 54 | transformations: { 55 | perspective: new matrixUtilities.Identity(), 56 | rotate: new matrixUtilities.Identity(), 57 | scale: new matrixUtilities.Identity(), 58 | skew: new matrixUtilities.Identity(), 59 | translate: new matrixUtilities.Identity() 60 | } 61 | }) 62 | 63 | // set data? 64 | if (data) { 65 | this.matrix(data) 66 | } 67 | 68 | } 69 | 70 | _.extend(CssToMatrix.prototype, { 71 | 72 | // set matrix in model 73 | matrix: function (data) { 74 | 75 | ////DEV 76 | if (data.length == null) 77 | throw new TypeError('expected parameter `data` to be an Array, but was given a ' + typeof data) 78 | 79 | var rows = data.length, 80 | columns = rows > 0 ? rows : 0 81 | 82 | if (rows !== 4 || columns !== 4) 83 | throw new Error('expected parameter `data` to be a 4x4 matrix of arrays, but was given a ' + rows + 'x' + columns + ' matrix') 84 | ////END DEV 85 | 86 | this.model.set('matrix', data) 87 | 88 | }, 89 | 90 | // apply transformations as defined in the model, and get back get calculated matrix 91 | getMatrix: function() { 92 | 93 | var matrix = this.model.get('matrix'), 94 | t = this.model.get('transformations') 95 | 96 | // perspective 97 | matrix = matrixUtilities.multiply(matrix, t.perspective) 98 | 99 | // translate 100 | matrix = matrixUtilities.multiply(matrix, t.translate) 101 | 102 | // rotate 103 | matrix = matrixUtilities.multiply(matrix, t.rotate) 104 | 105 | // skew 106 | matrix = matrixUtilities.multiply(matrix, t.skew) 107 | 108 | // scale 109 | matrix = matrixUtilities.multiply(matrix, t.scale) 110 | 111 | return matrixUtilities.flip(matrix) 112 | 113 | }, 114 | 115 | // get matrix formatted as a string that can be plugged right into CSS's `transform` function 116 | getMatrixCSS: function() { 117 | 118 | return 'matrix3d(' 119 | + this 120 | .getMatrix() 121 | .reduce(function (flat, row) { 122 | flat.push.apply(flat, row) 123 | return flat 124 | }, []) 125 | .join(',') 126 | + ')' 127 | 128 | }, 129 | 130 | // transform functions 131 | // 1-to-1 with their CSS equivalents 132 | rotate: function (a) { return this.rotateZ(a) }, 133 | rotateX: function (a) { return this.rotate3d(1, 0, 0, a) }, 134 | rotateY: function (a) { return this.rotate3d(0, 1, 0, a) }, 135 | rotateZ: function (a) { return this.rotate3d(0, 0, 1, a) }, 136 | scale: function (x, y) { return this.scale3d(x, y) }, 137 | scaleX: function (x) { return this.scale3d(x) }, 138 | scaleY: function (y) { return this.scale3d(null, y) }, 139 | scaleZ: function (z) { return this.scale3d(null, null, z) }, 140 | skewX: function (x) { return this.skew(x) }, 141 | skewY: function (y) { return this.skew(null, y) }, 142 | translate: function (x, y) { return this.translate3d(x, y) }, 143 | translateX: function (x) { return this.translate3d(x) }, 144 | translateY: function (y) { return this.translate3d(null, y) }, 145 | translateZ: function (z) { return this.translate3d(null, null, z) }, 146 | 147 | perspective: _.fluent(function (x) { 148 | 149 | if (x == null) { x = 0 } 150 | 151 | ////DEV 152 | if (!_.isNumber(x)) 153 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 154 | ////END DEV 155 | 156 | this.model.set('transformations/perspective', transformToMatrix.perspective(x)) 157 | 158 | }), 159 | 160 | rotate3d: _.fluent(function (x, y, z, a) { 161 | 162 | if (x == null) { x = 0 } 163 | if (y == null) { y = 0 } 164 | if (z == null) { z = 0 } 165 | if (a == null) { a = 0 } 166 | 167 | ////DEV 168 | if (!_.isNumber(x)) 169 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 170 | if (!_.isNumber(y)) 171 | throw new TypeError('expected parameter `y` to be a Number, but was given a ' + typeof y) 172 | if (!_.isNumber(z)) 173 | throw new TypeError('expected parameter `z` to be a Number, but was given a ' + typeof z) 174 | ////END DEV 175 | 176 | // if angle was passed as a string, convert it to a float first 177 | this.model.set('transformations/rotate', transformToMatrix.rotate3d(x, y, z, _.rad(a))) 178 | 179 | }), 180 | 181 | scale3d: _.fluent(function (x, y, z) { 182 | 183 | if (x == null) { x = 1 } 184 | if (y == null) { y = 1 } 185 | if (z == null) { z = 1 } 186 | 187 | ////DEV 188 | if (!_.isNumber(x)) 189 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 190 | if (!_.isNumber(y)) 191 | throw new TypeError('expected parameter `y` to be a Number, but was given a ' + typeof y) 192 | if (!_.isNumber(z)) 193 | throw new TypeError('expected parameter `z` to be a Number, but was given a ' + typeof z) 194 | ////END DEV 195 | 196 | this.model.set('transformations/scale', transformToMatrix.scale3d(x, y, z)) 197 | 198 | }), 199 | 200 | skew: _.fluent(function (x, y) { 201 | 202 | if (x == null) { x = 0 } 203 | if (y == null) { y = 0 } 204 | 205 | this.model.set('transformations/skew', matrixUtilities.to3d(transformToMatrix.skew(_.rad(x), _.rad(y)))) 206 | 207 | }), 208 | 209 | translate3d: _.fluent(function(x, y, z) { 210 | 211 | if (x == null) { x = 0 } 212 | if (y == null) { y = 0 } 213 | if (z == null) { z = 0 } 214 | 215 | ////DEV 216 | if (!_.isNumber(x)) 217 | throw new TypeError('expected parameter `x` to be a Number, but was given a ' + typeof x) 218 | if (!_.isNumber(y)) 219 | throw new TypeError('expected parameter `y` to be a Number, but was given a ' + typeof y) 220 | if (!_.isNumber(z)) 221 | throw new TypeError('expected parameter `z` to be a Number, but was given a ' + typeof z) 222 | ////END DEV 223 | 224 | this.model.set('transformations/translate', transformToMatrix.translate3d(x, y, z)) 225 | 226 | }) 227 | 228 | }) -------------------------------------------------------------------------------- /test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherny/css-to-matrix/c7f4ca861624e30067ce885d8b4513eb9a5802cc/test/.DS_Store -------------------------------------------------------------------------------- /test/chai.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 3 | /** 4 | * Require the given path. 5 | * 6 | * @param {String} path 7 | * @return {Object} exports 8 | * @api public 9 | */ 10 | 11 | function require(path, parent, orig) { 12 | var resolved = require.resolve(path); 13 | 14 | // lookup failed 15 | if (null == resolved) { 16 | orig = orig || path; 17 | parent = parent || 'root'; 18 | var err = new Error('Failed to require "' + orig + '" from "' + parent + '"'); 19 | err.path = orig; 20 | err.parent = parent; 21 | err.require = true; 22 | throw err; 23 | } 24 | 25 | var module = require.modules[resolved]; 26 | 27 | // perform real require() 28 | // by invoking the module's 29 | // registered function 30 | if (!module._resolving && !module.exports) { 31 | var mod = {}; 32 | mod.exports = {}; 33 | mod.client = mod.component = true; 34 | module._resolving = true; 35 | module.call(this, mod.exports, require.relative(resolved), mod); 36 | delete module._resolving; 37 | module.exports = mod.exports; 38 | } 39 | 40 | return module.exports; 41 | } 42 | 43 | /** 44 | * Registered modules. 45 | */ 46 | 47 | require.modules = {}; 48 | 49 | /** 50 | * Registered aliases. 51 | */ 52 | 53 | require.aliases = {}; 54 | 55 | /** 56 | * Resolve `path`. 57 | * 58 | * Lookup: 59 | * 60 | * - PATH/index.js 61 | * - PATH.js 62 | * - PATH 63 | * 64 | * @param {String} path 65 | * @return {String} path or null 66 | * @api private 67 | */ 68 | 69 | require.resolve = function(path) { 70 | if (path.charAt(0) === '/') path = path.slice(1); 71 | 72 | var paths = [ 73 | path, 74 | path + '.js', 75 | path + '.json', 76 | path + '/index.js', 77 | path + '/index.json' 78 | ]; 79 | 80 | for (var i = 0; i < paths.length; i++) { 81 | var path = paths[i]; 82 | if (require.modules.hasOwnProperty(path)) return path; 83 | if (require.aliases.hasOwnProperty(path)) return require.aliases[path]; 84 | } 85 | }; 86 | 87 | /** 88 | * Normalize `path` relative to the current path. 89 | * 90 | * @param {String} curr 91 | * @param {String} path 92 | * @return {String} 93 | * @api private 94 | */ 95 | 96 | require.normalize = function(curr, path) { 97 | var segs = []; 98 | 99 | if ('.' != path.charAt(0)) return path; 100 | 101 | curr = curr.split('/'); 102 | path = path.split('/'); 103 | 104 | for (var i = 0; i < path.length; ++i) { 105 | if ('..' == path[i]) { 106 | curr.pop(); 107 | } else if ('.' != path[i] && '' != path[i]) { 108 | segs.push(path[i]); 109 | } 110 | } 111 | 112 | return curr.concat(segs).join('/'); 113 | }; 114 | 115 | /** 116 | * Register module at `path` with callback `definition`. 117 | * 118 | * @param {String} path 119 | * @param {Function} definition 120 | * @api private 121 | */ 122 | 123 | require.register = function(path, definition) { 124 | require.modules[path] = definition; 125 | }; 126 | 127 | /** 128 | * Alias a module definition. 129 | * 130 | * @param {String} from 131 | * @param {String} to 132 | * @api private 133 | */ 134 | 135 | require.alias = function(from, to) { 136 | if (!require.modules.hasOwnProperty(from)) { 137 | throw new Error('Failed to alias "' + from + '", it does not exist'); 138 | } 139 | require.aliases[to] = from; 140 | }; 141 | 142 | /** 143 | * Return a require function relative to the `parent` path. 144 | * 145 | * @param {String} parent 146 | * @return {Function} 147 | * @api private 148 | */ 149 | 150 | require.relative = function(parent) { 151 | var p = require.normalize(parent, '..'); 152 | 153 | /** 154 | * lastIndexOf helper. 155 | */ 156 | 157 | function lastIndexOf(arr, obj) { 158 | var i = arr.length; 159 | while (i--) { 160 | if (arr[i] === obj) return i; 161 | } 162 | return -1; 163 | } 164 | 165 | /** 166 | * The relative require() itself. 167 | */ 168 | 169 | function localRequire(path) { 170 | var resolved = localRequire.resolve(path); 171 | return require(resolved, parent, path); 172 | } 173 | 174 | /** 175 | * Resolve relative to the parent. 176 | */ 177 | 178 | localRequire.resolve = function(path) { 179 | var c = path.charAt(0); 180 | if ('/' == c) return path.slice(1); 181 | if ('.' == c) return require.normalize(p, path); 182 | 183 | // resolve deps by returning 184 | // the dep in the nearest "deps" 185 | // directory 186 | var segs = parent.split('/'); 187 | var i = lastIndexOf(segs, 'deps') + 1; 188 | if (!i) i = 0; 189 | path = segs.slice(0, i + 1).join('/') + '/deps/' + path; 190 | return path; 191 | }; 192 | 193 | /** 194 | * Check if module is defined at `path`. 195 | */ 196 | 197 | localRequire.exists = function(path) { 198 | return require.modules.hasOwnProperty(localRequire.resolve(path)); 199 | }; 200 | 201 | return localRequire; 202 | }; 203 | require.register("chaijs-assertion-error/index.js", function(exports, require, module){ 204 | /*! 205 | * assertion-error 206 | * Copyright(c) 2013 Jake Luer 207 | * MIT Licensed 208 | */ 209 | 210 | /*! 211 | * Return a function that will copy properties from 212 | * one object to another excluding any originally 213 | * listed. Returned function will create a new `{}`. 214 | * 215 | * @param {String} excluded properties ... 216 | * @return {Function} 217 | */ 218 | 219 | function exclude () { 220 | var excludes = [].slice.call(arguments); 221 | 222 | function excludeProps (res, obj) { 223 | Object.keys(obj).forEach(function (key) { 224 | if (!~excludes.indexOf(key)) res[key] = obj[key]; 225 | }); 226 | } 227 | 228 | return function extendExclude () { 229 | var args = [].slice.call(arguments) 230 | , i = 0 231 | , res = {}; 232 | 233 | for (; i < args.length; i++) { 234 | excludeProps(res, args[i]); 235 | } 236 | 237 | return res; 238 | }; 239 | }; 240 | 241 | /*! 242 | * Primary Exports 243 | */ 244 | 245 | module.exports = AssertionError; 246 | 247 | /** 248 | * ### AssertionError 249 | * 250 | * An extension of the JavaScript `Error` constructor for 251 | * assertion and validation scenarios. 252 | * 253 | * @param {String} message 254 | * @param {Object} properties to include (optional) 255 | * @param {callee} start stack function (optional) 256 | */ 257 | 258 | function AssertionError (message, _props, ssf) { 259 | var extend = exclude('name', 'message', 'stack', 'constructor', 'toJSON') 260 | , props = extend(_props || {}); 261 | 262 | // default values 263 | this.message = message || 'Unspecified AssertionError'; 264 | this.showDiff = false; 265 | 266 | // copy from properties 267 | for (var key in props) { 268 | this[key] = props[key]; 269 | } 270 | 271 | // capture stack trace 272 | ssf = ssf || arguments.callee; 273 | if (ssf && Error.captureStackTrace) { 274 | Error.captureStackTrace(this, ssf); 275 | } 276 | } 277 | 278 | /*! 279 | * Inherit from Error.prototype 280 | */ 281 | 282 | AssertionError.prototype = Object.create(Error.prototype); 283 | 284 | /*! 285 | * Statically set name 286 | */ 287 | 288 | AssertionError.prototype.name = 'AssertionError'; 289 | 290 | /*! 291 | * Ensure correct constructor 292 | */ 293 | 294 | AssertionError.prototype.constructor = AssertionError; 295 | 296 | /** 297 | * Allow errors to be converted to JSON for static transfer. 298 | * 299 | * @param {Boolean} include stack (default: `true`) 300 | * @return {Object} object that can be `JSON.stringify` 301 | */ 302 | 303 | AssertionError.prototype.toJSON = function (stack) { 304 | var extend = exclude('constructor', 'toJSON', 'stack') 305 | , props = extend({ name: this.name }, this); 306 | 307 | // include stack if exists and not turned off 308 | if (false !== stack && this.stack) { 309 | props.stack = this.stack; 310 | } 311 | 312 | return props; 313 | }; 314 | 315 | }); 316 | require.register("chaijs-type-detect/lib/type.js", function(exports, require, module){ 317 | /*! 318 | * type-detect 319 | * Copyright(c) 2013 jake luer 320 | * MIT Licensed 321 | */ 322 | 323 | /*! 324 | * Primary Exports 325 | */ 326 | 327 | var exports = module.exports = getType; 328 | 329 | /*! 330 | * Detectable javascript natives 331 | */ 332 | 333 | var natives = { 334 | '[object Array]': 'array' 335 | , '[object RegExp]': 'regexp' 336 | , '[object Function]': 'function' 337 | , '[object Arguments]': 'arguments' 338 | , '[object Date]': 'date' 339 | }; 340 | 341 | /** 342 | * ### typeOf (obj) 343 | * 344 | * Use several different techniques to determine 345 | * the type of object being tested. 346 | * 347 | * 348 | * @param {Mixed} object 349 | * @return {String} object type 350 | * @api public 351 | */ 352 | 353 | function getType (obj) { 354 | var str = Object.prototype.toString.call(obj); 355 | if (natives[str]) return natives[str]; 356 | if (obj === null) return 'null'; 357 | if (obj === undefined) return 'undefined'; 358 | if (obj === Object(obj)) return 'object'; 359 | return typeof obj; 360 | } 361 | 362 | exports.Library = Library; 363 | 364 | /** 365 | * ### Library 366 | * 367 | * Create a repository for custom type detection. 368 | * 369 | * ```js 370 | * var lib = new type.Library; 371 | * ``` 372 | * 373 | */ 374 | 375 | function Library () { 376 | this.tests = {}; 377 | } 378 | 379 | /** 380 | * #### .of (obj) 381 | * 382 | * Expose replacement `typeof` detection to the library. 383 | * 384 | * ```js 385 | * if ('string' === lib.of('hello world')) { 386 | * // ... 387 | * } 388 | * ``` 389 | * 390 | * @param {Mixed} object to test 391 | * @return {String} type 392 | */ 393 | 394 | Library.prototype.of = getType; 395 | 396 | /** 397 | * #### .define (type, test) 398 | * 399 | * Add a test to for the `.test()` assertion. 400 | * 401 | * Can be defined as a regular expression: 402 | * 403 | * ```js 404 | * lib.define('int', /^[0-9]+$/); 405 | * ``` 406 | * 407 | * ... or as a function: 408 | * 409 | * ```js 410 | * lib.define('bln', function (obj) { 411 | * if ('boolean' === lib.of(obj)) return true; 412 | * var blns = [ 'yes', 'no', 'true', 'false', 1, 0 ]; 413 | * if ('string' === lib.of(obj)) obj = obj.toLowerCase(); 414 | * return !! ~blns.indexOf(obj); 415 | * }); 416 | * ``` 417 | * 418 | * @param {String} type 419 | * @param {RegExp|Function} test 420 | * @api public 421 | */ 422 | 423 | Library.prototype.define = function (type, test) { 424 | if (arguments.length === 1) return this.tests[type]; 425 | this.tests[type] = test; 426 | return this; 427 | }; 428 | 429 | /** 430 | * #### .test (obj, test) 431 | * 432 | * Assert that an object is of type. Will first 433 | * check natives, and if that does not pass it will 434 | * use the user defined custom tests. 435 | * 436 | * ```js 437 | * assert(lib.test('1', 'int')); 438 | * assert(lib.test('yes', 'bln')); 439 | * ``` 440 | * 441 | * @param {Mixed} object 442 | * @param {String} type 443 | * @return {Boolean} result 444 | * @api public 445 | */ 446 | 447 | Library.prototype.test = function (obj, type) { 448 | if (type === getType(obj)) return true; 449 | var test = this.tests[type]; 450 | 451 | if (test && 'regexp' === getType(test)) { 452 | return test.test(obj); 453 | } else if (test && 'function' === getType(test)) { 454 | return test(obj); 455 | } else { 456 | throw new ReferenceError('Type test "' + type + '" not defined or invalid.'); 457 | } 458 | }; 459 | 460 | }); 461 | require.register("chaijs-deep-eql/lib/eql.js", function(exports, require, module){ 462 | /*! 463 | * deep-eql 464 | * Copyright(c) 2013 Jake Luer 465 | * MIT Licensed 466 | */ 467 | 468 | /*! 469 | * Module dependencies 470 | */ 471 | 472 | var type = require('type-detect'); 473 | 474 | /*! 475 | * Buffer.isBuffer browser shim 476 | */ 477 | 478 | var Buffer; 479 | try { Buffer = require('buffer').Buffer; } 480 | catch(ex) { 481 | Buffer = {}; 482 | Buffer.isBuffer = function() { return false; } 483 | } 484 | 485 | /*! 486 | * Primary Export 487 | */ 488 | 489 | module.exports = deepEqual; 490 | 491 | /** 492 | * Assert super-strict (egal) equality between 493 | * two objects of any type. 494 | * 495 | * @param {Mixed} a 496 | * @param {Mixed} b 497 | * @param {Array} memoised (optional) 498 | * @return {Boolean} equal match 499 | */ 500 | 501 | function deepEqual(a, b, m) { 502 | if (sameValue(a, b)) { 503 | return true; 504 | } else if ('date' === type(a)) { 505 | return dateEqual(a, b); 506 | } else if ('regexp' === type(a)) { 507 | return regexpEqual(a, b); 508 | } else if (Buffer.isBuffer(a)) { 509 | return bufferEqual(a, b); 510 | } else if ('arguments' === type(a)) { 511 | return argumentsEqual(a, b, m); 512 | } else if (!typeEqual(a, b)) { 513 | return false; 514 | } else if (('object' !== type(a) && 'object' !== type(b)) 515 | && ('array' !== type(a) && 'array' !== type(b))) { 516 | return sameValue(a, b); 517 | } else { 518 | return objectEqual(a, b, m); 519 | } 520 | } 521 | 522 | /*! 523 | * Strict (egal) equality test. Ensures that NaN always 524 | * equals NaN and `-0` does not equal `+0`. 525 | * 526 | * @param {Mixed} a 527 | * @param {Mixed} b 528 | * @return {Boolean} equal match 529 | */ 530 | 531 | function sameValue(a, b) { 532 | if (a === b) return a !== 0 || 1 / a === 1 / b; 533 | return a !== a && b !== b; 534 | } 535 | 536 | /*! 537 | * Compare the types of two given objects and 538 | * return if they are equal. Note that an Array 539 | * has a type of `array` (not `object`) and arguments 540 | * have a type of `arguments` (not `array`/`object`). 541 | * 542 | * @param {Mixed} a 543 | * @param {Mixed} b 544 | * @return {Boolean} result 545 | */ 546 | 547 | function typeEqual(a, b) { 548 | return type(a) === type(b); 549 | } 550 | 551 | /*! 552 | * Compare two Date objects by asserting that 553 | * the time values are equal using `saveValue`. 554 | * 555 | * @param {Date} a 556 | * @param {Date} b 557 | * @return {Boolean} result 558 | */ 559 | 560 | function dateEqual(a, b) { 561 | if ('date' !== type(b)) return false; 562 | return sameValue(a.getTime(), b.getTime()); 563 | } 564 | 565 | /*! 566 | * Compare two regular expressions by converting them 567 | * to string and checking for `sameValue`. 568 | * 569 | * @param {RegExp} a 570 | * @param {RegExp} b 571 | * @return {Boolean} result 572 | */ 573 | 574 | function regexpEqual(a, b) { 575 | if ('regexp' !== type(b)) return false; 576 | return sameValue(a.toString(), b.toString()); 577 | } 578 | 579 | /*! 580 | * Assert deep equality of two `arguments` objects. 581 | * Unfortunately, these must be sliced to arrays 582 | * prior to test to ensure no bad behavior. 583 | * 584 | * @param {Arguments} a 585 | * @param {Arguments} b 586 | * @param {Array} memoize (optional) 587 | * @return {Boolean} result 588 | */ 589 | 590 | function argumentsEqual(a, b, m) { 591 | if ('arguments' !== type(b)) return false; 592 | a = [].slice.call(a); 593 | b = [].slice.call(b); 594 | return deepEqual(a, b, m); 595 | } 596 | 597 | /*! 598 | * Get enumerable properties of a given object. 599 | * 600 | * @param {Object} a 601 | * @return {Array} property names 602 | */ 603 | 604 | function enumerable(a) { 605 | var res = []; 606 | for (var key in a) res.push(key); 607 | return res; 608 | } 609 | 610 | /*! 611 | * Simple equality for flat iterable objects 612 | * such as Arrays or Node.js buffers. 613 | * 614 | * @param {Iterable} a 615 | * @param {Iterable} b 616 | * @return {Boolean} result 617 | */ 618 | 619 | function iterableEqual(a, b) { 620 | if (a.length !== b.length) return false; 621 | 622 | var i = 0; 623 | var match = true; 624 | 625 | for (; i < a.length; i++) { 626 | if (a[i] !== b[i]) { 627 | match = false; 628 | break; 629 | } 630 | } 631 | 632 | return match; 633 | } 634 | 635 | /*! 636 | * Extension to `iterableEqual` specifically 637 | * for Node.js Buffers. 638 | * 639 | * @param {Buffer} a 640 | * @param {Mixed} b 641 | * @return {Boolean} result 642 | */ 643 | 644 | function bufferEqual(a, b) { 645 | if (!Buffer.isBuffer(b)) return false; 646 | return iterableEqual(a, b); 647 | } 648 | 649 | /*! 650 | * Block for `objectEqual` ensuring non-existing 651 | * values don't get in. 652 | * 653 | * @param {Mixed} object 654 | * @return {Boolean} result 655 | */ 656 | 657 | function isValue(a) { 658 | return a !== null && a !== undefined; 659 | } 660 | 661 | /*! 662 | * Recursively check the equality of two objects. 663 | * Once basic sameness has been established it will 664 | * defer to `deepEqual` for each enumerable key 665 | * in the object. 666 | * 667 | * @param {Mixed} a 668 | * @param {Mixed} b 669 | * @return {Boolean} result 670 | */ 671 | 672 | function objectEqual(a, b, m) { 673 | if (!isValue(a) || !isValue(b)) { 674 | return false; 675 | } 676 | 677 | if (a.prototype !== b.prototype) { 678 | return false; 679 | } 680 | 681 | var i; 682 | if (m) { 683 | for (i = 0; i < m.length; i++) { 684 | if ((m[i][0] === a && m[i][1] === b) 685 | || (m[i][0] === b && m[i][1] === a)) { 686 | return true; 687 | } 688 | } 689 | } else { 690 | m = []; 691 | } 692 | 693 | try { 694 | var ka = enumerable(a); 695 | var kb = enumerable(b); 696 | } catch (ex) { 697 | return false; 698 | } 699 | 700 | ka.sort(); 701 | kb.sort(); 702 | 703 | if (!iterableEqual(ka, kb)) { 704 | return false; 705 | } 706 | 707 | m.push([ a, b ]); 708 | 709 | var key; 710 | for (i = ka.length - 1; i >= 0; i--) { 711 | key = ka[i]; 712 | if (!deepEqual(a[key], b[key], m)) { 713 | return false; 714 | } 715 | } 716 | 717 | return true; 718 | } 719 | 720 | }); 721 | require.register("chai/index.js", function(exports, require, module){ 722 | module.exports = require('./lib/chai'); 723 | 724 | }); 725 | require.register("chai/lib/chai.js", function(exports, require, module){ 726 | /*! 727 | * chai 728 | * Copyright(c) 2011-2013 Jake Luer 729 | * MIT Licensed 730 | */ 731 | 732 | var used = [] 733 | , exports = module.exports = {}; 734 | 735 | /*! 736 | * Chai version 737 | */ 738 | 739 | exports.version = '1.8.0'; 740 | 741 | /*! 742 | * Assertion Error 743 | */ 744 | 745 | exports.AssertionError = require('assertion-error'); 746 | 747 | /*! 748 | * Utils for plugins (not exported) 749 | */ 750 | 751 | var util = require('./chai/utils'); 752 | 753 | /** 754 | * # .use(function) 755 | * 756 | * Provides a way to extend the internals of Chai 757 | * 758 | * @param {Function} 759 | * @returns {this} for chaining 760 | * @api public 761 | */ 762 | 763 | exports.use = function (fn) { 764 | if (!~used.indexOf(fn)) { 765 | fn(this, util); 766 | used.push(fn); 767 | } 768 | 769 | return this; 770 | }; 771 | 772 | /*! 773 | * Primary `Assertion` prototype 774 | */ 775 | 776 | var assertion = require('./chai/assertion'); 777 | exports.use(assertion); 778 | 779 | /*! 780 | * Core Assertions 781 | */ 782 | 783 | var core = require('./chai/core/assertions'); 784 | exports.use(core); 785 | 786 | /*! 787 | * Expect interface 788 | */ 789 | 790 | var expect = require('./chai/interface/expect'); 791 | exports.use(expect); 792 | 793 | /*! 794 | * Should interface 795 | */ 796 | 797 | var should = require('./chai/interface/should'); 798 | exports.use(should); 799 | 800 | /*! 801 | * Assert interface 802 | */ 803 | 804 | var assert = require('./chai/interface/assert'); 805 | exports.use(assert); 806 | 807 | }); 808 | require.register("chai/lib/chai/assertion.js", function(exports, require, module){ 809 | /*! 810 | * chai 811 | * http://chaijs.com 812 | * Copyright(c) 2011-2013 Jake Luer 813 | * MIT Licensed 814 | */ 815 | 816 | module.exports = function (_chai, util) { 817 | /*! 818 | * Module dependencies. 819 | */ 820 | 821 | var AssertionError = _chai.AssertionError 822 | , flag = util.flag; 823 | 824 | /*! 825 | * Module export. 826 | */ 827 | 828 | _chai.Assertion = Assertion; 829 | 830 | /*! 831 | * Assertion Constructor 832 | * 833 | * Creates object for chaining. 834 | * 835 | * @api private 836 | */ 837 | 838 | function Assertion (obj, msg, stack) { 839 | flag(this, 'ssfi', stack || arguments.callee); 840 | flag(this, 'object', obj); 841 | flag(this, 'message', msg); 842 | } 843 | 844 | /*! 845 | * ### Assertion.includeStack 846 | * 847 | * User configurable property, influences whether stack trace 848 | * is included in Assertion error message. Default of false 849 | * suppresses stack trace in the error message 850 | * 851 | * Assertion.includeStack = true; // enable stack on error 852 | * 853 | * @api public 854 | */ 855 | 856 | Assertion.includeStack = false; 857 | 858 | /*! 859 | * ### Assertion.showDiff 860 | * 861 | * User configurable property, influences whether or not 862 | * the `showDiff` flag should be included in the thrown 863 | * AssertionErrors. `false` will always be `false`; `true` 864 | * will be true when the assertion has requested a diff 865 | * be shown. 866 | * 867 | * @api public 868 | */ 869 | 870 | Assertion.showDiff = true; 871 | 872 | Assertion.addProperty = function (name, fn) { 873 | util.addProperty(this.prototype, name, fn); 874 | }; 875 | 876 | Assertion.addMethod = function (name, fn) { 877 | util.addMethod(this.prototype, name, fn); 878 | }; 879 | 880 | Assertion.addChainableMethod = function (name, fn, chainingBehavior) { 881 | util.addChainableMethod(this.prototype, name, fn, chainingBehavior); 882 | }; 883 | 884 | Assertion.overwriteProperty = function (name, fn) { 885 | util.overwriteProperty(this.prototype, name, fn); 886 | }; 887 | 888 | Assertion.overwriteMethod = function (name, fn) { 889 | util.overwriteMethod(this.prototype, name, fn); 890 | }; 891 | 892 | /*! 893 | * ### .assert(expression, message, negateMessage, expected, actual) 894 | * 895 | * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. 896 | * 897 | * @name assert 898 | * @param {Philosophical} expression to be tested 899 | * @param {String} message to display if fails 900 | * @param {String} negatedMessage to display if negated expression fails 901 | * @param {Mixed} expected value (remember to check for negation) 902 | * @param {Mixed} actual (optional) will default to `this.obj` 903 | * @api private 904 | */ 905 | 906 | Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) { 907 | var ok = util.test(this, arguments); 908 | if (true !== showDiff) showDiff = false; 909 | if (true !== Assertion.showDiff) showDiff = false; 910 | 911 | if (!ok) { 912 | var msg = util.getMessage(this, arguments) 913 | , actual = util.getActual(this, arguments); 914 | throw new AssertionError(msg, { 915 | actual: actual 916 | , expected: expected 917 | , showDiff: showDiff 918 | }, (Assertion.includeStack) ? this.assert : flag(this, 'ssfi')); 919 | } 920 | }; 921 | 922 | /*! 923 | * ### ._obj 924 | * 925 | * Quick reference to stored `actual` value for plugin developers. 926 | * 927 | * @api private 928 | */ 929 | 930 | Object.defineProperty(Assertion.prototype, '_obj', 931 | { get: function () { 932 | return flag(this, 'object'); 933 | } 934 | , set: function (val) { 935 | flag(this, 'object', val); 936 | } 937 | }); 938 | }; 939 | 940 | }); 941 | require.register("chai/lib/chai/core/assertions.js", function(exports, require, module){ 942 | /*! 943 | * chai 944 | * http://chaijs.com 945 | * Copyright(c) 2011-2013 Jake Luer 946 | * MIT Licensed 947 | */ 948 | 949 | module.exports = function (chai, _) { 950 | var Assertion = chai.Assertion 951 | , toString = Object.prototype.toString 952 | , flag = _.flag; 953 | 954 | /** 955 | * ### Language Chains 956 | * 957 | * The following are provide as chainable getters to 958 | * improve the readability of your assertions. They 959 | * do not provide an testing capability unless they 960 | * have been overwritten by a plugin. 961 | * 962 | * **Chains** 963 | * 964 | * - to 965 | * - be 966 | * - been 967 | * - is 968 | * - that 969 | * - and 970 | * - have 971 | * - with 972 | * - at 973 | * - of 974 | * - same 975 | * 976 | * @name language chains 977 | * @api public 978 | */ 979 | 980 | [ 'to', 'be', 'been' 981 | , 'is', 'and', 'have' 982 | , 'with', 'that', 'at' 983 | , 'of', 'same' ].forEach(function (chain) { 984 | Assertion.addProperty(chain, function () { 985 | return this; 986 | }); 987 | }); 988 | 989 | /** 990 | * ### .not 991 | * 992 | * Negates any of assertions following in the chain. 993 | * 994 | * expect(foo).to.not.equal('bar'); 995 | * expect(goodFn).to.not.throw(Error); 996 | * expect({ foo: 'baz' }).to.have.property('foo') 997 | * .and.not.equal('bar'); 998 | * 999 | * @name not 1000 | * @api public 1001 | */ 1002 | 1003 | Assertion.addProperty('not', function () { 1004 | flag(this, 'negate', true); 1005 | }); 1006 | 1007 | /** 1008 | * ### .deep 1009 | * 1010 | * Sets the `deep` flag, later used by the `equal` and 1011 | * `property` assertions. 1012 | * 1013 | * expect(foo).to.deep.equal({ bar: 'baz' }); 1014 | * expect({ foo: { bar: { baz: 'quux' } } }) 1015 | * .to.have.deep.property('foo.bar.baz', 'quux'); 1016 | * 1017 | * @name deep 1018 | * @api public 1019 | */ 1020 | 1021 | Assertion.addProperty('deep', function () { 1022 | flag(this, 'deep', true); 1023 | }); 1024 | 1025 | /** 1026 | * ### .a(type) 1027 | * 1028 | * The `a` and `an` assertions are aliases that can be 1029 | * used either as language chains or to assert a value's 1030 | * type. 1031 | * 1032 | * // typeof 1033 | * expect('test').to.be.a('string'); 1034 | * expect({ foo: 'bar' }).to.be.an('object'); 1035 | * expect(null).to.be.a('null'); 1036 | * expect(undefined).to.be.an('undefined'); 1037 | * 1038 | * // language chain 1039 | * expect(foo).to.be.an.instanceof(Foo); 1040 | * 1041 | * @name a 1042 | * @alias an 1043 | * @param {String} type 1044 | * @param {String} message _optional_ 1045 | * @api public 1046 | */ 1047 | 1048 | function an (type, msg) { 1049 | if (msg) flag(this, 'message', msg); 1050 | type = type.toLowerCase(); 1051 | var obj = flag(this, 'object') 1052 | , article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a '; 1053 | 1054 | this.assert( 1055 | type === _.type(obj) 1056 | , 'expected #{this} to be ' + article + type 1057 | , 'expected #{this} not to be ' + article + type 1058 | ); 1059 | } 1060 | 1061 | Assertion.addChainableMethod('an', an); 1062 | Assertion.addChainableMethod('a', an); 1063 | 1064 | /** 1065 | * ### .include(value) 1066 | * 1067 | * The `include` and `contain` assertions can be used as either property 1068 | * based language chains or as methods to assert the inclusion of an object 1069 | * in an array or a substring in a string. When used as language chains, 1070 | * they toggle the `contain` flag for the `keys` assertion. 1071 | * 1072 | * expect([1,2,3]).to.include(2); 1073 | * expect('foobar').to.contain('foo'); 1074 | * expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); 1075 | * 1076 | * @name include 1077 | * @alias contain 1078 | * @param {Object|String|Number} obj 1079 | * @param {String} message _optional_ 1080 | * @api public 1081 | */ 1082 | 1083 | function includeChainingBehavior () { 1084 | flag(this, 'contains', true); 1085 | } 1086 | 1087 | function include (val, msg) { 1088 | if (msg) flag(this, 'message', msg); 1089 | var obj = flag(this, 'object') 1090 | this.assert( 1091 | ~obj.indexOf(val) 1092 | , 'expected #{this} to include ' + _.inspect(val) 1093 | , 'expected #{this} to not include ' + _.inspect(val)); 1094 | } 1095 | 1096 | Assertion.addChainableMethod('include', include, includeChainingBehavior); 1097 | Assertion.addChainableMethod('contain', include, includeChainingBehavior); 1098 | 1099 | /** 1100 | * ### .ok 1101 | * 1102 | * Asserts that the target is truthy. 1103 | * 1104 | * expect('everthing').to.be.ok; 1105 | * expect(1).to.be.ok; 1106 | * expect(false).to.not.be.ok; 1107 | * expect(undefined).to.not.be.ok; 1108 | * expect(null).to.not.be.ok; 1109 | * 1110 | * @name ok 1111 | * @api public 1112 | */ 1113 | 1114 | Assertion.addProperty('ok', function () { 1115 | this.assert( 1116 | flag(this, 'object') 1117 | , 'expected #{this} to be truthy' 1118 | , 'expected #{this} to be falsy'); 1119 | }); 1120 | 1121 | /** 1122 | * ### .true 1123 | * 1124 | * Asserts that the target is `true`. 1125 | * 1126 | * expect(true).to.be.true; 1127 | * expect(1).to.not.be.true; 1128 | * 1129 | * @name true 1130 | * @api public 1131 | */ 1132 | 1133 | Assertion.addProperty('true', function () { 1134 | this.assert( 1135 | true === flag(this, 'object') 1136 | , 'expected #{this} to be true' 1137 | , 'expected #{this} to be false' 1138 | , this.negate ? false : true 1139 | ); 1140 | }); 1141 | 1142 | /** 1143 | * ### .false 1144 | * 1145 | * Asserts that the target is `false`. 1146 | * 1147 | * expect(false).to.be.false; 1148 | * expect(0).to.not.be.false; 1149 | * 1150 | * @name false 1151 | * @api public 1152 | */ 1153 | 1154 | Assertion.addProperty('false', function () { 1155 | this.assert( 1156 | false === flag(this, 'object') 1157 | , 'expected #{this} to be false' 1158 | , 'expected #{this} to be true' 1159 | , this.negate ? true : false 1160 | ); 1161 | }); 1162 | 1163 | /** 1164 | * ### .null 1165 | * 1166 | * Asserts that the target is `null`. 1167 | * 1168 | * expect(null).to.be.null; 1169 | * expect(undefined).not.to.be.null; 1170 | * 1171 | * @name null 1172 | * @api public 1173 | */ 1174 | 1175 | Assertion.addProperty('null', function () { 1176 | this.assert( 1177 | null === flag(this, 'object') 1178 | , 'expected #{this} to be null' 1179 | , 'expected #{this} not to be null' 1180 | ); 1181 | }); 1182 | 1183 | /** 1184 | * ### .undefined 1185 | * 1186 | * Asserts that the target is `undefined`. 1187 | * 1188 | * expect(undefined).to.be.undefined; 1189 | * expect(null).to.not.be.undefined; 1190 | * 1191 | * @name undefined 1192 | * @api public 1193 | */ 1194 | 1195 | Assertion.addProperty('undefined', function () { 1196 | this.assert( 1197 | undefined === flag(this, 'object') 1198 | , 'expected #{this} to be undefined' 1199 | , 'expected #{this} not to be undefined' 1200 | ); 1201 | }); 1202 | 1203 | /** 1204 | * ### .exist 1205 | * 1206 | * Asserts that the target is neither `null` nor `undefined`. 1207 | * 1208 | * var foo = 'hi' 1209 | * , bar = null 1210 | * , baz; 1211 | * 1212 | * expect(foo).to.exist; 1213 | * expect(bar).to.not.exist; 1214 | * expect(baz).to.not.exist; 1215 | * 1216 | * @name exist 1217 | * @api public 1218 | */ 1219 | 1220 | Assertion.addProperty('exist', function () { 1221 | this.assert( 1222 | null != flag(this, 'object') 1223 | , 'expected #{this} to exist' 1224 | , 'expected #{this} to not exist' 1225 | ); 1226 | }); 1227 | 1228 | 1229 | /** 1230 | * ### .empty 1231 | * 1232 | * Asserts that the target's length is `0`. For arrays, it checks 1233 | * the `length` property. For objects, it gets the count of 1234 | * enumerable keys. 1235 | * 1236 | * expect([]).to.be.empty; 1237 | * expect('').to.be.empty; 1238 | * expect({}).to.be.empty; 1239 | * 1240 | * @name empty 1241 | * @api public 1242 | */ 1243 | 1244 | Assertion.addProperty('empty', function () { 1245 | var obj = flag(this, 'object') 1246 | , expected = obj; 1247 | 1248 | if (Array.isArray(obj) || 'string' === typeof object) { 1249 | expected = obj.length; 1250 | } else if (typeof obj === 'object') { 1251 | expected = Object.keys(obj).length; 1252 | } 1253 | 1254 | this.assert( 1255 | !expected 1256 | , 'expected #{this} to be empty' 1257 | , 'expected #{this} not to be empty' 1258 | ); 1259 | }); 1260 | 1261 | /** 1262 | * ### .arguments 1263 | * 1264 | * Asserts that the target is an arguments object. 1265 | * 1266 | * function test () { 1267 | * expect(arguments).to.be.arguments; 1268 | * } 1269 | * 1270 | * @name arguments 1271 | * @alias Arguments 1272 | * @api public 1273 | */ 1274 | 1275 | function checkArguments () { 1276 | var obj = flag(this, 'object') 1277 | , type = Object.prototype.toString.call(obj); 1278 | this.assert( 1279 | '[object Arguments]' === type 1280 | , 'expected #{this} to be arguments but got ' + type 1281 | , 'expected #{this} to not be arguments' 1282 | ); 1283 | } 1284 | 1285 | Assertion.addProperty('arguments', checkArguments); 1286 | Assertion.addProperty('Arguments', checkArguments); 1287 | 1288 | /** 1289 | * ### .equal(value) 1290 | * 1291 | * Asserts that the target is strictly equal (`===`) to `value`. 1292 | * Alternately, if the `deep` flag is set, asserts that 1293 | * the target is deeply equal to `value`. 1294 | * 1295 | * expect('hello').to.equal('hello'); 1296 | * expect(42).to.equal(42); 1297 | * expect(1).to.not.equal(true); 1298 | * expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' }); 1299 | * expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); 1300 | * 1301 | * @name equal 1302 | * @alias equals 1303 | * @alias eq 1304 | * @alias deep.equal 1305 | * @param {Mixed} value 1306 | * @param {String} message _optional_ 1307 | * @api public 1308 | */ 1309 | 1310 | function assertEqual (val, msg) { 1311 | if (msg) flag(this, 'message', msg); 1312 | var obj = flag(this, 'object'); 1313 | if (flag(this, 'deep')) { 1314 | return this.eql(val); 1315 | } else { 1316 | this.assert( 1317 | val === obj 1318 | , 'expected #{this} to equal #{exp}' 1319 | , 'expected #{this} to not equal #{exp}' 1320 | , val 1321 | , this._obj 1322 | , true 1323 | ); 1324 | } 1325 | } 1326 | 1327 | Assertion.addMethod('equal', assertEqual); 1328 | Assertion.addMethod('equals', assertEqual); 1329 | Assertion.addMethod('eq', assertEqual); 1330 | 1331 | /** 1332 | * ### .eql(value) 1333 | * 1334 | * Asserts that the target is deeply equal to `value`. 1335 | * 1336 | * expect({ foo: 'bar' }).to.eql({ foo: 'bar' }); 1337 | * expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]); 1338 | * 1339 | * @name eql 1340 | * @alias eqls 1341 | * @param {Mixed} value 1342 | * @param {String} message _optional_ 1343 | * @api public 1344 | */ 1345 | 1346 | function assertEql(obj, msg) { 1347 | if (msg) flag(this, 'message', msg); 1348 | this.assert( 1349 | _.eql(obj, flag(this, 'object')) 1350 | , 'expected #{this} to deeply equal #{exp}' 1351 | , 'expected #{this} to not deeply equal #{exp}' 1352 | , obj 1353 | , this._obj 1354 | , true 1355 | ); 1356 | } 1357 | 1358 | Assertion.addMethod('eql', assertEql); 1359 | Assertion.addMethod('eqls', assertEql); 1360 | 1361 | /** 1362 | * ### .above(value) 1363 | * 1364 | * Asserts that the target is greater than `value`. 1365 | * 1366 | * expect(10).to.be.above(5); 1367 | * 1368 | * Can also be used in conjunction with `length` to 1369 | * assert a minimum length. The benefit being a 1370 | * more informative error message than if the length 1371 | * was supplied directly. 1372 | * 1373 | * expect('foo').to.have.length.above(2); 1374 | * expect([ 1, 2, 3 ]).to.have.length.above(2); 1375 | * 1376 | * @name above 1377 | * @alias gt 1378 | * @alias greaterThan 1379 | * @param {Number} value 1380 | * @param {String} message _optional_ 1381 | * @api public 1382 | */ 1383 | 1384 | function assertAbove (n, msg) { 1385 | if (msg) flag(this, 'message', msg); 1386 | var obj = flag(this, 'object'); 1387 | if (flag(this, 'doLength')) { 1388 | new Assertion(obj, msg).to.have.property('length'); 1389 | var len = obj.length; 1390 | this.assert( 1391 | len > n 1392 | , 'expected #{this} to have a length above #{exp} but got #{act}' 1393 | , 'expected #{this} to not have a length above #{exp}' 1394 | , n 1395 | , len 1396 | ); 1397 | } else { 1398 | this.assert( 1399 | obj > n 1400 | , 'expected #{this} to be above ' + n 1401 | , 'expected #{this} to be at most ' + n 1402 | ); 1403 | } 1404 | } 1405 | 1406 | Assertion.addMethod('above', assertAbove); 1407 | Assertion.addMethod('gt', assertAbove); 1408 | Assertion.addMethod('greaterThan', assertAbove); 1409 | 1410 | /** 1411 | * ### .least(value) 1412 | * 1413 | * Asserts that the target is greater than or equal to `value`. 1414 | * 1415 | * expect(10).to.be.at.least(10); 1416 | * 1417 | * Can also be used in conjunction with `length` to 1418 | * assert a minimum length. The benefit being a 1419 | * more informative error message than if the length 1420 | * was supplied directly. 1421 | * 1422 | * expect('foo').to.have.length.of.at.least(2); 1423 | * expect([ 1, 2, 3 ]).to.have.length.of.at.least(3); 1424 | * 1425 | * @name least 1426 | * @alias gte 1427 | * @param {Number} value 1428 | * @param {String} message _optional_ 1429 | * @api public 1430 | */ 1431 | 1432 | function assertLeast (n, msg) { 1433 | if (msg) flag(this, 'message', msg); 1434 | var obj = flag(this, 'object'); 1435 | if (flag(this, 'doLength')) { 1436 | new Assertion(obj, msg).to.have.property('length'); 1437 | var len = obj.length; 1438 | this.assert( 1439 | len >= n 1440 | , 'expected #{this} to have a length at least #{exp} but got #{act}' 1441 | , 'expected #{this} to have a length below #{exp}' 1442 | , n 1443 | , len 1444 | ); 1445 | } else { 1446 | this.assert( 1447 | obj >= n 1448 | , 'expected #{this} to be at least ' + n 1449 | , 'expected #{this} to be below ' + n 1450 | ); 1451 | } 1452 | } 1453 | 1454 | Assertion.addMethod('least', assertLeast); 1455 | Assertion.addMethod('gte', assertLeast); 1456 | 1457 | /** 1458 | * ### .below(value) 1459 | * 1460 | * Asserts that the target is less than `value`. 1461 | * 1462 | * expect(5).to.be.below(10); 1463 | * 1464 | * Can also be used in conjunction with `length` to 1465 | * assert a maximum length. The benefit being a 1466 | * more informative error message than if the length 1467 | * was supplied directly. 1468 | * 1469 | * expect('foo').to.have.length.below(4); 1470 | * expect([ 1, 2, 3 ]).to.have.length.below(4); 1471 | * 1472 | * @name below 1473 | * @alias lt 1474 | * @alias lessThan 1475 | * @param {Number} value 1476 | * @param {String} message _optional_ 1477 | * @api public 1478 | */ 1479 | 1480 | function assertBelow (n, msg) { 1481 | if (msg) flag(this, 'message', msg); 1482 | var obj = flag(this, 'object'); 1483 | if (flag(this, 'doLength')) { 1484 | new Assertion(obj, msg).to.have.property('length'); 1485 | var len = obj.length; 1486 | this.assert( 1487 | len < n 1488 | , 'expected #{this} to have a length below #{exp} but got #{act}' 1489 | , 'expected #{this} to not have a length below #{exp}' 1490 | , n 1491 | , len 1492 | ); 1493 | } else { 1494 | this.assert( 1495 | obj < n 1496 | , 'expected #{this} to be below ' + n 1497 | , 'expected #{this} to be at least ' + n 1498 | ); 1499 | } 1500 | } 1501 | 1502 | Assertion.addMethod('below', assertBelow); 1503 | Assertion.addMethod('lt', assertBelow); 1504 | Assertion.addMethod('lessThan', assertBelow); 1505 | 1506 | /** 1507 | * ### .most(value) 1508 | * 1509 | * Asserts that the target is less than or equal to `value`. 1510 | * 1511 | * expect(5).to.be.at.most(5); 1512 | * 1513 | * Can also be used in conjunction with `length` to 1514 | * assert a maximum length. The benefit being a 1515 | * more informative error message than if the length 1516 | * was supplied directly. 1517 | * 1518 | * expect('foo').to.have.length.of.at.most(4); 1519 | * expect([ 1, 2, 3 ]).to.have.length.of.at.most(3); 1520 | * 1521 | * @name most 1522 | * @alias lte 1523 | * @param {Number} value 1524 | * @param {String} message _optional_ 1525 | * @api public 1526 | */ 1527 | 1528 | function assertMost (n, msg) { 1529 | if (msg) flag(this, 'message', msg); 1530 | var obj = flag(this, 'object'); 1531 | if (flag(this, 'doLength')) { 1532 | new Assertion(obj, msg).to.have.property('length'); 1533 | var len = obj.length; 1534 | this.assert( 1535 | len <= n 1536 | , 'expected #{this} to have a length at most #{exp} but got #{act}' 1537 | , 'expected #{this} to have a length above #{exp}' 1538 | , n 1539 | , len 1540 | ); 1541 | } else { 1542 | this.assert( 1543 | obj <= n 1544 | , 'expected #{this} to be at most ' + n 1545 | , 'expected #{this} to be above ' + n 1546 | ); 1547 | } 1548 | } 1549 | 1550 | Assertion.addMethod('most', assertMost); 1551 | Assertion.addMethod('lte', assertMost); 1552 | 1553 | /** 1554 | * ### .within(start, finish) 1555 | * 1556 | * Asserts that the target is within a range. 1557 | * 1558 | * expect(7).to.be.within(5,10); 1559 | * 1560 | * Can also be used in conjunction with `length` to 1561 | * assert a length range. The benefit being a 1562 | * more informative error message than if the length 1563 | * was supplied directly. 1564 | * 1565 | * expect('foo').to.have.length.within(2,4); 1566 | * expect([ 1, 2, 3 ]).to.have.length.within(2,4); 1567 | * 1568 | * @name within 1569 | * @param {Number} start lowerbound inclusive 1570 | * @param {Number} finish upperbound inclusive 1571 | * @param {String} message _optional_ 1572 | * @api public 1573 | */ 1574 | 1575 | Assertion.addMethod('within', function (start, finish, msg) { 1576 | if (msg) flag(this, 'message', msg); 1577 | var obj = flag(this, 'object') 1578 | , range = start + '..' + finish; 1579 | if (flag(this, 'doLength')) { 1580 | new Assertion(obj, msg).to.have.property('length'); 1581 | var len = obj.length; 1582 | this.assert( 1583 | len >= start && len <= finish 1584 | , 'expected #{this} to have a length within ' + range 1585 | , 'expected #{this} to not have a length within ' + range 1586 | ); 1587 | } else { 1588 | this.assert( 1589 | obj >= start && obj <= finish 1590 | , 'expected #{this} to be within ' + range 1591 | , 'expected #{this} to not be within ' + range 1592 | ); 1593 | } 1594 | }); 1595 | 1596 | /** 1597 | * ### .instanceof(constructor) 1598 | * 1599 | * Asserts that the target is an instance of `constructor`. 1600 | * 1601 | * var Tea = function (name) { this.name = name; } 1602 | * , Chai = new Tea('chai'); 1603 | * 1604 | * expect(Chai).to.be.an.instanceof(Tea); 1605 | * expect([ 1, 2, 3 ]).to.be.instanceof(Array); 1606 | * 1607 | * @name instanceof 1608 | * @param {Constructor} constructor 1609 | * @param {String} message _optional_ 1610 | * @alias instanceOf 1611 | * @api public 1612 | */ 1613 | 1614 | function assertInstanceOf (constructor, msg) { 1615 | if (msg) flag(this, 'message', msg); 1616 | var name = _.getName(constructor); 1617 | this.assert( 1618 | flag(this, 'object') instanceof constructor 1619 | , 'expected #{this} to be an instance of ' + name 1620 | , 'expected #{this} to not be an instance of ' + name 1621 | ); 1622 | }; 1623 | 1624 | Assertion.addMethod('instanceof', assertInstanceOf); 1625 | Assertion.addMethod('instanceOf', assertInstanceOf); 1626 | 1627 | /** 1628 | * ### .property(name, [value]) 1629 | * 1630 | * Asserts that the target has a property `name`, optionally asserting that 1631 | * the value of that property is strictly equal to `value`. 1632 | * If the `deep` flag is set, you can use dot- and bracket-notation for deep 1633 | * references into objects and arrays. 1634 | * 1635 | * // simple referencing 1636 | * var obj = { foo: 'bar' }; 1637 | * expect(obj).to.have.property('foo'); 1638 | * expect(obj).to.have.property('foo', 'bar'); 1639 | * 1640 | * // deep referencing 1641 | * var deepObj = { 1642 | * green: { tea: 'matcha' } 1643 | * , teas: [ 'chai', 'matcha', { tea: 'konacha' } ] 1644 | * }; 1645 | 1646 | * expect(deepObj).to.have.deep.property('green.tea', 'matcha'); 1647 | * expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); 1648 | * expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); 1649 | * 1650 | * You can also use an array as the starting point of a `deep.property` 1651 | * assertion, or traverse nested arrays. 1652 | * 1653 | * var arr = [ 1654 | * [ 'chai', 'matcha', 'konacha' ] 1655 | * , [ { tea: 'chai' } 1656 | * , { tea: 'matcha' } 1657 | * , { tea: 'konacha' } ] 1658 | * ]; 1659 | * 1660 | * expect(arr).to.have.deep.property('[0][1]', 'matcha'); 1661 | * expect(arr).to.have.deep.property('[1][2].tea', 'konacha'); 1662 | * 1663 | * Furthermore, `property` changes the subject of the assertion 1664 | * to be the value of that property from the original object. This 1665 | * permits for further chainable assertions on that property. 1666 | * 1667 | * expect(obj).to.have.property('foo') 1668 | * .that.is.a('string'); 1669 | * expect(deepObj).to.have.property('green') 1670 | * .that.is.an('object') 1671 | * .that.deep.equals({ tea: 'matcha' }); 1672 | * expect(deepObj).to.have.property('teas') 1673 | * .that.is.an('array') 1674 | * .with.deep.property('[2]') 1675 | * .that.deep.equals({ tea: 'konacha' }); 1676 | * 1677 | * @name property 1678 | * @alias deep.property 1679 | * @param {String} name 1680 | * @param {Mixed} value (optional) 1681 | * @param {String} message _optional_ 1682 | * @returns value of property for chaining 1683 | * @api public 1684 | */ 1685 | 1686 | Assertion.addMethod('property', function (name, val, msg) { 1687 | if (msg) flag(this, 'message', msg); 1688 | 1689 | var descriptor = flag(this, 'deep') ? 'deep property ' : 'property ' 1690 | , negate = flag(this, 'negate') 1691 | , obj = flag(this, 'object') 1692 | , value = flag(this, 'deep') 1693 | ? _.getPathValue(name, obj) 1694 | : obj[name]; 1695 | 1696 | if (negate && undefined !== val) { 1697 | if (undefined === value) { 1698 | msg = (msg != null) ? msg + ': ' : ''; 1699 | throw new Error(msg + _.inspect(obj) + ' has no ' + descriptor + _.inspect(name)); 1700 | } 1701 | } else { 1702 | this.assert( 1703 | undefined !== value 1704 | , 'expected #{this} to have a ' + descriptor + _.inspect(name) 1705 | , 'expected #{this} to not have ' + descriptor + _.inspect(name)); 1706 | } 1707 | 1708 | if (undefined !== val) { 1709 | this.assert( 1710 | val === value 1711 | , 'expected #{this} to have a ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' 1712 | , 'expected #{this} to not have a ' + descriptor + _.inspect(name) + ' of #{act}' 1713 | , val 1714 | , value 1715 | ); 1716 | } 1717 | 1718 | flag(this, 'object', value); 1719 | }); 1720 | 1721 | 1722 | /** 1723 | * ### .ownProperty(name) 1724 | * 1725 | * Asserts that the target has an own property `name`. 1726 | * 1727 | * expect('test').to.have.ownProperty('length'); 1728 | * 1729 | * @name ownProperty 1730 | * @alias haveOwnProperty 1731 | * @param {String} name 1732 | * @param {String} message _optional_ 1733 | * @api public 1734 | */ 1735 | 1736 | function assertOwnProperty (name, msg) { 1737 | if (msg) flag(this, 'message', msg); 1738 | var obj = flag(this, 'object'); 1739 | this.assert( 1740 | obj.hasOwnProperty(name) 1741 | , 'expected #{this} to have own property ' + _.inspect(name) 1742 | , 'expected #{this} to not have own property ' + _.inspect(name) 1743 | ); 1744 | } 1745 | 1746 | Assertion.addMethod('ownProperty', assertOwnProperty); 1747 | Assertion.addMethod('haveOwnProperty', assertOwnProperty); 1748 | 1749 | /** 1750 | * ### .length(value) 1751 | * 1752 | * Asserts that the target's `length` property has 1753 | * the expected value. 1754 | * 1755 | * expect([ 1, 2, 3]).to.have.length(3); 1756 | * expect('foobar').to.have.length(6); 1757 | * 1758 | * Can also be used as a chain precursor to a value 1759 | * comparison for the length property. 1760 | * 1761 | * expect('foo').to.have.length.above(2); 1762 | * expect([ 1, 2, 3 ]).to.have.length.above(2); 1763 | * expect('foo').to.have.length.below(4); 1764 | * expect([ 1, 2, 3 ]).to.have.length.below(4); 1765 | * expect('foo').to.have.length.within(2,4); 1766 | * expect([ 1, 2, 3 ]).to.have.length.within(2,4); 1767 | * 1768 | * @name length 1769 | * @alias lengthOf 1770 | * @param {Number} length 1771 | * @param {String} message _optional_ 1772 | * @api public 1773 | */ 1774 | 1775 | function assertLengthChain () { 1776 | flag(this, 'doLength', true); 1777 | } 1778 | 1779 | function assertLength (n, msg) { 1780 | if (msg) flag(this, 'message', msg); 1781 | var obj = flag(this, 'object'); 1782 | new Assertion(obj, msg).to.have.property('length'); 1783 | var len = obj.length; 1784 | 1785 | this.assert( 1786 | len == n 1787 | , 'expected #{this} to have a length of #{exp} but got #{act}' 1788 | , 'expected #{this} to not have a length of #{act}' 1789 | , n 1790 | , len 1791 | ); 1792 | } 1793 | 1794 | Assertion.addChainableMethod('length', assertLength, assertLengthChain); 1795 | Assertion.addMethod('lengthOf', assertLength, assertLengthChain); 1796 | 1797 | /** 1798 | * ### .match(regexp) 1799 | * 1800 | * Asserts that the target matches a regular expression. 1801 | * 1802 | * expect('foobar').to.match(/^foo/); 1803 | * 1804 | * @name match 1805 | * @param {RegExp} RegularExpression 1806 | * @param {String} message _optional_ 1807 | * @api public 1808 | */ 1809 | 1810 | Assertion.addMethod('match', function (re, msg) { 1811 | if (msg) flag(this, 'message', msg); 1812 | var obj = flag(this, 'object'); 1813 | this.assert( 1814 | re.exec(obj) 1815 | , 'expected #{this} to match ' + re 1816 | , 'expected #{this} not to match ' + re 1817 | ); 1818 | }); 1819 | 1820 | /** 1821 | * ### .string(string) 1822 | * 1823 | * Asserts that the string target contains another string. 1824 | * 1825 | * expect('foobar').to.have.string('bar'); 1826 | * 1827 | * @name string 1828 | * @param {String} string 1829 | * @param {String} message _optional_ 1830 | * @api public 1831 | */ 1832 | 1833 | Assertion.addMethod('string', function (str, msg) { 1834 | if (msg) flag(this, 'message', msg); 1835 | var obj = flag(this, 'object'); 1836 | new Assertion(obj, msg).is.a('string'); 1837 | 1838 | this.assert( 1839 | ~obj.indexOf(str) 1840 | , 'expected #{this} to contain ' + _.inspect(str) 1841 | , 'expected #{this} to not contain ' + _.inspect(str) 1842 | ); 1843 | }); 1844 | 1845 | 1846 | /** 1847 | * ### .keys(key1, [key2], [...]) 1848 | * 1849 | * Asserts that the target has exactly the given keys, or 1850 | * asserts the inclusion of some keys when using the 1851 | * `include` or `contain` modifiers. 1852 | * 1853 | * expect({ foo: 1, bar: 2 }).to.have.keys(['foo', 'bar']); 1854 | * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('foo', 'bar'); 1855 | * 1856 | * @name keys 1857 | * @alias key 1858 | * @param {String...|Array} keys 1859 | * @api public 1860 | */ 1861 | 1862 | function assertKeys (keys) { 1863 | var obj = flag(this, 'object') 1864 | , str 1865 | , ok = true; 1866 | 1867 | keys = keys instanceof Array 1868 | ? keys 1869 | : Array.prototype.slice.call(arguments); 1870 | 1871 | if (!keys.length) throw new Error('keys required'); 1872 | 1873 | var actual = Object.keys(obj) 1874 | , len = keys.length; 1875 | 1876 | // Inclusion 1877 | ok = keys.every(function(key){ 1878 | return ~actual.indexOf(key); 1879 | }); 1880 | 1881 | // Strict 1882 | if (!flag(this, 'negate') && !flag(this, 'contains')) { 1883 | ok = ok && keys.length == actual.length; 1884 | } 1885 | 1886 | // Key string 1887 | if (len > 1) { 1888 | keys = keys.map(function(key){ 1889 | return _.inspect(key); 1890 | }); 1891 | var last = keys.pop(); 1892 | str = keys.join(', ') + ', and ' + last; 1893 | } else { 1894 | str = _.inspect(keys[0]); 1895 | } 1896 | 1897 | // Form 1898 | str = (len > 1 ? 'keys ' : 'key ') + str; 1899 | 1900 | // Have / include 1901 | str = (flag(this, 'contains') ? 'contain ' : 'have ') + str; 1902 | 1903 | // Assertion 1904 | this.assert( 1905 | ok 1906 | , 'expected #{this} to ' + str 1907 | , 'expected #{this} to not ' + str 1908 | ); 1909 | } 1910 | 1911 | Assertion.addMethod('keys', assertKeys); 1912 | Assertion.addMethod('key', assertKeys); 1913 | 1914 | /** 1915 | * ### .throw(constructor) 1916 | * 1917 | * Asserts that the function target will throw a specific error, or specific type of error 1918 | * (as determined using `instanceof`), optionally with a RegExp or string inclusion test 1919 | * for the error's message. 1920 | * 1921 | * var err = new ReferenceError('This is a bad function.'); 1922 | * var fn = function () { throw err; } 1923 | * expect(fn).to.throw(ReferenceError); 1924 | * expect(fn).to.throw(Error); 1925 | * expect(fn).to.throw(/bad function/); 1926 | * expect(fn).to.not.throw('good function'); 1927 | * expect(fn).to.throw(ReferenceError, /bad function/); 1928 | * expect(fn).to.throw(err); 1929 | * expect(fn).to.not.throw(new RangeError('Out of range.')); 1930 | * 1931 | * Please note that when a throw expectation is negated, it will check each 1932 | * parameter independently, starting with error constructor type. The appropriate way 1933 | * to check for the existence of a type of error but for a message that does not match 1934 | * is to use `and`. 1935 | * 1936 | * expect(fn).to.throw(ReferenceError) 1937 | * .and.not.throw(/good function/); 1938 | * 1939 | * @name throw 1940 | * @alias throws 1941 | * @alias Throw 1942 | * @param {ErrorConstructor} constructor 1943 | * @param {String|RegExp} expected error message 1944 | * @param {String} message _optional_ 1945 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 1946 | * @api public 1947 | */ 1948 | 1949 | function assertThrows (constructor, errMsg, msg) { 1950 | if (msg) flag(this, 'message', msg); 1951 | var obj = flag(this, 'object'); 1952 | new Assertion(obj, msg).is.a('function'); 1953 | 1954 | var thrown = false 1955 | , desiredError = null 1956 | , name = null 1957 | , thrownError = null; 1958 | 1959 | if (arguments.length === 0) { 1960 | errMsg = null; 1961 | constructor = null; 1962 | } else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) { 1963 | errMsg = constructor; 1964 | constructor = null; 1965 | } else if (constructor && constructor instanceof Error) { 1966 | desiredError = constructor; 1967 | constructor = null; 1968 | errMsg = null; 1969 | } else if (typeof constructor === 'function') { 1970 | name = (new constructor()).name; 1971 | } else { 1972 | constructor = null; 1973 | } 1974 | 1975 | try { 1976 | obj(); 1977 | } catch (err) { 1978 | // first, check desired error 1979 | if (desiredError) { 1980 | this.assert( 1981 | err === desiredError 1982 | , 'expected #{this} to throw #{exp} but #{act} was thrown' 1983 | , 'expected #{this} to not throw #{exp}' 1984 | , desiredError 1985 | , err 1986 | ); 1987 | 1988 | return this; 1989 | } 1990 | // next, check constructor 1991 | if (constructor) { 1992 | this.assert( 1993 | err instanceof constructor 1994 | , 'expected #{this} to throw #{exp} but #{act} was thrown' 1995 | , 'expected #{this} to not throw #{exp} but #{act} was thrown' 1996 | , name 1997 | , err 1998 | ); 1999 | 2000 | if (!errMsg) return this; 2001 | } 2002 | // next, check message 2003 | var message = 'object' === _.type(err) && "message" in err 2004 | ? err.message 2005 | : '' + err; 2006 | 2007 | if ((message != null) && errMsg && errMsg instanceof RegExp) { 2008 | this.assert( 2009 | errMsg.exec(message) 2010 | , 'expected #{this} to throw error matching #{exp} but got #{act}' 2011 | , 'expected #{this} to throw error not matching #{exp}' 2012 | , errMsg 2013 | , message 2014 | ); 2015 | 2016 | return this; 2017 | } else if ((message != null) && errMsg && 'string' === typeof errMsg) { 2018 | this.assert( 2019 | ~message.indexOf(errMsg) 2020 | , 'expected #{this} to throw error including #{exp} but got #{act}' 2021 | , 'expected #{this} to throw error not including #{act}' 2022 | , errMsg 2023 | , message 2024 | ); 2025 | 2026 | return this; 2027 | } else { 2028 | thrown = true; 2029 | thrownError = err; 2030 | } 2031 | } 2032 | 2033 | var actuallyGot = '' 2034 | , expectedThrown = name !== null 2035 | ? name 2036 | : desiredError 2037 | ? '#{exp}' //_.inspect(desiredError) 2038 | : 'an error'; 2039 | 2040 | if (thrown) { 2041 | actuallyGot = ' but #{act} was thrown' 2042 | } 2043 | 2044 | this.assert( 2045 | thrown === true 2046 | , 'expected #{this} to throw ' + expectedThrown + actuallyGot 2047 | , 'expected #{this} to not throw ' + expectedThrown + actuallyGot 2048 | , desiredError 2049 | , thrownError 2050 | ); 2051 | }; 2052 | 2053 | Assertion.addMethod('throw', assertThrows); 2054 | Assertion.addMethod('throws', assertThrows); 2055 | Assertion.addMethod('Throw', assertThrows); 2056 | 2057 | /** 2058 | * ### .respondTo(method) 2059 | * 2060 | * Asserts that the object or class target will respond to a method. 2061 | * 2062 | * Klass.prototype.bar = function(){}; 2063 | * expect(Klass).to.respondTo('bar'); 2064 | * expect(obj).to.respondTo('bar'); 2065 | * 2066 | * To check if a constructor will respond to a static function, 2067 | * set the `itself` flag. 2068 | * 2069 | * Klass.baz = function(){}; 2070 | * expect(Klass).itself.to.respondTo('baz'); 2071 | * 2072 | * @name respondTo 2073 | * @param {String} method 2074 | * @param {String} message _optional_ 2075 | * @api public 2076 | */ 2077 | 2078 | Assertion.addMethod('respondTo', function (method, msg) { 2079 | if (msg) flag(this, 'message', msg); 2080 | var obj = flag(this, 'object') 2081 | , itself = flag(this, 'itself') 2082 | , context = ('function' === _.type(obj) && !itself) 2083 | ? obj.prototype[method] 2084 | : obj[method]; 2085 | 2086 | this.assert( 2087 | 'function' === typeof context 2088 | , 'expected #{this} to respond to ' + _.inspect(method) 2089 | , 'expected #{this} to not respond to ' + _.inspect(method) 2090 | ); 2091 | }); 2092 | 2093 | /** 2094 | * ### .itself 2095 | * 2096 | * Sets the `itself` flag, later used by the `respondTo` assertion. 2097 | * 2098 | * function Foo() {} 2099 | * Foo.bar = function() {} 2100 | * Foo.prototype.baz = function() {} 2101 | * 2102 | * expect(Foo).itself.to.respondTo('bar'); 2103 | * expect(Foo).itself.not.to.respondTo('baz'); 2104 | * 2105 | * @name itself 2106 | * @api public 2107 | */ 2108 | 2109 | Assertion.addProperty('itself', function () { 2110 | flag(this, 'itself', true); 2111 | }); 2112 | 2113 | /** 2114 | * ### .satisfy(method) 2115 | * 2116 | * Asserts that the target passes a given truth test. 2117 | * 2118 | * expect(1).to.satisfy(function(num) { return num > 0; }); 2119 | * 2120 | * @name satisfy 2121 | * @param {Function} matcher 2122 | * @param {String} message _optional_ 2123 | * @api public 2124 | */ 2125 | 2126 | Assertion.addMethod('satisfy', function (matcher, msg) { 2127 | if (msg) flag(this, 'message', msg); 2128 | var obj = flag(this, 'object'); 2129 | this.assert( 2130 | matcher(obj) 2131 | , 'expected #{this} to satisfy ' + _.objDisplay(matcher) 2132 | , 'expected #{this} to not satisfy' + _.objDisplay(matcher) 2133 | , this.negate ? false : true 2134 | , matcher(obj) 2135 | ); 2136 | }); 2137 | 2138 | /** 2139 | * ### .closeTo(expected, delta) 2140 | * 2141 | * Asserts that the target is equal `expected`, to within a +/- `delta` range. 2142 | * 2143 | * expect(1.5).to.be.closeTo(1, 0.5); 2144 | * 2145 | * @name closeTo 2146 | * @param {Number} expected 2147 | * @param {Number} delta 2148 | * @param {String} message _optional_ 2149 | * @api public 2150 | */ 2151 | 2152 | Assertion.addMethod('closeTo', function (expected, delta, msg) { 2153 | if (msg) flag(this, 'message', msg); 2154 | var obj = flag(this, 'object'); 2155 | this.assert( 2156 | Math.abs(obj - expected) <= delta 2157 | , 'expected #{this} to be close to ' + expected + ' +/- ' + delta 2158 | , 'expected #{this} not to be close to ' + expected + ' +/- ' + delta 2159 | ); 2160 | }); 2161 | 2162 | function isSubsetOf(subset, superset) { 2163 | return subset.every(function(elem) { 2164 | return superset.indexOf(elem) !== -1; 2165 | }) 2166 | } 2167 | 2168 | /** 2169 | * ### .members(set) 2170 | * 2171 | * Asserts that the target is a superset of `set`, 2172 | * or that the target and `set` have the same members. 2173 | * 2174 | * expect([1, 2, 3]).to.include.members([3, 2]); 2175 | * expect([1, 2, 3]).to.not.include.members([3, 2, 8]); 2176 | * 2177 | * expect([4, 2]).to.have.members([2, 4]); 2178 | * expect([5, 2]).to.not.have.members([5, 2, 1]); 2179 | * 2180 | * @name members 2181 | * @param {Array} set 2182 | * @param {String} message _optional_ 2183 | * @api public 2184 | */ 2185 | 2186 | Assertion.addMethod('members', function (subset, msg) { 2187 | if (msg) flag(this, 'message', msg); 2188 | var obj = flag(this, 'object'); 2189 | 2190 | new Assertion(obj).to.be.an('array'); 2191 | new Assertion(subset).to.be.an('array'); 2192 | 2193 | if (flag(this, 'contains')) { 2194 | return this.assert( 2195 | isSubsetOf(subset, obj) 2196 | , 'expected #{this} to be a superset of #{act}' 2197 | , 'expected #{this} to not be a superset of #{act}' 2198 | , obj 2199 | , subset 2200 | ); 2201 | } 2202 | 2203 | this.assert( 2204 | isSubsetOf(obj, subset) && isSubsetOf(subset, obj) 2205 | , 'expected #{this} to have the same members as #{act}' 2206 | , 'expected #{this} to not have the same members as #{act}' 2207 | , obj 2208 | , subset 2209 | ); 2210 | }); 2211 | }; 2212 | 2213 | }); 2214 | require.register("chai/lib/chai/interface/assert.js", function(exports, require, module){ 2215 | /*! 2216 | * chai 2217 | * Copyright(c) 2011-2013 Jake Luer 2218 | * MIT Licensed 2219 | */ 2220 | 2221 | 2222 | module.exports = function (chai, util) { 2223 | 2224 | /*! 2225 | * Chai dependencies. 2226 | */ 2227 | 2228 | var Assertion = chai.Assertion 2229 | , flag = util.flag; 2230 | 2231 | /*! 2232 | * Module export. 2233 | */ 2234 | 2235 | /** 2236 | * ### assert(expression, message) 2237 | * 2238 | * Write your own test expressions. 2239 | * 2240 | * assert('foo' !== 'bar', 'foo is not bar'); 2241 | * assert(Array.isArray([]), 'empty arrays are arrays'); 2242 | * 2243 | * @param {Mixed} expression to test for truthiness 2244 | * @param {String} message to display on error 2245 | * @name assert 2246 | * @api public 2247 | */ 2248 | 2249 | var assert = chai.assert = function (express, errmsg) { 2250 | var test = new Assertion(null); 2251 | test.assert( 2252 | express 2253 | , errmsg 2254 | , '[ negation message unavailable ]' 2255 | ); 2256 | }; 2257 | 2258 | /** 2259 | * ### .fail(actual, expected, [message], [operator]) 2260 | * 2261 | * Throw a failure. Node.js `assert` module-compatible. 2262 | * 2263 | * @name fail 2264 | * @param {Mixed} actual 2265 | * @param {Mixed} expected 2266 | * @param {String} message 2267 | * @param {String} operator 2268 | * @api public 2269 | */ 2270 | 2271 | assert.fail = function (actual, expected, message, operator) { 2272 | throw new chai.AssertionError({ 2273 | actual: actual 2274 | , expected: expected 2275 | , message: message 2276 | , operator: operator 2277 | , stackStartFunction: assert.fail 2278 | }); 2279 | }; 2280 | 2281 | /** 2282 | * ### .ok(object, [message]) 2283 | * 2284 | * Asserts that `object` is truthy. 2285 | * 2286 | * assert.ok('everything', 'everything is ok'); 2287 | * assert.ok(false, 'this will fail'); 2288 | * 2289 | * @name ok 2290 | * @param {Mixed} object to test 2291 | * @param {String} message 2292 | * @api public 2293 | */ 2294 | 2295 | assert.ok = function (val, msg) { 2296 | new Assertion(val, msg).is.ok; 2297 | }; 2298 | 2299 | /** 2300 | * ### .notOk(object, [message]) 2301 | * 2302 | * Asserts that `object` is falsy. 2303 | * 2304 | * assert.notOk('everything', 'this will fail'); 2305 | * assert.notOk(false, 'this will pass'); 2306 | * 2307 | * @name notOk 2308 | * @param {Mixed} object to test 2309 | * @param {String} message 2310 | * @api public 2311 | */ 2312 | 2313 | assert.notOk = function (val, msg) { 2314 | new Assertion(val, msg).is.not.ok; 2315 | }; 2316 | 2317 | /** 2318 | * ### .equal(actual, expected, [message]) 2319 | * 2320 | * Asserts non-strict equality (`==`) of `actual` and `expected`. 2321 | * 2322 | * assert.equal(3, '3', '== coerces values to strings'); 2323 | * 2324 | * @name equal 2325 | * @param {Mixed} actual 2326 | * @param {Mixed} expected 2327 | * @param {String} message 2328 | * @api public 2329 | */ 2330 | 2331 | assert.equal = function (act, exp, msg) { 2332 | var test = new Assertion(act, msg); 2333 | 2334 | test.assert( 2335 | exp == flag(test, 'object') 2336 | , 'expected #{this} to equal #{exp}' 2337 | , 'expected #{this} to not equal #{act}' 2338 | , exp 2339 | , act 2340 | ); 2341 | }; 2342 | 2343 | /** 2344 | * ### .notEqual(actual, expected, [message]) 2345 | * 2346 | * Asserts non-strict inequality (`!=`) of `actual` and `expected`. 2347 | * 2348 | * assert.notEqual(3, 4, 'these numbers are not equal'); 2349 | * 2350 | * @name notEqual 2351 | * @param {Mixed} actual 2352 | * @param {Mixed} expected 2353 | * @param {String} message 2354 | * @api public 2355 | */ 2356 | 2357 | assert.notEqual = function (act, exp, msg) { 2358 | var test = new Assertion(act, msg); 2359 | 2360 | test.assert( 2361 | exp != flag(test, 'object') 2362 | , 'expected #{this} to not equal #{exp}' 2363 | , 'expected #{this} to equal #{act}' 2364 | , exp 2365 | , act 2366 | ); 2367 | }; 2368 | 2369 | /** 2370 | * ### .strictEqual(actual, expected, [message]) 2371 | * 2372 | * Asserts strict equality (`===`) of `actual` and `expected`. 2373 | * 2374 | * assert.strictEqual(true, true, 'these booleans are strictly equal'); 2375 | * 2376 | * @name strictEqual 2377 | * @param {Mixed} actual 2378 | * @param {Mixed} expected 2379 | * @param {String} message 2380 | * @api public 2381 | */ 2382 | 2383 | assert.strictEqual = function (act, exp, msg) { 2384 | new Assertion(act, msg).to.equal(exp); 2385 | }; 2386 | 2387 | /** 2388 | * ### .notStrictEqual(actual, expected, [message]) 2389 | * 2390 | * Asserts strict inequality (`!==`) of `actual` and `expected`. 2391 | * 2392 | * assert.notStrictEqual(3, '3', 'no coercion for strict equality'); 2393 | * 2394 | * @name notStrictEqual 2395 | * @param {Mixed} actual 2396 | * @param {Mixed} expected 2397 | * @param {String} message 2398 | * @api public 2399 | */ 2400 | 2401 | assert.notStrictEqual = function (act, exp, msg) { 2402 | new Assertion(act, msg).to.not.equal(exp); 2403 | }; 2404 | 2405 | /** 2406 | * ### .deepEqual(actual, expected, [message]) 2407 | * 2408 | * Asserts that `actual` is deeply equal to `expected`. 2409 | * 2410 | * assert.deepEqual({ tea: 'green' }, { tea: 'green' }); 2411 | * 2412 | * @name deepEqual 2413 | * @param {Mixed} actual 2414 | * @param {Mixed} expected 2415 | * @param {String} message 2416 | * @api public 2417 | */ 2418 | 2419 | assert.deepEqual = function (act, exp, msg) { 2420 | new Assertion(act, msg).to.eql(exp); 2421 | }; 2422 | 2423 | /** 2424 | * ### .notDeepEqual(actual, expected, [message]) 2425 | * 2426 | * Assert that `actual` is not deeply equal to `expected`. 2427 | * 2428 | * assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); 2429 | * 2430 | * @name notDeepEqual 2431 | * @param {Mixed} actual 2432 | * @param {Mixed} expected 2433 | * @param {String} message 2434 | * @api public 2435 | */ 2436 | 2437 | assert.notDeepEqual = function (act, exp, msg) { 2438 | new Assertion(act, msg).to.not.eql(exp); 2439 | }; 2440 | 2441 | /** 2442 | * ### .isTrue(value, [message]) 2443 | * 2444 | * Asserts that `value` is true. 2445 | * 2446 | * var teaServed = true; 2447 | * assert.isTrue(teaServed, 'the tea has been served'); 2448 | * 2449 | * @name isTrue 2450 | * @param {Mixed} value 2451 | * @param {String} message 2452 | * @api public 2453 | */ 2454 | 2455 | assert.isTrue = function (val, msg) { 2456 | new Assertion(val, msg).is['true']; 2457 | }; 2458 | 2459 | /** 2460 | * ### .isFalse(value, [message]) 2461 | * 2462 | * Asserts that `value` is false. 2463 | * 2464 | * var teaServed = false; 2465 | * assert.isFalse(teaServed, 'no tea yet? hmm...'); 2466 | * 2467 | * @name isFalse 2468 | * @param {Mixed} value 2469 | * @param {String} message 2470 | * @api public 2471 | */ 2472 | 2473 | assert.isFalse = function (val, msg) { 2474 | new Assertion(val, msg).is['false']; 2475 | }; 2476 | 2477 | /** 2478 | * ### .isNull(value, [message]) 2479 | * 2480 | * Asserts that `value` is null. 2481 | * 2482 | * assert.isNull(err, 'there was no error'); 2483 | * 2484 | * @name isNull 2485 | * @param {Mixed} value 2486 | * @param {String} message 2487 | * @api public 2488 | */ 2489 | 2490 | assert.isNull = function (val, msg) { 2491 | new Assertion(val, msg).to.equal(null); 2492 | }; 2493 | 2494 | /** 2495 | * ### .isNotNull(value, [message]) 2496 | * 2497 | * Asserts that `value` is not null. 2498 | * 2499 | * var tea = 'tasty chai'; 2500 | * assert.isNotNull(tea, 'great, time for tea!'); 2501 | * 2502 | * @name isNotNull 2503 | * @param {Mixed} value 2504 | * @param {String} message 2505 | * @api public 2506 | */ 2507 | 2508 | assert.isNotNull = function (val, msg) { 2509 | new Assertion(val, msg).to.not.equal(null); 2510 | }; 2511 | 2512 | /** 2513 | * ### .isUndefined(value, [message]) 2514 | * 2515 | * Asserts that `value` is `undefined`. 2516 | * 2517 | * var tea; 2518 | * assert.isUndefined(tea, 'no tea defined'); 2519 | * 2520 | * @name isUndefined 2521 | * @param {Mixed} value 2522 | * @param {String} message 2523 | * @api public 2524 | */ 2525 | 2526 | assert.isUndefined = function (val, msg) { 2527 | new Assertion(val, msg).to.equal(undefined); 2528 | }; 2529 | 2530 | /** 2531 | * ### .isDefined(value, [message]) 2532 | * 2533 | * Asserts that `value` is not `undefined`. 2534 | * 2535 | * var tea = 'cup of chai'; 2536 | * assert.isDefined(tea, 'tea has been defined'); 2537 | * 2538 | * @name isDefined 2539 | * @param {Mixed} value 2540 | * @param {String} message 2541 | * @api public 2542 | */ 2543 | 2544 | assert.isDefined = function (val, msg) { 2545 | new Assertion(val, msg).to.not.equal(undefined); 2546 | }; 2547 | 2548 | /** 2549 | * ### .isFunction(value, [message]) 2550 | * 2551 | * Asserts that `value` is a function. 2552 | * 2553 | * function serveTea() { return 'cup of tea'; }; 2554 | * assert.isFunction(serveTea, 'great, we can have tea now'); 2555 | * 2556 | * @name isFunction 2557 | * @param {Mixed} value 2558 | * @param {String} message 2559 | * @api public 2560 | */ 2561 | 2562 | assert.isFunction = function (val, msg) { 2563 | new Assertion(val, msg).to.be.a('function'); 2564 | }; 2565 | 2566 | /** 2567 | * ### .isNotFunction(value, [message]) 2568 | * 2569 | * Asserts that `value` is _not_ a function. 2570 | * 2571 | * var serveTea = [ 'heat', 'pour', 'sip' ]; 2572 | * assert.isNotFunction(serveTea, 'great, we have listed the steps'); 2573 | * 2574 | * @name isNotFunction 2575 | * @param {Mixed} value 2576 | * @param {String} message 2577 | * @api public 2578 | */ 2579 | 2580 | assert.isNotFunction = function (val, msg) { 2581 | new Assertion(val, msg).to.not.be.a('function'); 2582 | }; 2583 | 2584 | /** 2585 | * ### .isObject(value, [message]) 2586 | * 2587 | * Asserts that `value` is an object (as revealed by 2588 | * `Object.prototype.toString`). 2589 | * 2590 | * var selection = { name: 'Chai', serve: 'with spices' }; 2591 | * assert.isObject(selection, 'tea selection is an object'); 2592 | * 2593 | * @name isObject 2594 | * @param {Mixed} value 2595 | * @param {String} message 2596 | * @api public 2597 | */ 2598 | 2599 | assert.isObject = function (val, msg) { 2600 | new Assertion(val, msg).to.be.a('object'); 2601 | }; 2602 | 2603 | /** 2604 | * ### .isNotObject(value, [message]) 2605 | * 2606 | * Asserts that `value` is _not_ an object. 2607 | * 2608 | * var selection = 'chai' 2609 | * assert.isObject(selection, 'tea selection is not an object'); 2610 | * assert.isObject(null, 'null is not an object'); 2611 | * 2612 | * @name isNotObject 2613 | * @param {Mixed} value 2614 | * @param {String} message 2615 | * @api public 2616 | */ 2617 | 2618 | assert.isNotObject = function (val, msg) { 2619 | new Assertion(val, msg).to.not.be.a('object'); 2620 | }; 2621 | 2622 | /** 2623 | * ### .isArray(value, [message]) 2624 | * 2625 | * Asserts that `value` is an array. 2626 | * 2627 | * var menu = [ 'green', 'chai', 'oolong' ]; 2628 | * assert.isArray(menu, 'what kind of tea do we want?'); 2629 | * 2630 | * @name isArray 2631 | * @param {Mixed} value 2632 | * @param {String} message 2633 | * @api public 2634 | */ 2635 | 2636 | assert.isArray = function (val, msg) { 2637 | new Assertion(val, msg).to.be.an('array'); 2638 | }; 2639 | 2640 | /** 2641 | * ### .isNotArray(value, [message]) 2642 | * 2643 | * Asserts that `value` is _not_ an array. 2644 | * 2645 | * var menu = 'green|chai|oolong'; 2646 | * assert.isNotArray(menu, 'what kind of tea do we want?'); 2647 | * 2648 | * @name isNotArray 2649 | * @param {Mixed} value 2650 | * @param {String} message 2651 | * @api public 2652 | */ 2653 | 2654 | assert.isNotArray = function (val, msg) { 2655 | new Assertion(val, msg).to.not.be.an('array'); 2656 | }; 2657 | 2658 | /** 2659 | * ### .isString(value, [message]) 2660 | * 2661 | * Asserts that `value` is a string. 2662 | * 2663 | * var teaOrder = 'chai'; 2664 | * assert.isString(teaOrder, 'order placed'); 2665 | * 2666 | * @name isString 2667 | * @param {Mixed} value 2668 | * @param {String} message 2669 | * @api public 2670 | */ 2671 | 2672 | assert.isString = function (val, msg) { 2673 | new Assertion(val, msg).to.be.a('string'); 2674 | }; 2675 | 2676 | /** 2677 | * ### .isNotString(value, [message]) 2678 | * 2679 | * Asserts that `value` is _not_ a string. 2680 | * 2681 | * var teaOrder = 4; 2682 | * assert.isNotString(teaOrder, 'order placed'); 2683 | * 2684 | * @name isNotString 2685 | * @param {Mixed} value 2686 | * @param {String} message 2687 | * @api public 2688 | */ 2689 | 2690 | assert.isNotString = function (val, msg) { 2691 | new Assertion(val, msg).to.not.be.a('string'); 2692 | }; 2693 | 2694 | /** 2695 | * ### .isNumber(value, [message]) 2696 | * 2697 | * Asserts that `value` is a number. 2698 | * 2699 | * var cups = 2; 2700 | * assert.isNumber(cups, 'how many cups'); 2701 | * 2702 | * @name isNumber 2703 | * @param {Number} value 2704 | * @param {String} message 2705 | * @api public 2706 | */ 2707 | 2708 | assert.isNumber = function (val, msg) { 2709 | new Assertion(val, msg).to.be.a('number'); 2710 | }; 2711 | 2712 | /** 2713 | * ### .isNotNumber(value, [message]) 2714 | * 2715 | * Asserts that `value` is _not_ a number. 2716 | * 2717 | * var cups = '2 cups please'; 2718 | * assert.isNotNumber(cups, 'how many cups'); 2719 | * 2720 | * @name isNotNumber 2721 | * @param {Mixed} value 2722 | * @param {String} message 2723 | * @api public 2724 | */ 2725 | 2726 | assert.isNotNumber = function (val, msg) { 2727 | new Assertion(val, msg).to.not.be.a('number'); 2728 | }; 2729 | 2730 | /** 2731 | * ### .isBoolean(value, [message]) 2732 | * 2733 | * Asserts that `value` is a boolean. 2734 | * 2735 | * var teaReady = true 2736 | * , teaServed = false; 2737 | * 2738 | * assert.isBoolean(teaReady, 'is the tea ready'); 2739 | * assert.isBoolean(teaServed, 'has tea been served'); 2740 | * 2741 | * @name isBoolean 2742 | * @param {Mixed} value 2743 | * @param {String} message 2744 | * @api public 2745 | */ 2746 | 2747 | assert.isBoolean = function (val, msg) { 2748 | new Assertion(val, msg).to.be.a('boolean'); 2749 | }; 2750 | 2751 | /** 2752 | * ### .isNotBoolean(value, [message]) 2753 | * 2754 | * Asserts that `value` is _not_ a boolean. 2755 | * 2756 | * var teaReady = 'yep' 2757 | * , teaServed = 'nope'; 2758 | * 2759 | * assert.isNotBoolean(teaReady, 'is the tea ready'); 2760 | * assert.isNotBoolean(teaServed, 'has tea been served'); 2761 | * 2762 | * @name isNotBoolean 2763 | * @param {Mixed} value 2764 | * @param {String} message 2765 | * @api public 2766 | */ 2767 | 2768 | assert.isNotBoolean = function (val, msg) { 2769 | new Assertion(val, msg).to.not.be.a('boolean'); 2770 | }; 2771 | 2772 | /** 2773 | * ### .typeOf(value, name, [message]) 2774 | * 2775 | * Asserts that `value`'s type is `name`, as determined by 2776 | * `Object.prototype.toString`. 2777 | * 2778 | * assert.typeOf({ tea: 'chai' }, 'object', 'we have an object'); 2779 | * assert.typeOf(['chai', 'jasmine'], 'array', 'we have an array'); 2780 | * assert.typeOf('tea', 'string', 'we have a string'); 2781 | * assert.typeOf(/tea/, 'regexp', 'we have a regular expression'); 2782 | * assert.typeOf(null, 'null', 'we have a null'); 2783 | * assert.typeOf(undefined, 'undefined', 'we have an undefined'); 2784 | * 2785 | * @name typeOf 2786 | * @param {Mixed} value 2787 | * @param {String} name 2788 | * @param {String} message 2789 | * @api public 2790 | */ 2791 | 2792 | assert.typeOf = function (val, type, msg) { 2793 | new Assertion(val, msg).to.be.a(type); 2794 | }; 2795 | 2796 | /** 2797 | * ### .notTypeOf(value, name, [message]) 2798 | * 2799 | * Asserts that `value`'s type is _not_ `name`, as determined by 2800 | * `Object.prototype.toString`. 2801 | * 2802 | * assert.notTypeOf('tea', 'number', 'strings are not numbers'); 2803 | * 2804 | * @name notTypeOf 2805 | * @param {Mixed} value 2806 | * @param {String} typeof name 2807 | * @param {String} message 2808 | * @api public 2809 | */ 2810 | 2811 | assert.notTypeOf = function (val, type, msg) { 2812 | new Assertion(val, msg).to.not.be.a(type); 2813 | }; 2814 | 2815 | /** 2816 | * ### .instanceOf(object, constructor, [message]) 2817 | * 2818 | * Asserts that `value` is an instance of `constructor`. 2819 | * 2820 | * var Tea = function (name) { this.name = name; } 2821 | * , chai = new Tea('chai'); 2822 | * 2823 | * assert.instanceOf(chai, Tea, 'chai is an instance of tea'); 2824 | * 2825 | * @name instanceOf 2826 | * @param {Object} object 2827 | * @param {Constructor} constructor 2828 | * @param {String} message 2829 | * @api public 2830 | */ 2831 | 2832 | assert.instanceOf = function (val, type, msg) { 2833 | new Assertion(val, msg).to.be.instanceOf(type); 2834 | }; 2835 | 2836 | /** 2837 | * ### .notInstanceOf(object, constructor, [message]) 2838 | * 2839 | * Asserts `value` is not an instance of `constructor`. 2840 | * 2841 | * var Tea = function (name) { this.name = name; } 2842 | * , chai = new String('chai'); 2843 | * 2844 | * assert.notInstanceOf(chai, Tea, 'chai is not an instance of tea'); 2845 | * 2846 | * @name notInstanceOf 2847 | * @param {Object} object 2848 | * @param {Constructor} constructor 2849 | * @param {String} message 2850 | * @api public 2851 | */ 2852 | 2853 | assert.notInstanceOf = function (val, type, msg) { 2854 | new Assertion(val, msg).to.not.be.instanceOf(type); 2855 | }; 2856 | 2857 | /** 2858 | * ### .include(haystack, needle, [message]) 2859 | * 2860 | * Asserts that `haystack` includes `needle`. Works 2861 | * for strings and arrays. 2862 | * 2863 | * assert.include('foobar', 'bar', 'foobar contains string "bar"'); 2864 | * assert.include([ 1, 2, 3 ], 3, 'array contains value'); 2865 | * 2866 | * @name include 2867 | * @param {Array|String} haystack 2868 | * @param {Mixed} needle 2869 | * @param {String} message 2870 | * @api public 2871 | */ 2872 | 2873 | assert.include = function (exp, inc, msg) { 2874 | var obj = new Assertion(exp, msg); 2875 | 2876 | if (Array.isArray(exp)) { 2877 | obj.to.include(inc); 2878 | } else if ('string' === typeof exp) { 2879 | obj.to.contain.string(inc); 2880 | } else { 2881 | throw new chai.AssertionError( 2882 | 'expected an array or string' 2883 | , null 2884 | , assert.include 2885 | ); 2886 | } 2887 | }; 2888 | 2889 | /** 2890 | * ### .notInclude(haystack, needle, [message]) 2891 | * 2892 | * Asserts that `haystack` does not include `needle`. Works 2893 | * for strings and arrays. 2894 | *i 2895 | * assert.notInclude('foobar', 'baz', 'string not include substring'); 2896 | * assert.notInclude([ 1, 2, 3 ], 4, 'array not include contain value'); 2897 | * 2898 | * @name notInclude 2899 | * @param {Array|String} haystack 2900 | * @param {Mixed} needle 2901 | * @param {String} message 2902 | * @api public 2903 | */ 2904 | 2905 | assert.notInclude = function (exp, inc, msg) { 2906 | var obj = new Assertion(exp, msg); 2907 | 2908 | if (Array.isArray(exp)) { 2909 | obj.to.not.include(inc); 2910 | } else if ('string' === typeof exp) { 2911 | obj.to.not.contain.string(inc); 2912 | } else { 2913 | throw new chai.AssertionError( 2914 | 'expected an array or string' 2915 | , null 2916 | , assert.notInclude 2917 | ); 2918 | } 2919 | }; 2920 | 2921 | /** 2922 | * ### .match(value, regexp, [message]) 2923 | * 2924 | * Asserts that `value` matches the regular expression `regexp`. 2925 | * 2926 | * assert.match('foobar', /^foo/, 'regexp matches'); 2927 | * 2928 | * @name match 2929 | * @param {Mixed} value 2930 | * @param {RegExp} regexp 2931 | * @param {String} message 2932 | * @api public 2933 | */ 2934 | 2935 | assert.match = function (exp, re, msg) { 2936 | new Assertion(exp, msg).to.match(re); 2937 | }; 2938 | 2939 | /** 2940 | * ### .notMatch(value, regexp, [message]) 2941 | * 2942 | * Asserts that `value` does not match the regular expression `regexp`. 2943 | * 2944 | * assert.notMatch('foobar', /^foo/, 'regexp does not match'); 2945 | * 2946 | * @name notMatch 2947 | * @param {Mixed} value 2948 | * @param {RegExp} regexp 2949 | * @param {String} message 2950 | * @api public 2951 | */ 2952 | 2953 | assert.notMatch = function (exp, re, msg) { 2954 | new Assertion(exp, msg).to.not.match(re); 2955 | }; 2956 | 2957 | /** 2958 | * ### .property(object, property, [message]) 2959 | * 2960 | * Asserts that `object` has a property named by `property`. 2961 | * 2962 | * assert.property({ tea: { green: 'matcha' }}, 'tea'); 2963 | * 2964 | * @name property 2965 | * @param {Object} object 2966 | * @param {String} property 2967 | * @param {String} message 2968 | * @api public 2969 | */ 2970 | 2971 | assert.property = function (obj, prop, msg) { 2972 | new Assertion(obj, msg).to.have.property(prop); 2973 | }; 2974 | 2975 | /** 2976 | * ### .notProperty(object, property, [message]) 2977 | * 2978 | * Asserts that `object` does _not_ have a property named by `property`. 2979 | * 2980 | * assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); 2981 | * 2982 | * @name notProperty 2983 | * @param {Object} object 2984 | * @param {String} property 2985 | * @param {String} message 2986 | * @api public 2987 | */ 2988 | 2989 | assert.notProperty = function (obj, prop, msg) { 2990 | new Assertion(obj, msg).to.not.have.property(prop); 2991 | }; 2992 | 2993 | /** 2994 | * ### .deepProperty(object, property, [message]) 2995 | * 2996 | * Asserts that `object` has a property named by `property`, which can be a 2997 | * string using dot- and bracket-notation for deep reference. 2998 | * 2999 | * assert.deepProperty({ tea: { green: 'matcha' }}, 'tea.green'); 3000 | * 3001 | * @name deepProperty 3002 | * @param {Object} object 3003 | * @param {String} property 3004 | * @param {String} message 3005 | * @api public 3006 | */ 3007 | 3008 | assert.deepProperty = function (obj, prop, msg) { 3009 | new Assertion(obj, msg).to.have.deep.property(prop); 3010 | }; 3011 | 3012 | /** 3013 | * ### .notDeepProperty(object, property, [message]) 3014 | * 3015 | * Asserts that `object` does _not_ have a property named by `property`, which 3016 | * can be a string using dot- and bracket-notation for deep reference. 3017 | * 3018 | * assert.notDeepProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); 3019 | * 3020 | * @name notDeepProperty 3021 | * @param {Object} object 3022 | * @param {String} property 3023 | * @param {String} message 3024 | * @api public 3025 | */ 3026 | 3027 | assert.notDeepProperty = function (obj, prop, msg) { 3028 | new Assertion(obj, msg).to.not.have.deep.property(prop); 3029 | }; 3030 | 3031 | /** 3032 | * ### .propertyVal(object, property, value, [message]) 3033 | * 3034 | * Asserts that `object` has a property named by `property` with value given 3035 | * by `value`. 3036 | * 3037 | * assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); 3038 | * 3039 | * @name propertyVal 3040 | * @param {Object} object 3041 | * @param {String} property 3042 | * @param {Mixed} value 3043 | * @param {String} message 3044 | * @api public 3045 | */ 3046 | 3047 | assert.propertyVal = function (obj, prop, val, msg) { 3048 | new Assertion(obj, msg).to.have.property(prop, val); 3049 | }; 3050 | 3051 | /** 3052 | * ### .propertyNotVal(object, property, value, [message]) 3053 | * 3054 | * Asserts that `object` has a property named by `property`, but with a value 3055 | * different from that given by `value`. 3056 | * 3057 | * assert.propertyNotVal({ tea: 'is good' }, 'tea', 'is bad'); 3058 | * 3059 | * @name propertyNotVal 3060 | * @param {Object} object 3061 | * @param {String} property 3062 | * @param {Mixed} value 3063 | * @param {String} message 3064 | * @api public 3065 | */ 3066 | 3067 | assert.propertyNotVal = function (obj, prop, val, msg) { 3068 | new Assertion(obj, msg).to.not.have.property(prop, val); 3069 | }; 3070 | 3071 | /** 3072 | * ### .deepPropertyVal(object, property, value, [message]) 3073 | * 3074 | * Asserts that `object` has a property named by `property` with value given 3075 | * by `value`. `property` can use dot- and bracket-notation for deep 3076 | * reference. 3077 | * 3078 | * assert.deepPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); 3079 | * 3080 | * @name deepPropertyVal 3081 | * @param {Object} object 3082 | * @param {String} property 3083 | * @param {Mixed} value 3084 | * @param {String} message 3085 | * @api public 3086 | */ 3087 | 3088 | assert.deepPropertyVal = function (obj, prop, val, msg) { 3089 | new Assertion(obj, msg).to.have.deep.property(prop, val); 3090 | }; 3091 | 3092 | /** 3093 | * ### .deepPropertyNotVal(object, property, value, [message]) 3094 | * 3095 | * Asserts that `object` has a property named by `property`, but with a value 3096 | * different from that given by `value`. `property` can use dot- and 3097 | * bracket-notation for deep reference. 3098 | * 3099 | * assert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); 3100 | * 3101 | * @name deepPropertyNotVal 3102 | * @param {Object} object 3103 | * @param {String} property 3104 | * @param {Mixed} value 3105 | * @param {String} message 3106 | * @api public 3107 | */ 3108 | 3109 | assert.deepPropertyNotVal = function (obj, prop, val, msg) { 3110 | new Assertion(obj, msg).to.not.have.deep.property(prop, val); 3111 | }; 3112 | 3113 | /** 3114 | * ### .lengthOf(object, length, [message]) 3115 | * 3116 | * Asserts that `object` has a `length` property with the expected value. 3117 | * 3118 | * assert.lengthOf([1,2,3], 3, 'array has length of 3'); 3119 | * assert.lengthOf('foobar', 5, 'string has length of 6'); 3120 | * 3121 | * @name lengthOf 3122 | * @param {Mixed} object 3123 | * @param {Number} length 3124 | * @param {String} message 3125 | * @api public 3126 | */ 3127 | 3128 | assert.lengthOf = function (exp, len, msg) { 3129 | new Assertion(exp, msg).to.have.length(len); 3130 | }; 3131 | 3132 | /** 3133 | * ### .throws(function, [constructor/string/regexp], [string/regexp], [message]) 3134 | * 3135 | * Asserts that `function` will throw an error that is an instance of 3136 | * `constructor`, or alternately that it will throw an error with message 3137 | * matching `regexp`. 3138 | * 3139 | * assert.throw(fn, 'function throws a reference error'); 3140 | * assert.throw(fn, /function throws a reference error/); 3141 | * assert.throw(fn, ReferenceError); 3142 | * assert.throw(fn, ReferenceError, 'function throws a reference error'); 3143 | * assert.throw(fn, ReferenceError, /function throws a reference error/); 3144 | * 3145 | * @name throws 3146 | * @alias throw 3147 | * @alias Throw 3148 | * @param {Function} function 3149 | * @param {ErrorConstructor} constructor 3150 | * @param {RegExp} regexp 3151 | * @param {String} message 3152 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 3153 | * @api public 3154 | */ 3155 | 3156 | assert.Throw = function (fn, errt, errs, msg) { 3157 | if ('string' === typeof errt || errt instanceof RegExp) { 3158 | errs = errt; 3159 | errt = null; 3160 | } 3161 | 3162 | new Assertion(fn, msg).to.Throw(errt, errs); 3163 | }; 3164 | 3165 | /** 3166 | * ### .doesNotThrow(function, [constructor/regexp], [message]) 3167 | * 3168 | * Asserts that `function` will _not_ throw an error that is an instance of 3169 | * `constructor`, or alternately that it will not throw an error with message 3170 | * matching `regexp`. 3171 | * 3172 | * assert.doesNotThrow(fn, Error, 'function does not throw'); 3173 | * 3174 | * @name doesNotThrow 3175 | * @param {Function} function 3176 | * @param {ErrorConstructor} constructor 3177 | * @param {RegExp} regexp 3178 | * @param {String} message 3179 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 3180 | * @api public 3181 | */ 3182 | 3183 | assert.doesNotThrow = function (fn, type, msg) { 3184 | if ('string' === typeof type) { 3185 | msg = type; 3186 | type = null; 3187 | } 3188 | 3189 | new Assertion(fn, msg).to.not.Throw(type); 3190 | }; 3191 | 3192 | /** 3193 | * ### .operator(val1, operator, val2, [message]) 3194 | * 3195 | * Compares two values using `operator`. 3196 | * 3197 | * assert.operator(1, '<', 2, 'everything is ok'); 3198 | * assert.operator(1, '>', 2, 'this will fail'); 3199 | * 3200 | * @name operator 3201 | * @param {Mixed} val1 3202 | * @param {String} operator 3203 | * @param {Mixed} val2 3204 | * @param {String} message 3205 | * @api public 3206 | */ 3207 | 3208 | assert.operator = function (val, operator, val2, msg) { 3209 | if (!~['==', '===', '>', '>=', '<', '<=', '!=', '!=='].indexOf(operator)) { 3210 | throw new Error('Invalid operator "' + operator + '"'); 3211 | } 3212 | var test = new Assertion(eval(val + operator + val2), msg); 3213 | test.assert( 3214 | true === flag(test, 'object') 3215 | , 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) 3216 | , 'expected ' + util.inspect(val) + ' to not be ' + operator + ' ' + util.inspect(val2) ); 3217 | }; 3218 | 3219 | /** 3220 | * ### .closeTo(actual, expected, delta, [message]) 3221 | * 3222 | * Asserts that the target is equal `expected`, to within a +/- `delta` range. 3223 | * 3224 | * assert.closeTo(1.5, 1, 0.5, 'numbers are close'); 3225 | * 3226 | * @name closeTo 3227 | * @param {Number} actual 3228 | * @param {Number} expected 3229 | * @param {Number} delta 3230 | * @param {String} message 3231 | * @api public 3232 | */ 3233 | 3234 | assert.closeTo = function (act, exp, delta, msg) { 3235 | new Assertion(act, msg).to.be.closeTo(exp, delta); 3236 | }; 3237 | 3238 | /** 3239 | * ### .sameMembers(set1, set2, [message]) 3240 | * 3241 | * Asserts that `set1` and `set2` have the same members. 3242 | * Order is not taken into account. 3243 | * 3244 | * assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members'); 3245 | * 3246 | * @name sameMembers 3247 | * @param {Array} superset 3248 | * @param {Array} subset 3249 | * @param {String} message 3250 | * @api public 3251 | */ 3252 | 3253 | assert.sameMembers = function (set1, set2, msg) { 3254 | new Assertion(set1, msg).to.have.same.members(set2); 3255 | } 3256 | 3257 | /** 3258 | * ### .includeMembers(superset, subset, [message]) 3259 | * 3260 | * Asserts that `subset` is included in `superset`. 3261 | * Order is not taken into account. 3262 | * 3263 | * assert.includeMembers([ 1, 2, 3 ], [ 2, 1 ], 'include members'); 3264 | * 3265 | * @name includeMembers 3266 | * @param {Array} superset 3267 | * @param {Array} subset 3268 | * @param {String} message 3269 | * @api public 3270 | */ 3271 | 3272 | assert.includeMembers = function (superset, subset, msg) { 3273 | new Assertion(superset, msg).to.include.members(subset); 3274 | } 3275 | 3276 | /*! 3277 | * Undocumented / untested 3278 | */ 3279 | 3280 | assert.ifError = function (val, msg) { 3281 | new Assertion(val, msg).to.not.be.ok; 3282 | }; 3283 | 3284 | /*! 3285 | * Aliases. 3286 | */ 3287 | 3288 | (function alias(name, as){ 3289 | assert[as] = assert[name]; 3290 | return alias; 3291 | }) 3292 | ('Throw', 'throw') 3293 | ('Throw', 'throws'); 3294 | }; 3295 | 3296 | }); 3297 | require.register("chai/lib/chai/interface/expect.js", function(exports, require, module){ 3298 | /*! 3299 | * chai 3300 | * Copyright(c) 2011-2013 Jake Luer 3301 | * MIT Licensed 3302 | */ 3303 | 3304 | module.exports = function (chai, util) { 3305 | chai.expect = function (val, message) { 3306 | return new chai.Assertion(val, message); 3307 | }; 3308 | }; 3309 | 3310 | 3311 | }); 3312 | require.register("chai/lib/chai/interface/should.js", function(exports, require, module){ 3313 | /*! 3314 | * chai 3315 | * Copyright(c) 2011-2013 Jake Luer 3316 | * MIT Licensed 3317 | */ 3318 | 3319 | module.exports = function (chai, util) { 3320 | var Assertion = chai.Assertion; 3321 | 3322 | function loadShould () { 3323 | // modify Object.prototype to have `should` 3324 | Object.defineProperty(Object.prototype, 'should', 3325 | { 3326 | set: function (value) { 3327 | // See https://github.com/chaijs/chai/issues/86: this makes 3328 | // `whatever.should = someValue` actually set `someValue`, which is 3329 | // especially useful for `global.should = require('chai').should()`. 3330 | // 3331 | // Note that we have to use [[DefineProperty]] instead of [[Put]] 3332 | // since otherwise we would trigger this very setter! 3333 | Object.defineProperty(this, 'should', { 3334 | value: value, 3335 | enumerable: true, 3336 | configurable: true, 3337 | writable: true 3338 | }); 3339 | } 3340 | , get: function(){ 3341 | if (this instanceof String || this instanceof Number) { 3342 | return new Assertion(this.constructor(this)); 3343 | } else if (this instanceof Boolean) { 3344 | return new Assertion(this == true); 3345 | } 3346 | return new Assertion(this); 3347 | } 3348 | , configurable: true 3349 | }); 3350 | 3351 | var should = {}; 3352 | 3353 | should.equal = function (val1, val2, msg) { 3354 | new Assertion(val1, msg).to.equal(val2); 3355 | }; 3356 | 3357 | should.Throw = function (fn, errt, errs, msg) { 3358 | new Assertion(fn, msg).to.Throw(errt, errs); 3359 | }; 3360 | 3361 | should.exist = function (val, msg) { 3362 | new Assertion(val, msg).to.exist; 3363 | } 3364 | 3365 | // negation 3366 | should.not = {} 3367 | 3368 | should.not.equal = function (val1, val2, msg) { 3369 | new Assertion(val1, msg).to.not.equal(val2); 3370 | }; 3371 | 3372 | should.not.Throw = function (fn, errt, errs, msg) { 3373 | new Assertion(fn, msg).to.not.Throw(errt, errs); 3374 | }; 3375 | 3376 | should.not.exist = function (val, msg) { 3377 | new Assertion(val, msg).to.not.exist; 3378 | } 3379 | 3380 | should['throw'] = should['Throw']; 3381 | should.not['throw'] = should.not['Throw']; 3382 | 3383 | return should; 3384 | }; 3385 | 3386 | chai.should = loadShould; 3387 | chai.Should = loadShould; 3388 | }; 3389 | 3390 | }); 3391 | require.register("chai/lib/chai/utils/addChainableMethod.js", function(exports, require, module){ 3392 | /*! 3393 | * Chai - addChainingMethod utility 3394 | * Copyright(c) 2012-2013 Jake Luer 3395 | * MIT Licensed 3396 | */ 3397 | 3398 | /*! 3399 | * Module dependencies 3400 | */ 3401 | 3402 | var transferFlags = require('./transferFlags'); 3403 | 3404 | /*! 3405 | * Module variables 3406 | */ 3407 | 3408 | // Check whether `__proto__` is supported 3409 | var hasProtoSupport = '__proto__' in Object; 3410 | 3411 | // Without `__proto__` support, this module will need to add properties to a function. 3412 | // However, some Function.prototype methods cannot be overwritten, 3413 | // and there seems no easy cross-platform way to detect them (@see chaijs/chai/issues/69). 3414 | var excludeNames = /^(?:length|name|arguments|caller)$/; 3415 | 3416 | // Cache `Function` properties 3417 | var call = Function.prototype.call, 3418 | apply = Function.prototype.apply; 3419 | 3420 | /** 3421 | * ### addChainableMethod (ctx, name, method, chainingBehavior) 3422 | * 3423 | * Adds a method to an object, such that the method can also be chained. 3424 | * 3425 | * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { 3426 | * var obj = utils.flag(this, 'object'); 3427 | * new chai.Assertion(obj).to.be.equal(str); 3428 | * }); 3429 | * 3430 | * Can also be accessed directly from `chai.Assertion`. 3431 | * 3432 | * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); 3433 | * 3434 | * The result can then be used as both a method assertion, executing both `method` and 3435 | * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. 3436 | * 3437 | * expect(fooStr).to.be.foo('bar'); 3438 | * expect(fooStr).to.be.foo.equal('foo'); 3439 | * 3440 | * @param {Object} ctx object to which the method is added 3441 | * @param {String} name of method to add 3442 | * @param {Function} method function to be used for `name`, when called 3443 | * @param {Function} chainingBehavior function to be called every time the property is accessed 3444 | * @name addChainableMethod 3445 | * @api public 3446 | */ 3447 | 3448 | module.exports = function (ctx, name, method, chainingBehavior) { 3449 | if (typeof chainingBehavior !== 'function') 3450 | chainingBehavior = function () { }; 3451 | 3452 | Object.defineProperty(ctx, name, 3453 | { get: function () { 3454 | chainingBehavior.call(this); 3455 | 3456 | var assert = function () { 3457 | var result = method.apply(this, arguments); 3458 | return result === undefined ? this : result; 3459 | }; 3460 | 3461 | // Use `__proto__` if available 3462 | if (hasProtoSupport) { 3463 | // Inherit all properties from the object by replacing the `Function` prototype 3464 | var prototype = assert.__proto__ = Object.create(this); 3465 | // Restore the `call` and `apply` methods from `Function` 3466 | prototype.call = call; 3467 | prototype.apply = apply; 3468 | } 3469 | // Otherwise, redefine all properties (slow!) 3470 | else { 3471 | var asserterNames = Object.getOwnPropertyNames(ctx); 3472 | asserterNames.forEach(function (asserterName) { 3473 | if (!excludeNames.test(asserterName)) { 3474 | var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); 3475 | Object.defineProperty(assert, asserterName, pd); 3476 | } 3477 | }); 3478 | } 3479 | 3480 | transferFlags(this, assert); 3481 | return assert; 3482 | } 3483 | , configurable: true 3484 | }); 3485 | }; 3486 | 3487 | }); 3488 | require.register("chai/lib/chai/utils/addMethod.js", function(exports, require, module){ 3489 | /*! 3490 | * Chai - addMethod utility 3491 | * Copyright(c) 2012-2013 Jake Luer 3492 | * MIT Licensed 3493 | */ 3494 | 3495 | /** 3496 | * ### .addMethod (ctx, name, method) 3497 | * 3498 | * Adds a method to the prototype of an object. 3499 | * 3500 | * utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { 3501 | * var obj = utils.flag(this, 'object'); 3502 | * new chai.Assertion(obj).to.be.equal(str); 3503 | * }); 3504 | * 3505 | * Can also be accessed directly from `chai.Assertion`. 3506 | * 3507 | * chai.Assertion.addMethod('foo', fn); 3508 | * 3509 | * Then can be used as any other assertion. 3510 | * 3511 | * expect(fooStr).to.be.foo('bar'); 3512 | * 3513 | * @param {Object} ctx object to which the method is added 3514 | * @param {String} name of method to add 3515 | * @param {Function} method function to be used for name 3516 | * @name addMethod 3517 | * @api public 3518 | */ 3519 | 3520 | module.exports = function (ctx, name, method) { 3521 | ctx[name] = function () { 3522 | var result = method.apply(this, arguments); 3523 | return result === undefined ? this : result; 3524 | }; 3525 | }; 3526 | 3527 | }); 3528 | require.register("chai/lib/chai/utils/addProperty.js", function(exports, require, module){ 3529 | /*! 3530 | * Chai - addProperty utility 3531 | * Copyright(c) 2012-2013 Jake Luer 3532 | * MIT Licensed 3533 | */ 3534 | 3535 | /** 3536 | * ### addProperty (ctx, name, getter) 3537 | * 3538 | * Adds a property to the prototype of an object. 3539 | * 3540 | * utils.addProperty(chai.Assertion.prototype, 'foo', function () { 3541 | * var obj = utils.flag(this, 'object'); 3542 | * new chai.Assertion(obj).to.be.instanceof(Foo); 3543 | * }); 3544 | * 3545 | * Can also be accessed directly from `chai.Assertion`. 3546 | * 3547 | * chai.Assertion.addProperty('foo', fn); 3548 | * 3549 | * Then can be used as any other assertion. 3550 | * 3551 | * expect(myFoo).to.be.foo; 3552 | * 3553 | * @param {Object} ctx object to which the property is added 3554 | * @param {String} name of property to add 3555 | * @param {Function} getter function to be used for name 3556 | * @name addProperty 3557 | * @api public 3558 | */ 3559 | 3560 | module.exports = function (ctx, name, getter) { 3561 | Object.defineProperty(ctx, name, 3562 | { get: function () { 3563 | var result = getter.call(this); 3564 | return result === undefined ? this : result; 3565 | } 3566 | , configurable: true 3567 | }); 3568 | }; 3569 | 3570 | }); 3571 | require.register("chai/lib/chai/utils/flag.js", function(exports, require, module){ 3572 | /*! 3573 | * Chai - flag utility 3574 | * Copyright(c) 2012-2013 Jake Luer 3575 | * MIT Licensed 3576 | */ 3577 | 3578 | /** 3579 | * ### flag(object ,key, [value]) 3580 | * 3581 | * Get or set a flag value on an object. If a 3582 | * value is provided it will be set, else it will 3583 | * return the currently set value or `undefined` if 3584 | * the value is not set. 3585 | * 3586 | * utils.flag(this, 'foo', 'bar'); // setter 3587 | * utils.flag(this, 'foo'); // getter, returns `bar` 3588 | * 3589 | * @param {Object} object (constructed Assertion 3590 | * @param {String} key 3591 | * @param {Mixed} value (optional) 3592 | * @name flag 3593 | * @api private 3594 | */ 3595 | 3596 | module.exports = function (obj, key, value) { 3597 | var flags = obj.__flags || (obj.__flags = Object.create(null)); 3598 | if (arguments.length === 3) { 3599 | flags[key] = value; 3600 | } else { 3601 | return flags[key]; 3602 | } 3603 | }; 3604 | 3605 | }); 3606 | require.register("chai/lib/chai/utils/getActual.js", function(exports, require, module){ 3607 | /*! 3608 | * Chai - getActual utility 3609 | * Copyright(c) 2012-2013 Jake Luer 3610 | * MIT Licensed 3611 | */ 3612 | 3613 | /** 3614 | * # getActual(object, [actual]) 3615 | * 3616 | * Returns the `actual` value for an Assertion 3617 | * 3618 | * @param {Object} object (constructed Assertion) 3619 | * @param {Arguments} chai.Assertion.prototype.assert arguments 3620 | */ 3621 | 3622 | module.exports = function (obj, args) { 3623 | var actual = args[4]; 3624 | return 'undefined' !== typeof actual ? actual : obj._obj; 3625 | }; 3626 | 3627 | }); 3628 | require.register("chai/lib/chai/utils/getEnumerableProperties.js", function(exports, require, module){ 3629 | /*! 3630 | * Chai - getEnumerableProperties utility 3631 | * Copyright(c) 2012-2013 Jake Luer 3632 | * MIT Licensed 3633 | */ 3634 | 3635 | /** 3636 | * ### .getEnumerableProperties(object) 3637 | * 3638 | * This allows the retrieval of enumerable property names of an object, 3639 | * inherited or not. 3640 | * 3641 | * @param {Object} object 3642 | * @returns {Array} 3643 | * @name getEnumerableProperties 3644 | * @api public 3645 | */ 3646 | 3647 | module.exports = function getEnumerableProperties(object) { 3648 | var result = []; 3649 | for (var name in object) { 3650 | result.push(name); 3651 | } 3652 | return result; 3653 | }; 3654 | 3655 | }); 3656 | require.register("chai/lib/chai/utils/getMessage.js", function(exports, require, module){ 3657 | /*! 3658 | * Chai - message composition utility 3659 | * Copyright(c) 2012-2013 Jake Luer 3660 | * MIT Licensed 3661 | */ 3662 | 3663 | /*! 3664 | * Module dependancies 3665 | */ 3666 | 3667 | var flag = require('./flag') 3668 | , getActual = require('./getActual') 3669 | , inspect = require('./inspect') 3670 | , objDisplay = require('./objDisplay'); 3671 | 3672 | /** 3673 | * ### .getMessage(object, message, negateMessage) 3674 | * 3675 | * Construct the error message based on flags 3676 | * and template tags. Template tags will return 3677 | * a stringified inspection of the object referenced. 3678 | * 3679 | * Message template tags: 3680 | * - `#{this}` current asserted object 3681 | * - `#{act}` actual value 3682 | * - `#{exp}` expected value 3683 | * 3684 | * @param {Object} object (constructed Assertion) 3685 | * @param {Arguments} chai.Assertion.prototype.assert arguments 3686 | * @name getMessage 3687 | * @api public 3688 | */ 3689 | 3690 | module.exports = function (obj, args) { 3691 | var negate = flag(obj, 'negate') 3692 | , val = flag(obj, 'object') 3693 | , expected = args[3] 3694 | , actual = getActual(obj, args) 3695 | , msg = negate ? args[2] : args[1] 3696 | , flagMsg = flag(obj, 'message'); 3697 | 3698 | msg = msg || ''; 3699 | msg = msg 3700 | .replace(/#{this}/g, objDisplay(val)) 3701 | .replace(/#{act}/g, objDisplay(actual)) 3702 | .replace(/#{exp}/g, objDisplay(expected)); 3703 | 3704 | return flagMsg ? flagMsg + ': ' + msg : msg; 3705 | }; 3706 | 3707 | }); 3708 | require.register("chai/lib/chai/utils/getName.js", function(exports, require, module){ 3709 | /*! 3710 | * Chai - getName utility 3711 | * Copyright(c) 2012-2013 Jake Luer 3712 | * MIT Licensed 3713 | */ 3714 | 3715 | /** 3716 | * # getName(func) 3717 | * 3718 | * Gets the name of a function, in a cross-browser way. 3719 | * 3720 | * @param {Function} a function (usually a constructor) 3721 | */ 3722 | 3723 | module.exports = function (func) { 3724 | if (func.name) return func.name; 3725 | 3726 | var match = /^\s?function ([^(]*)\(/.exec(func); 3727 | return match && match[1] ? match[1] : ""; 3728 | }; 3729 | 3730 | }); 3731 | require.register("chai/lib/chai/utils/getPathValue.js", function(exports, require, module){ 3732 | /*! 3733 | * Chai - getPathValue utility 3734 | * Copyright(c) 2012-2013 Jake Luer 3735 | * @see https://github.com/logicalparadox/filtr 3736 | * MIT Licensed 3737 | */ 3738 | 3739 | /** 3740 | * ### .getPathValue(path, object) 3741 | * 3742 | * This allows the retrieval of values in an 3743 | * object given a string path. 3744 | * 3745 | * var obj = { 3746 | * prop1: { 3747 | * arr: ['a', 'b', 'c'] 3748 | * , str: 'Hello' 3749 | * } 3750 | * , prop2: { 3751 | * arr: [ { nested: 'Universe' } ] 3752 | * , str: 'Hello again!' 3753 | * } 3754 | * } 3755 | * 3756 | * The following would be the results. 3757 | * 3758 | * getPathValue('prop1.str', obj); // Hello 3759 | * getPathValue('prop1.att[2]', obj); // b 3760 | * getPathValue('prop2.arr[0].nested', obj); // Universe 3761 | * 3762 | * @param {String} path 3763 | * @param {Object} object 3764 | * @returns {Object} value or `undefined` 3765 | * @name getPathValue 3766 | * @api public 3767 | */ 3768 | 3769 | var getPathValue = module.exports = function (path, obj) { 3770 | var parsed = parsePath(path); 3771 | return _getPathValue(parsed, obj); 3772 | }; 3773 | 3774 | /*! 3775 | * ## parsePath(path) 3776 | * 3777 | * Helper function used to parse string object 3778 | * paths. Use in conjunction with `_getPathValue`. 3779 | * 3780 | * var parsed = parsePath('myobject.property.subprop'); 3781 | * 3782 | * ### Paths: 3783 | * 3784 | * * Can be as near infinitely deep and nested 3785 | * * Arrays are also valid using the formal `myobject.document[3].property`. 3786 | * 3787 | * @param {String} path 3788 | * @returns {Object} parsed 3789 | * @api private 3790 | */ 3791 | 3792 | function parsePath (path) { 3793 | var str = path.replace(/\[/g, '.[') 3794 | , parts = str.match(/(\\\.|[^.]+?)+/g); 3795 | return parts.map(function (value) { 3796 | var re = /\[(\d+)\]$/ 3797 | , mArr = re.exec(value) 3798 | if (mArr) return { i: parseFloat(mArr[1]) }; 3799 | else return { p: value }; 3800 | }); 3801 | }; 3802 | 3803 | /*! 3804 | * ## _getPathValue(parsed, obj) 3805 | * 3806 | * Helper companion function for `.parsePath` that returns 3807 | * the value located at the parsed address. 3808 | * 3809 | * var value = getPathValue(parsed, obj); 3810 | * 3811 | * @param {Object} parsed definition from `parsePath`. 3812 | * @param {Object} object to search against 3813 | * @returns {Object|Undefined} value 3814 | * @api private 3815 | */ 3816 | 3817 | function _getPathValue (parsed, obj) { 3818 | var tmp = obj 3819 | , res; 3820 | for (var i = 0, l = parsed.length; i < l; i++) { 3821 | var part = parsed[i]; 3822 | if (tmp) { 3823 | if ('undefined' !== typeof part.p) 3824 | tmp = tmp[part.p]; 3825 | else if ('undefined' !== typeof part.i) 3826 | tmp = tmp[part.i]; 3827 | if (i == (l - 1)) res = tmp; 3828 | } else { 3829 | res = undefined; 3830 | } 3831 | } 3832 | return res; 3833 | }; 3834 | 3835 | }); 3836 | require.register("chai/lib/chai/utils/getProperties.js", function(exports, require, module){ 3837 | /*! 3838 | * Chai - getProperties utility 3839 | * Copyright(c) 2012-2013 Jake Luer 3840 | * MIT Licensed 3841 | */ 3842 | 3843 | /** 3844 | * ### .getProperties(object) 3845 | * 3846 | * This allows the retrieval of property names of an object, enumerable or not, 3847 | * inherited or not. 3848 | * 3849 | * @param {Object} object 3850 | * @returns {Array} 3851 | * @name getProperties 3852 | * @api public 3853 | */ 3854 | 3855 | module.exports = function getProperties(object) { 3856 | var result = Object.getOwnPropertyNames(subject); 3857 | 3858 | function addProperty(property) { 3859 | if (result.indexOf(property) === -1) { 3860 | result.push(property); 3861 | } 3862 | } 3863 | 3864 | var proto = Object.getPrototypeOf(subject); 3865 | while (proto !== null) { 3866 | Object.getOwnPropertyNames(proto).forEach(addProperty); 3867 | proto = Object.getPrototypeOf(proto); 3868 | } 3869 | 3870 | return result; 3871 | }; 3872 | 3873 | }); 3874 | require.register("chai/lib/chai/utils/index.js", function(exports, require, module){ 3875 | /*! 3876 | * chai 3877 | * Copyright(c) 2011 Jake Luer 3878 | * MIT Licensed 3879 | */ 3880 | 3881 | /*! 3882 | * Main exports 3883 | */ 3884 | 3885 | var exports = module.exports = {}; 3886 | 3887 | /*! 3888 | * test utility 3889 | */ 3890 | 3891 | exports.test = require('./test'); 3892 | 3893 | /*! 3894 | * type utility 3895 | */ 3896 | 3897 | exports.type = require('./type'); 3898 | 3899 | /*! 3900 | * message utility 3901 | */ 3902 | 3903 | exports.getMessage = require('./getMessage'); 3904 | 3905 | /*! 3906 | * actual utility 3907 | */ 3908 | 3909 | exports.getActual = require('./getActual'); 3910 | 3911 | /*! 3912 | * Inspect util 3913 | */ 3914 | 3915 | exports.inspect = require('./inspect'); 3916 | 3917 | /*! 3918 | * Object Display util 3919 | */ 3920 | 3921 | exports.objDisplay = require('./objDisplay'); 3922 | 3923 | /*! 3924 | * Flag utility 3925 | */ 3926 | 3927 | exports.flag = require('./flag'); 3928 | 3929 | /*! 3930 | * Flag transferring utility 3931 | */ 3932 | 3933 | exports.transferFlags = require('./transferFlags'); 3934 | 3935 | /*! 3936 | * Deep equal utility 3937 | */ 3938 | 3939 | exports.eql = require('deep-eql'); 3940 | 3941 | /*! 3942 | * Deep path value 3943 | */ 3944 | 3945 | exports.getPathValue = require('./getPathValue'); 3946 | 3947 | /*! 3948 | * Function name 3949 | */ 3950 | 3951 | exports.getName = require('./getName'); 3952 | 3953 | /*! 3954 | * add Property 3955 | */ 3956 | 3957 | exports.addProperty = require('./addProperty'); 3958 | 3959 | /*! 3960 | * add Method 3961 | */ 3962 | 3963 | exports.addMethod = require('./addMethod'); 3964 | 3965 | /*! 3966 | * overwrite Property 3967 | */ 3968 | 3969 | exports.overwriteProperty = require('./overwriteProperty'); 3970 | 3971 | /*! 3972 | * overwrite Method 3973 | */ 3974 | 3975 | exports.overwriteMethod = require('./overwriteMethod'); 3976 | 3977 | /*! 3978 | * Add a chainable method 3979 | */ 3980 | 3981 | exports.addChainableMethod = require('./addChainableMethod'); 3982 | 3983 | 3984 | }); 3985 | require.register("chai/lib/chai/utils/inspect.js", function(exports, require, module){ 3986 | // This is (almost) directly from Node.js utils 3987 | // https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js 3988 | 3989 | var getName = require('./getName'); 3990 | var getProperties = require('./getProperties'); 3991 | var getEnumerableProperties = require('./getEnumerableProperties'); 3992 | 3993 | module.exports = inspect; 3994 | 3995 | /** 3996 | * Echos the value of a value. Trys to print the value out 3997 | * in the best way possible given the different types. 3998 | * 3999 | * @param {Object} obj The object to print out. 4000 | * @param {Boolean} showHidden Flag that shows hidden (not enumerable) 4001 | * properties of objects. 4002 | * @param {Number} depth Depth in which to descend in object. Default is 2. 4003 | * @param {Boolean} colors Flag to turn on ANSI escape codes to color the 4004 | * output. Default is false (no coloring). 4005 | */ 4006 | function inspect(obj, showHidden, depth, colors) { 4007 | var ctx = { 4008 | showHidden: showHidden, 4009 | seen: [], 4010 | stylize: function (str) { return str; } 4011 | }; 4012 | return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); 4013 | } 4014 | 4015 | // https://gist.github.com/1044128/ 4016 | var getOuterHTML = function(element) { 4017 | if ('outerHTML' in element) return element.outerHTML; 4018 | var ns = "http://www.w3.org/1999/xhtml"; 4019 | var container = document.createElementNS(ns, '_'); 4020 | var elemProto = (window.HTMLElement || window.Element).prototype; 4021 | var xmlSerializer = new XMLSerializer(); 4022 | var html; 4023 | if (document.xmlVersion) { 4024 | return xmlSerializer.serializeToString(element); 4025 | } else { 4026 | container.appendChild(element.cloneNode(false)); 4027 | html = container.innerHTML.replace('><', '>' + element.innerHTML + '<'); 4028 | container.innerHTML = ''; 4029 | return html; 4030 | } 4031 | }; 4032 | 4033 | // Returns true if object is a DOM element. 4034 | var isDOMElement = function (object) { 4035 | if (typeof HTMLElement === 'object') { 4036 | return object instanceof HTMLElement; 4037 | } else { 4038 | return object && 4039 | typeof object === 'object' && 4040 | object.nodeType === 1 && 4041 | typeof object.nodeName === 'string'; 4042 | } 4043 | }; 4044 | 4045 | function formatValue(ctx, value, recurseTimes) { 4046 | // Provide a hook for user-specified inspect functions. 4047 | // Check that value is an object with an inspect function on it 4048 | if (value && typeof value.inspect === 'function' && 4049 | // Filter out the util module, it's inspect function is special 4050 | value.inspect !== exports.inspect && 4051 | // Also filter out any prototype objects using the circular check. 4052 | !(value.constructor && value.constructor.prototype === value)) { 4053 | var ret = value.inspect(recurseTimes); 4054 | if (typeof ret !== 'string') { 4055 | ret = formatValue(ctx, ret, recurseTimes); 4056 | } 4057 | return ret; 4058 | } 4059 | 4060 | // Primitive types cannot have properties 4061 | var primitive = formatPrimitive(ctx, value); 4062 | if (primitive) { 4063 | return primitive; 4064 | } 4065 | 4066 | // If it's DOM elem, get outer HTML. 4067 | if (isDOMElement(value)) { 4068 | return getOuterHTML(value); 4069 | } 4070 | 4071 | // Look up the keys of the object. 4072 | var visibleKeys = getEnumerableProperties(value); 4073 | var keys = ctx.showHidden ? getProperties(value) : visibleKeys; 4074 | 4075 | // Some type of object without properties can be shortcutted. 4076 | // In IE, errors have a single `stack` property, or if they are vanilla `Error`, 4077 | // a `stack` plus `description` property; ignore those for consistency. 4078 | if (keys.length === 0 || (isError(value) && ( 4079 | (keys.length === 1 && keys[0] === 'stack') || 4080 | (keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') 4081 | ))) { 4082 | if (typeof value === 'function') { 4083 | var name = getName(value); 4084 | var nameSuffix = name ? ': ' + name : ''; 4085 | return ctx.stylize('[Function' + nameSuffix + ']', 'special'); 4086 | } 4087 | if (isRegExp(value)) { 4088 | return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 4089 | } 4090 | if (isDate(value)) { 4091 | return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); 4092 | } 4093 | if (isError(value)) { 4094 | return formatError(value); 4095 | } 4096 | } 4097 | 4098 | var base = '', array = false, braces = ['{', '}']; 4099 | 4100 | // Make Array say that they are Array 4101 | if (isArray(value)) { 4102 | array = true; 4103 | braces = ['[', ']']; 4104 | } 4105 | 4106 | // Make functions say that they are functions 4107 | if (typeof value === 'function') { 4108 | var name = getName(value); 4109 | var nameSuffix = name ? ': ' + name : ''; 4110 | base = ' [Function' + nameSuffix + ']'; 4111 | } 4112 | 4113 | // Make RegExps say that they are RegExps 4114 | if (isRegExp(value)) { 4115 | base = ' ' + RegExp.prototype.toString.call(value); 4116 | } 4117 | 4118 | // Make dates with properties first say the date 4119 | if (isDate(value)) { 4120 | base = ' ' + Date.prototype.toUTCString.call(value); 4121 | } 4122 | 4123 | // Make error with message first say the error 4124 | if (isError(value)) { 4125 | return formatError(value); 4126 | } 4127 | 4128 | if (keys.length === 0 && (!array || value.length == 0)) { 4129 | return braces[0] + base + braces[1]; 4130 | } 4131 | 4132 | if (recurseTimes < 0) { 4133 | if (isRegExp(value)) { 4134 | return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 4135 | } else { 4136 | return ctx.stylize('[Object]', 'special'); 4137 | } 4138 | } 4139 | 4140 | ctx.seen.push(value); 4141 | 4142 | var output; 4143 | if (array) { 4144 | output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); 4145 | } else { 4146 | output = keys.map(function(key) { 4147 | return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); 4148 | }); 4149 | } 4150 | 4151 | ctx.seen.pop(); 4152 | 4153 | return reduceToSingleString(output, base, braces); 4154 | } 4155 | 4156 | 4157 | function formatPrimitive(ctx, value) { 4158 | switch (typeof value) { 4159 | case 'undefined': 4160 | return ctx.stylize('undefined', 'undefined'); 4161 | 4162 | case 'string': 4163 | var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') 4164 | .replace(/'/g, "\\'") 4165 | .replace(/\\"/g, '"') + '\''; 4166 | return ctx.stylize(simple, 'string'); 4167 | 4168 | case 'number': 4169 | return ctx.stylize('' + value, 'number'); 4170 | 4171 | case 'boolean': 4172 | return ctx.stylize('' + value, 'boolean'); 4173 | } 4174 | // For some reason typeof null is "object", so special case here. 4175 | if (value === null) { 4176 | return ctx.stylize('null', 'null'); 4177 | } 4178 | } 4179 | 4180 | 4181 | function formatError(value) { 4182 | return '[' + Error.prototype.toString.call(value) + ']'; 4183 | } 4184 | 4185 | 4186 | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { 4187 | var output = []; 4188 | for (var i = 0, l = value.length; i < l; ++i) { 4189 | if (Object.prototype.hasOwnProperty.call(value, String(i))) { 4190 | output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 4191 | String(i), true)); 4192 | } else { 4193 | output.push(''); 4194 | } 4195 | } 4196 | keys.forEach(function(key) { 4197 | if (!key.match(/^\d+$/)) { 4198 | output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 4199 | key, true)); 4200 | } 4201 | }); 4202 | return output; 4203 | } 4204 | 4205 | 4206 | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { 4207 | var name, str; 4208 | if (value.__lookupGetter__) { 4209 | if (value.__lookupGetter__(key)) { 4210 | if (value.__lookupSetter__(key)) { 4211 | str = ctx.stylize('[Getter/Setter]', 'special'); 4212 | } else { 4213 | str = ctx.stylize('[Getter]', 'special'); 4214 | } 4215 | } else { 4216 | if (value.__lookupSetter__(key)) { 4217 | str = ctx.stylize('[Setter]', 'special'); 4218 | } 4219 | } 4220 | } 4221 | if (visibleKeys.indexOf(key) < 0) { 4222 | name = '[' + key + ']'; 4223 | } 4224 | if (!str) { 4225 | if (ctx.seen.indexOf(value[key]) < 0) { 4226 | if (recurseTimes === null) { 4227 | str = formatValue(ctx, value[key], null); 4228 | } else { 4229 | str = formatValue(ctx, value[key], recurseTimes - 1); 4230 | } 4231 | if (str.indexOf('\n') > -1) { 4232 | if (array) { 4233 | str = str.split('\n').map(function(line) { 4234 | return ' ' + line; 4235 | }).join('\n').substr(2); 4236 | } else { 4237 | str = '\n' + str.split('\n').map(function(line) { 4238 | return ' ' + line; 4239 | }).join('\n'); 4240 | } 4241 | } 4242 | } else { 4243 | str = ctx.stylize('[Circular]', 'special'); 4244 | } 4245 | } 4246 | if (typeof name === 'undefined') { 4247 | if (array && key.match(/^\d+$/)) { 4248 | return str; 4249 | } 4250 | name = JSON.stringify('' + key); 4251 | if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { 4252 | name = name.substr(1, name.length - 2); 4253 | name = ctx.stylize(name, 'name'); 4254 | } else { 4255 | name = name.replace(/'/g, "\\'") 4256 | .replace(/\\"/g, '"') 4257 | .replace(/(^"|"$)/g, "'"); 4258 | name = ctx.stylize(name, 'string'); 4259 | } 4260 | } 4261 | 4262 | return name + ': ' + str; 4263 | } 4264 | 4265 | 4266 | function reduceToSingleString(output, base, braces) { 4267 | var numLinesEst = 0; 4268 | var length = output.reduce(function(prev, cur) { 4269 | numLinesEst++; 4270 | if (cur.indexOf('\n') >= 0) numLinesEst++; 4271 | return prev + cur.length + 1; 4272 | }, 0); 4273 | 4274 | if (length > 60) { 4275 | return braces[0] + 4276 | (base === '' ? '' : base + '\n ') + 4277 | ' ' + 4278 | output.join(',\n ') + 4279 | ' ' + 4280 | braces[1]; 4281 | } 4282 | 4283 | return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; 4284 | } 4285 | 4286 | function isArray(ar) { 4287 | return Array.isArray(ar) || 4288 | (typeof ar === 'object' && objectToString(ar) === '[object Array]'); 4289 | } 4290 | 4291 | function isRegExp(re) { 4292 | return typeof re === 'object' && objectToString(re) === '[object RegExp]'; 4293 | } 4294 | 4295 | function isDate(d) { 4296 | return typeof d === 'object' && objectToString(d) === '[object Date]'; 4297 | } 4298 | 4299 | function isError(e) { 4300 | return typeof e === 'object' && objectToString(e) === '[object Error]'; 4301 | } 4302 | 4303 | function objectToString(o) { 4304 | return Object.prototype.toString.call(o); 4305 | } 4306 | 4307 | }); 4308 | require.register("chai/lib/chai/utils/objDisplay.js", function(exports, require, module){ 4309 | /*! 4310 | * Chai - flag utility 4311 | * Copyright(c) 2012-2013 Jake Luer 4312 | * MIT Licensed 4313 | */ 4314 | 4315 | /*! 4316 | * Module dependancies 4317 | */ 4318 | 4319 | var inspect = require('./inspect'); 4320 | 4321 | /** 4322 | * ### .objDisplay (object) 4323 | * 4324 | * Determines if an object or an array matches 4325 | * criteria to be inspected in-line for error 4326 | * messages or should be truncated. 4327 | * 4328 | * @param {Mixed} javascript object to inspect 4329 | * @name objDisplay 4330 | * @api public 4331 | */ 4332 | 4333 | module.exports = function (obj) { 4334 | var str = inspect(obj) 4335 | , type = Object.prototype.toString.call(obj); 4336 | 4337 | if (str.length >= 40) { 4338 | if (type === '[object Function]') { 4339 | return !obj.name || obj.name === '' 4340 | ? '[Function]' 4341 | : '[Function: ' + obj.name + ']'; 4342 | } else if (type === '[object Array]') { 4343 | return '[ Array(' + obj.length + ') ]'; 4344 | } else if (type === '[object Object]') { 4345 | var keys = Object.keys(obj) 4346 | , kstr = keys.length > 2 4347 | ? keys.splice(0, 2).join(', ') + ', ...' 4348 | : keys.join(', '); 4349 | return '{ Object (' + kstr + ') }'; 4350 | } else { 4351 | return str; 4352 | } 4353 | } else { 4354 | return str; 4355 | } 4356 | }; 4357 | 4358 | }); 4359 | require.register("chai/lib/chai/utils/overwriteMethod.js", function(exports, require, module){ 4360 | /*! 4361 | * Chai - overwriteMethod utility 4362 | * Copyright(c) 2012-2013 Jake Luer 4363 | * MIT Licensed 4364 | */ 4365 | 4366 | /** 4367 | * ### overwriteMethod (ctx, name, fn) 4368 | * 4369 | * Overwites an already existing method and provides 4370 | * access to previous function. Must return function 4371 | * to be used for name. 4372 | * 4373 | * utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { 4374 | * return function (str) { 4375 | * var obj = utils.flag(this, 'object'); 4376 | * if (obj instanceof Foo) { 4377 | * new chai.Assertion(obj.value).to.equal(str); 4378 | * } else { 4379 | * _super.apply(this, arguments); 4380 | * } 4381 | * } 4382 | * }); 4383 | * 4384 | * Can also be accessed directly from `chai.Assertion`. 4385 | * 4386 | * chai.Assertion.overwriteMethod('foo', fn); 4387 | * 4388 | * Then can be used as any other assertion. 4389 | * 4390 | * expect(myFoo).to.equal('bar'); 4391 | * 4392 | * @param {Object} ctx object whose method is to be overwritten 4393 | * @param {String} name of method to overwrite 4394 | * @param {Function} method function that returns a function to be used for name 4395 | * @name overwriteMethod 4396 | * @api public 4397 | */ 4398 | 4399 | module.exports = function (ctx, name, method) { 4400 | var _method = ctx[name] 4401 | , _super = function () { return this; }; 4402 | 4403 | if (_method && 'function' === typeof _method) 4404 | _super = _method; 4405 | 4406 | ctx[name] = function () { 4407 | var result = method(_super).apply(this, arguments); 4408 | return result === undefined ? this : result; 4409 | } 4410 | }; 4411 | 4412 | }); 4413 | require.register("chai/lib/chai/utils/overwriteProperty.js", function(exports, require, module){ 4414 | /*! 4415 | * Chai - overwriteProperty utility 4416 | * Copyright(c) 2012-2013 Jake Luer 4417 | * MIT Licensed 4418 | */ 4419 | 4420 | /** 4421 | * ### overwriteProperty (ctx, name, fn) 4422 | * 4423 | * Overwites an already existing property getter and provides 4424 | * access to previous value. Must return function to use as getter. 4425 | * 4426 | * utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { 4427 | * return function () { 4428 | * var obj = utils.flag(this, 'object'); 4429 | * if (obj instanceof Foo) { 4430 | * new chai.Assertion(obj.name).to.equal('bar'); 4431 | * } else { 4432 | * _super.call(this); 4433 | * } 4434 | * } 4435 | * }); 4436 | * 4437 | * 4438 | * Can also be accessed directly from `chai.Assertion`. 4439 | * 4440 | * chai.Assertion.overwriteProperty('foo', fn); 4441 | * 4442 | * Then can be used as any other assertion. 4443 | * 4444 | * expect(myFoo).to.be.ok; 4445 | * 4446 | * @param {Object} ctx object whose property is to be overwritten 4447 | * @param {String} name of property to overwrite 4448 | * @param {Function} getter function that returns a getter function to be used for name 4449 | * @name overwriteProperty 4450 | * @api public 4451 | */ 4452 | 4453 | module.exports = function (ctx, name, getter) { 4454 | var _get = Object.getOwnPropertyDescriptor(ctx, name) 4455 | , _super = function () {}; 4456 | 4457 | if (_get && 'function' === typeof _get.get) 4458 | _super = _get.get 4459 | 4460 | Object.defineProperty(ctx, name, 4461 | { get: function () { 4462 | var result = getter(_super).call(this); 4463 | return result === undefined ? this : result; 4464 | } 4465 | , configurable: true 4466 | }); 4467 | }; 4468 | 4469 | }); 4470 | require.register("chai/lib/chai/utils/test.js", function(exports, require, module){ 4471 | /*! 4472 | * Chai - test utility 4473 | * Copyright(c) 2012-2013 Jake Luer 4474 | * MIT Licensed 4475 | */ 4476 | 4477 | /*! 4478 | * Module dependancies 4479 | */ 4480 | 4481 | var flag = require('./flag'); 4482 | 4483 | /** 4484 | * # test(object, expression) 4485 | * 4486 | * Test and object for expression. 4487 | * 4488 | * @param {Object} object (constructed Assertion) 4489 | * @param {Arguments} chai.Assertion.prototype.assert arguments 4490 | */ 4491 | 4492 | module.exports = function (obj, args) { 4493 | var negate = flag(obj, 'negate') 4494 | , expr = args[0]; 4495 | return negate ? !expr : expr; 4496 | }; 4497 | 4498 | }); 4499 | require.register("chai/lib/chai/utils/transferFlags.js", function(exports, require, module){ 4500 | /*! 4501 | * Chai - transferFlags utility 4502 | * Copyright(c) 2012-2013 Jake Luer 4503 | * MIT Licensed 4504 | */ 4505 | 4506 | /** 4507 | * ### transferFlags(assertion, object, includeAll = true) 4508 | * 4509 | * Transfer all the flags for `assertion` to `object`. If 4510 | * `includeAll` is set to `false`, then the base Chai 4511 | * assertion flags (namely `object`, `ssfi`, and `message`) 4512 | * will not be transferred. 4513 | * 4514 | * 4515 | * var newAssertion = new Assertion(); 4516 | * utils.transferFlags(assertion, newAssertion); 4517 | * 4518 | * var anotherAsseriton = new Assertion(myObj); 4519 | * utils.transferFlags(assertion, anotherAssertion, false); 4520 | * 4521 | * @param {Assertion} assertion the assertion to transfer the flags from 4522 | * @param {Object} object the object to transfer the flags too; usually a new assertion 4523 | * @param {Boolean} includeAll 4524 | * @name getAllFlags 4525 | * @api private 4526 | */ 4527 | 4528 | module.exports = function (assertion, object, includeAll) { 4529 | var flags = assertion.__flags || (assertion.__flags = Object.create(null)); 4530 | 4531 | if (!object.__flags) { 4532 | object.__flags = Object.create(null); 4533 | } 4534 | 4535 | includeAll = arguments.length === 3 ? includeAll : true; 4536 | 4537 | for (var flag in flags) { 4538 | if (includeAll || 4539 | (flag !== 'object' && flag !== 'ssfi' && flag != 'message')) { 4540 | object.__flags[flag] = flags[flag]; 4541 | } 4542 | } 4543 | }; 4544 | 4545 | }); 4546 | require.register("chai/lib/chai/utils/type.js", function(exports, require, module){ 4547 | /*! 4548 | * Chai - type utility 4549 | * Copyright(c) 2012-2013 Jake Luer 4550 | * MIT Licensed 4551 | */ 4552 | 4553 | /*! 4554 | * Detectable javascript natives 4555 | */ 4556 | 4557 | var natives = { 4558 | '[object Arguments]': 'arguments' 4559 | , '[object Array]': 'array' 4560 | , '[object Date]': 'date' 4561 | , '[object Function]': 'function' 4562 | , '[object Number]': 'number' 4563 | , '[object RegExp]': 'regexp' 4564 | , '[object String]': 'string' 4565 | }; 4566 | 4567 | /** 4568 | * ### type(object) 4569 | * 4570 | * Better implementation of `typeof` detection that can 4571 | * be used cross-browser. Handles the inconsistencies of 4572 | * Array, `null`, and `undefined` detection. 4573 | * 4574 | * utils.type({}) // 'object' 4575 | * utils.type(null) // `null' 4576 | * utils.type(undefined) // `undefined` 4577 | * utils.type([]) // `array` 4578 | * 4579 | * @param {Mixed} object to detect type of 4580 | * @name type 4581 | * @api private 4582 | */ 4583 | 4584 | module.exports = function (obj) { 4585 | var str = Object.prototype.toString.call(obj); 4586 | if (natives[str]) return natives[str]; 4587 | if (obj === null) return 'null'; 4588 | if (obj === undefined) return 'undefined'; 4589 | if (obj === Object(obj)) return 'object'; 4590 | return typeof obj; 4591 | }; 4592 | 4593 | }); 4594 | 4595 | 4596 | require.alias("chaijs-assertion-error/index.js", "chai/deps/assertion-error/index.js"); 4597 | require.alias("chaijs-assertion-error/index.js", "chai/deps/assertion-error/index.js"); 4598 | require.alias("chaijs-assertion-error/index.js", "assertion-error/index.js"); 4599 | require.alias("chaijs-assertion-error/index.js", "chaijs-assertion-error/index.js"); 4600 | require.alias("chaijs-deep-eql/lib/eql.js", "chai/deps/deep-eql/lib/eql.js"); 4601 | require.alias("chaijs-deep-eql/lib/eql.js", "chai/deps/deep-eql/index.js"); 4602 | require.alias("chaijs-deep-eql/lib/eql.js", "deep-eql/index.js"); 4603 | require.alias("chaijs-type-detect/lib/type.js", "chaijs-deep-eql/deps/type-detect/lib/type.js"); 4604 | require.alias("chaijs-type-detect/lib/type.js", "chaijs-deep-eql/deps/type-detect/index.js"); 4605 | require.alias("chaijs-type-detect/lib/type.js", "chaijs-type-detect/index.js"); 4606 | require.alias("chaijs-deep-eql/lib/eql.js", "chaijs-deep-eql/index.js"); 4607 | require.alias("chai/index.js", "chai/index.js");if (typeof exports == "object") { 4608 | module.exports = require("chai"); 4609 | } else if (typeof define == "function" && define.amd) { 4610 | define(function(){ return require("chai"); }); 4611 | } else { 4612 | this["chai"] = require("chai"); 4613 | }})(); -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha Tests 5 | 6 | 7 | 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, #mocha li { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | 17 | #mocha ul { 18 | list-style: none; 19 | } 20 | 21 | #mocha h1, #mocha h2 { 22 | margin: 0; 23 | } 24 | 25 | #mocha h1 { 26 | margin-top: 15px; 27 | font-size: 1em; 28 | font-weight: 200; 29 | } 30 | 31 | #mocha h1 a { 32 | text-decoration: none; 33 | color: inherit; 34 | } 35 | 36 | #mocha h1 a:hover { 37 | text-decoration: underline; 38 | } 39 | 40 | #mocha .suite .suite h1 { 41 | margin-top: 0; 42 | font-size: .8em; 43 | } 44 | 45 | #mocha .hidden { 46 | display: none; 47 | } 48 | 49 | #mocha h2 { 50 | font-size: 12px; 51 | font-weight: normal; 52 | cursor: pointer; 53 | } 54 | 55 | #mocha .suite { 56 | margin-left: 15px; 57 | } 58 | 59 | #mocha .test { 60 | margin-left: 15px; 61 | overflow: hidden; 62 | } 63 | 64 | #mocha .test.pending:hover h2::after { 65 | content: '(pending)'; 66 | font-family: arial, sans-serif; 67 | } 68 | 69 | #mocha .test.pass.medium .duration { 70 | background: #C09853; 71 | } 72 | 73 | #mocha .test.pass.slow .duration { 74 | background: #B94A48; 75 | } 76 | 77 | #mocha .test.pass::before { 78 | content: '✓'; 79 | font-size: 12px; 80 | display: block; 81 | float: left; 82 | margin-right: 5px; 83 | color: #00d6b2; 84 | } 85 | 86 | #mocha .test.pass .duration { 87 | font-size: 9px; 88 | margin-left: 5px; 89 | padding: 2px 5px; 90 | color: white; 91 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 92 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 93 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -webkit-border-radius: 5px; 95 | -moz-border-radius: 5px; 96 | -ms-border-radius: 5px; 97 | -o-border-radius: 5px; 98 | border-radius: 5px; 99 | } 100 | 101 | #mocha .test.pass.fast .duration { 102 | display: none; 103 | } 104 | 105 | #mocha .test.pending { 106 | color: #0b97c4; 107 | } 108 | 109 | #mocha .test.pending::before { 110 | content: '◦'; 111 | color: #0b97c4; 112 | } 113 | 114 | #mocha .test.fail { 115 | color: #c00; 116 | } 117 | 118 | #mocha .test.fail pre { 119 | color: black; 120 | } 121 | 122 | #mocha .test.fail::before { 123 | content: '✖'; 124 | font-size: 12px; 125 | display: block; 126 | float: left; 127 | margin-right: 5px; 128 | color: #c00; 129 | } 130 | 131 | #mocha .test pre.error { 132 | color: #c00; 133 | max-height: 300px; 134 | overflow: auto; 135 | } 136 | 137 | #mocha .test pre { 138 | display: block; 139 | float: left; 140 | clear: left; 141 | font: 12px/1.5 monaco, monospace; 142 | margin: 5px; 143 | padding: 15px; 144 | border: 1px solid #eee; 145 | border-bottom-color: #ddd; 146 | -webkit-border-radius: 3px; 147 | -webkit-box-shadow: 0 1px 3px #eee; 148 | -moz-border-radius: 3px; 149 | -moz-box-shadow: 0 1px 3px #eee; 150 | border-radius: 3px; 151 | } 152 | 153 | #mocha .test h2 { 154 | position: relative; 155 | } 156 | 157 | #mocha .test a.replay { 158 | position: absolute; 159 | top: 3px; 160 | right: 0; 161 | text-decoration: none; 162 | vertical-align: middle; 163 | display: block; 164 | width: 15px; 165 | height: 15px; 166 | line-height: 15px; 167 | text-align: center; 168 | background: #eee; 169 | font-size: 15px; 170 | -moz-border-radius: 15px; 171 | border-radius: 15px; 172 | -webkit-transition: opacity 200ms; 173 | -moz-transition: opacity 200ms; 174 | transition: opacity 200ms; 175 | opacity: 0.3; 176 | color: #888; 177 | } 178 | 179 | #mocha .test:hover a.replay { 180 | opacity: 1; 181 | } 182 | 183 | #mocha-report.pass .test.fail { 184 | display: none; 185 | } 186 | 187 | #mocha-report.fail .test.pass { 188 | display: none; 189 | } 190 | 191 | #mocha-report.pending .test.pass, 192 | #mocha-report.pending .test.fail { 193 | display: none; 194 | } 195 | #mocha-report.pending .test.pass.pending { 196 | display: block; 197 | } 198 | 199 | #mocha-error { 200 | color: #c00; 201 | font-size: 1.5em; 202 | font-weight: 100; 203 | letter-spacing: 1px; 204 | } 205 | 206 | #mocha-stats { 207 | position: fixed; 208 | top: 15px; 209 | right: 10px; 210 | font-size: 12px; 211 | margin: 0; 212 | color: #888; 213 | z-index: 1; 214 | } 215 | 216 | #mocha-stats .progress { 217 | float: right; 218 | padding-top: 0; 219 | } 220 | 221 | #mocha-stats em { 222 | color: black; 223 | } 224 | 225 | #mocha-stats a { 226 | text-decoration: none; 227 | color: inherit; 228 | } 229 | 230 | #mocha-stats a:hover { 231 | border-bottom: 1px solid #eee; 232 | } 233 | 234 | #mocha-stats li { 235 | display: inline-block; 236 | margin: 0 5px; 237 | list-style: none; 238 | padding-top: 11px; 239 | } 240 | 241 | #mocha-stats canvas { 242 | width: 40px; 243 | height: 40px; 244 | } 245 | 246 | #mocha code .comment { color: #ddd } 247 | #mocha code .init { color: #2F6FAD } 248 | #mocha code .string { color: #5890AD } 249 | #mocha code .keyword { color: #8A6343 } 250 | #mocha code .number { color: #2F6FAD } 251 | 252 | @media screen and (max-device-width: 480px) { 253 | #mocha { 254 | margin: 60px 0px; 255 | } 256 | 257 | #mocha #stats { 258 | position: absolute; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /test/test.coffee.md: -------------------------------------------------------------------------------- 1 | setup 2 | 3 | CssToMatrix = window['css-to-matrix'] 4 | expect = chai.expect 5 | div = document.getElementById 'test' 6 | precision = 100000 7 | data = [ 8 | [1,2,3,4] 9 | [5,6,7,8] 10 | [9,10,11,12] 11 | [13,14,15,16] 12 | ] 13 | 14 | setup 15 | 16 | mocha.setup 'bdd' 17 | 18 | describe 'constructor', -> 19 | 20 | it 'adds data that is passed when intialized to its model', -> 21 | 22 | cssToMatrix = new CssToMatrix data 23 | actual = cssToMatrix.model.get 'matrix' 24 | 25 | expect(actual).to.deep.equal data 26 | 27 | describe 'matrix', -> 28 | 29 | it 'should add valid data to its instance model', -> 30 | 31 | cssToMatrix = new CssToMatrix 32 | cssToMatrix.matrix data 33 | actual = cssToMatrix.model.get 'matrix' 34 | 35 | expect(actual).to.deep.equal data 36 | 37 | it 'should throw an error when intialized with an invalid array', -> 38 | 39 | fn = -> new CssToMatrix 'bad' 40 | 41 | expect(fn).to.throw Error 42 | 43 | describe 'getMatrix', -> 44 | 45 | it 'should properly apply transformations', -> 46 | 47 | cssToMatrix = new CssToMatrix 48 | cssToMatrix.translate3d 10, 20, 30 49 | 50 | actual = cssToMatrix.getMatrix() 51 | expected = [ 52 | [1, 0, 0, 0] 53 | [0, 1, 0, 0] 54 | [0, 0, 1, 0] 55 | [10, 20, 30, 1] 56 | ] 57 | 58 | expect(actual).to.deep.equal expected 59 | 60 | describe 'getMatrixCSS', -> 61 | 62 | it 'should convert matricies to CSS strings', -> 63 | 64 | cssToMatrix = new CssToMatrix data 65 | css = cssToMatrix.getMatrixCSS() 66 | 67 | expect(css).to.equal 'matrix3d(1,5,9,13,2,6,10,14,3,7,11,15,4,8,12,16)' 68 | 69 | run 70 | 71 | mocha.run() -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var CssToMatrix, data, div, expect, precision; 3 | 4 | CssToMatrix = window['css-to-matrix']; 5 | 6 | expect = chai.expect; 7 | 8 | div = document.getElementById('test'); 9 | 10 | precision = 100000; 11 | 12 | data = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]; 13 | 14 | mocha.setup('bdd'); 15 | 16 | describe('constructor', function() { 17 | return it('adds data that is passed when intialized to its model', function() { 18 | var actual, cssToMatrix; 19 | cssToMatrix = new CssToMatrix(data); 20 | actual = cssToMatrix.model.get('matrix'); 21 | return expect(actual).to.deep.equal(data); 22 | }); 23 | }); 24 | 25 | describe('matrix', function() { 26 | it('should add valid data to its instance model', function() { 27 | var actual, cssToMatrix; 28 | cssToMatrix = new CssToMatrix; 29 | cssToMatrix.matrix(data); 30 | actual = cssToMatrix.model.get('matrix'); 31 | return expect(actual).to.deep.equal(data); 32 | }); 33 | return it('should throw an error when intialized with an invalid array', function() { 34 | var fn; 35 | fn = function() { 36 | return new CssToMatrix('bad'); 37 | }; 38 | return expect(fn).to["throw"](Error); 39 | }); 40 | }); 41 | 42 | describe('getMatrix', function() { 43 | return it('should properly apply transformations', function() { 44 | var actual, cssToMatrix, expected; 45 | cssToMatrix = new CssToMatrix; 46 | cssToMatrix.translate3d(10, 20, 30); 47 | actual = cssToMatrix.getMatrix(); 48 | expected = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [10, 20, 30, 1]]; 49 | return expect(actual).to.deep.equal(expected); 50 | }); 51 | }); 52 | 53 | describe('getMatrixCSS', function() { 54 | return it('should convert matricies to CSS strings', function() { 55 | var css, cssToMatrix; 56 | cssToMatrix = new CssToMatrix(data); 57 | css = cssToMatrix.getMatrixCSS(); 58 | return expect(css).to.equal('matrix3d(1,5,9,13,2,6,10,14,3,7,11,15,4,8,12,16)'); 59 | }); 60 | }); 61 | 62 | mocha.run(); 63 | 64 | }).call(this); 65 | --------------------------------------------------------------------------------