├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── README.md ├── SpecRunner.html ├── images ├── failing-tests.png └── passing-tests.png ├── lib ├── chai │ └── chai.js ├── jquery.js ├── mocha │ ├── mocha.css │ └── mocha.js ├── sinon │ └── sinon.js └── underscore.js ├── spec ├── getElementsByClassNameSpec.js ├── nthFibonacciSpec.js ├── powerSpec.js └── sumArraySpec.js └── src ├── getElementsByClassName.js ├── nthFibonacci.js ├── power.js └── sumArray.js /.eslintignore: -------------------------------------------------------------------------------- 1 | .eslint* 2 | **/node_modules 3 | **/bower_components 4 | **/lib 5 | **/vendor 6 | **/*.min.js 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * These rules enforce Hack Reactor's style guide. 3 | * Visit this repo for more information: 4 | * https://github.com/reactorcore/eslint-config-hackreactor 5 | */ 6 | 7 | module.exports = { 8 | env: { 9 | 'es6': true 10 | }, 11 | parserOptions: { 12 | ecmaFeatures: { 13 | 'jsx': true 14 | } 15 | }, 16 | rules: { 17 | /* Indentation */ 18 | 'no-mixed-spaces-and-tabs': 2, 19 | 'indent': [2, 2], 20 | /* Variable cames */ 21 | 'camelcase': 2, 22 | /* Language constructs */ 23 | 'curly': 2, 24 | 'eqeqeq': [2, 'smart'], 25 | 'func-style': [2, 'expression'], 26 | /* Semicolons */ 27 | 'semi': 2, 28 | 'no-extra-semi': 2, 29 | /* Padding & additional whitespace (perferred but optional) */ 30 | 'brace-style': [2, '1tbs', { 'allowSingleLine': true }], 31 | 'semi-spacing': 1, 32 | 'key-spacing': 1, 33 | 'block-spacing': 1, 34 | 'comma-spacing': 1, 35 | 'no-multi-spaces': 1, 36 | 'space-before-blocks': 1, 37 | 'keyword-spacing': [1, { 'before': true, 'after': true }], 38 | 'space-infix-ops': 1, 39 | /* Variable declaration */ 40 | 'one-var': [1, { 'uninitialized': 'always', 'initialized': 'never' }], 41 | /* Minuta */ 42 | 'comma-style': [2, 'last'], 43 | 'quotes': [1, 'single'] 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### node etc ### 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled Dirs (http://nodejs.org/api/addons.html) 22 | build/ 23 | dist/ 24 | 25 | # Dependency directorys 26 | # Deployed apps should consider commenting these lines out: 27 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 28 | node_modules/ 29 | bower_components/ 30 | 31 | # Floobits 32 | .floo 33 | .floobit 34 | .floo 35 | .flooignore 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hack Reactor: Recursion in JavaScript 2 | 3 | You're here to solidify your understanding of recursion -- a fundamental programming concept -- in JavaScript. 4 | 5 | *IMPORTANT*: Completion of this workshop is no guarantee of admission into the Hack Reactor immersive program, nor does it have any influence in the admissions process. 6 | 7 | ## Prerequisites 8 | 9 | This workshop is designed for folks in the midst of studying intermediate-level JavaScript who have a few months of foundational experience. You will be expected to understand the basics of the Document Object Model (DOM) as applied to HTML. 10 | 11 | ## Textbook 12 | 13 | No textbook is required for this workshop. All materials are included in this GitHub repo. 14 | 15 | ## Technical requirements 16 | 17 | Laptop, Google Chrome browser and a text editor. If you do not have a text editor, we recommend Visual Studio Code, Sublime Text, or Atom. 18 | 19 | ## Slides 20 | 21 | The lesson slides for this workshop can be found [here](https://docs.google.com/presentation/d/e/2PACX-1vR-oZyN4pYr3gbnPcrkUBtDrfnR16d6TnZGxhAI4Q52Fu_hm0_KAmEzf_HjEJNhCaPPln40XAriXTbu/pub?start=false&loop=false&delayms=60000). 22 | 23 | # How to use this repository 24 | 25 | ### Let's get started... 26 | 27 | Run the SpecRunner.html file in a browser. This document currently shows 4 failing tests. 28 | 29 | - The `spec` folder holds all the failing tests that are being displayed in SpecRunner.html. 30 | - The `src` folder holds the functions that are being called to run the tests. 31 | - Your task is to edit the files in `src` to complete the functions and get the tests to pass. 32 | - These files are just javascript files so you can use `console.log` to help debug and inspect these functions. 33 | 34 | ## Recursion Review 35 | 36 | Recursion is a technique for solving problems wherein a function makes calls to itself. By doing so, it can complete a small amount of the processing, and delegate the rest of the problem to the recursive calls. 37 | 38 | Consider the following function: 39 | 40 | ```js 41 | var eat = function(meal){ 42 | console.log('meal before bite:', meal); 43 | console.log('now eating', meal.pop()); 44 | if(meal.length){ 45 | eat(meal); 46 | } else { 47 | console.log('done with the meal!'); 48 | } 49 | } 50 | ``` 51 | 52 | Which produces this output: 53 | 54 | ```js 55 | eat(['soup', 'potatoes', 'fish']); 56 | // => meal before bite: ["soup", "potatoes", "fish"] 57 | // => now eating fish 58 | // => meal before bite: ["soup", "potatoes"] 59 | // => now eating potatoes 60 | // => meal before bite: ["soup"] 61 | // => now eating soup 62 | // => done with the meal! 63 | ``` 64 | 65 | You can use recursion on problems where smaller parts of the problem look the same as the larger problem as a whole. 66 | 67 | In this sprint, you'll be practicing writing recursive functions, building up to the reimplementation of a JavaScript browser method that involves recursion (getElementsByClassName). In so doing, don't use the things you're reimplementing, or any other built-in shortcuts that make these problems trivial. (You'll probably know if you're cheating, but feel free to ask us if you're not sure.) 68 | 69 | (Curious fact: many browsers don't have any of these functions in them, and people do need to reimplement them. When we reimplement new browser functionality in older browsers, it's called a "polyfill".) 70 | 71 | ## Exercises 72 | 73 | More detailed instructions and examples for each exercise can be found in the individual `.js` files in the `src` folder. 74 | 75 | ### 1: sumArray 76 | 77 | - [ ] Implement `sumArray` with your own code in `src/sumArray.js` 78 | 79 | ### 2: power 80 | 81 | - [ ] Implement `power` with your own code in `src/power.js` 82 | 83 | ### 3: nthFibonacci 84 | 85 | - [ ] Implement `nthFibonacci` with your own code in `src/nthFibonacci.js` 86 | 87 | ### 4: getElementsByClassName 88 | 89 | - [ ] Implement `getElementsByClassName` with your own code in `src/getElementsByClassName.js` 90 | - [ ] You should use `document.body`, `element.childNodes`, and `element.classList` 91 | - NOTE: You may also use methods from the [underscore](https://underscorejs.org) library for assitance, but are not required to do so. 92 | - You can view the MDN documentation for getElementsByClassName [here](https://developer.mozilla.org/en/docs/Web/API/Document/getElementsByClassName) 93 | 94 | #### Failing Tests Example 95 | 96 | ![failing tests](images/failing-tests.png) 97 | 98 | #### Passing Tests Example 99 | 100 | ![passing tests](images/passing-tests.png) 101 | 102 | ### Don't forget.. 103 | 104 | You should throroughly read all of code in front of you and aim to understand line-by-line what is happening. -------------------------------------------------------------------------------- /SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha Spec Runner 5 | 6 | 7 | 8 | 9 | 10 | 11 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /images/failing-tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackreactor/recursion_in_javascript/a8d1f0f1bc9fdb77416aff244d4f38147945f546/images/failing-tests.png -------------------------------------------------------------------------------- /images/passing-tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackreactor/recursion_in_javascript/a8d1f0f1bc9fdb77416aff244d4f38147945f546/images/passing-tests.png -------------------------------------------------------------------------------- /lib/chai/chai.js: -------------------------------------------------------------------------------- 1 | !function (name, context, definition) { 2 | if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') { 3 | module.exports = definition(); 4 | } else if (typeof define === 'function' && typeof define.amd === 'object') { 5 | define(function () { 6 | return definition(); 7 | }); 8 | } else { 9 | context[name] = definition(); 10 | } 11 | }('chai', this, function () { 12 | 13 | function require(p) { 14 | var path = require.resolve(p) 15 | , mod = require.modules[path]; 16 | if (!mod) throw new Error('failed to require "' + p + '"'); 17 | if (!mod.exports) { 18 | mod.exports = {}; 19 | mod.call(mod.exports, mod, mod.exports, require.relative(path)); 20 | } 21 | return mod.exports; 22 | } 23 | 24 | require.modules = {}; 25 | 26 | require.resolve = function (path) { 27 | var orig = path 28 | , reg = path + '.js' 29 | , index = path + '/index.js'; 30 | return require.modules[reg] && reg 31 | || require.modules[index] && index 32 | || orig; 33 | }; 34 | 35 | require.register = function (path, fn) { 36 | require.modules[path] = fn; 37 | }; 38 | 39 | require.relative = function (parent) { 40 | return function(p){ 41 | if ('.' != p.charAt(0)) return require(p); 42 | 43 | var path = parent.split('/') 44 | , segs = p.split('/'); 45 | path.pop(); 46 | 47 | for (var i = 0; i < segs.length; i++) { 48 | var seg = segs[i]; 49 | if ('..' == seg) path.pop(); 50 | else if ('.' != seg) path.push(seg); 51 | } 52 | 53 | return require(path.join('/')); 54 | }; 55 | }; 56 | 57 | require.alias = function (from, to) { 58 | var fn = require.modules[from]; 59 | require.modules[to] = fn; 60 | }; 61 | 62 | 63 | require.register("chai.js", function(module, exports, require){ 64 | /*! 65 | * chai 66 | * Copyright(c) 2011-2013 Jake Luer 67 | * MIT Licensed 68 | */ 69 | 70 | var used = [] 71 | , exports = module.exports = {}; 72 | 73 | /*! 74 | * Chai version 75 | */ 76 | 77 | exports.version = '1.5.0'; 78 | 79 | /*! 80 | * Primary `Assertion` prototype 81 | */ 82 | 83 | exports.Assertion = require('./chai/assertion'); 84 | 85 | /*! 86 | * Assertion Error 87 | */ 88 | 89 | exports.AssertionError = require('./chai/error'); 90 | 91 | /*! 92 | * Utils for plugins (not exported) 93 | */ 94 | 95 | var util = require('./chai/utils'); 96 | 97 | /** 98 | * # .use(function) 99 | * 100 | * Provides a way to extend the internals of Chai 101 | * 102 | * @param {Function} 103 | * @returns {this} for chaining 104 | * @api public 105 | */ 106 | 107 | exports.use = function (fn) { 108 | if (!~used.indexOf(fn)) { 109 | fn(this, util); 110 | used.push(fn); 111 | } 112 | 113 | return this; 114 | }; 115 | 116 | /*! 117 | * Core Assertions 118 | */ 119 | 120 | var core = require('./chai/core/assertions'); 121 | exports.use(core); 122 | 123 | /*! 124 | * Expect interface 125 | */ 126 | 127 | var expect = require('./chai/interface/expect'); 128 | exports.use(expect); 129 | 130 | /*! 131 | * Should interface 132 | */ 133 | 134 | var should = require('./chai/interface/should'); 135 | exports.use(should); 136 | 137 | /*! 138 | * Assert interface 139 | */ 140 | 141 | var assert = require('./chai/interface/assert'); 142 | exports.use(assert); 143 | 144 | }); // module: chai.js 145 | 146 | require.register("chai/assertion.js", function(module, exports, require){ 147 | /*! 148 | * chai 149 | * http://chaijs.com 150 | * Copyright(c) 2011-2013 Jake Luer 151 | * MIT Licensed 152 | */ 153 | 154 | /*! 155 | * Module dependencies. 156 | */ 157 | 158 | var AssertionError = require('./error') 159 | , util = require('./utils') 160 | , flag = util.flag; 161 | 162 | /*! 163 | * Module export. 164 | */ 165 | 166 | module.exports = Assertion; 167 | 168 | 169 | /*! 170 | * Assertion Constructor 171 | * 172 | * Creates object for chaining. 173 | * 174 | * @api private 175 | */ 176 | 177 | function Assertion (obj, msg, stack) { 178 | flag(this, 'ssfi', stack || arguments.callee); 179 | flag(this, 'object', obj); 180 | flag(this, 'message', msg); 181 | } 182 | 183 | /*! 184 | * ### Assertion.includeStack 185 | * 186 | * User configurable property, influences whether stack trace 187 | * is included in Assertion error message. Default of false 188 | * suppresses stack trace in the error message 189 | * 190 | * Assertion.includeStack = true; // enable stack on error 191 | * 192 | * @api public 193 | */ 194 | 195 | Assertion.includeStack = false; 196 | 197 | /*! 198 | * ### Assertion.showDiff 199 | * 200 | * User configurable property, influences whether or not 201 | * the `showDiff` flag should be included in the thrown 202 | * AssertionErrors. `false` will always be `false`; `true` 203 | * will be true when the assertion has requested a diff 204 | * be shown. 205 | * 206 | * @api public 207 | */ 208 | 209 | Assertion.showDiff = true; 210 | 211 | Assertion.addProperty = function (name, fn) { 212 | util.addProperty(this.prototype, name, fn); 213 | }; 214 | 215 | Assertion.addMethod = function (name, fn) { 216 | util.addMethod(this.prototype, name, fn); 217 | }; 218 | 219 | Assertion.addChainableMethod = function (name, fn, chainingBehavior) { 220 | util.addChainableMethod(this.prototype, name, fn, chainingBehavior); 221 | }; 222 | 223 | Assertion.overwriteProperty = function (name, fn) { 224 | util.overwriteProperty(this.prototype, name, fn); 225 | }; 226 | 227 | Assertion.overwriteMethod = function (name, fn) { 228 | util.overwriteMethod(this.prototype, name, fn); 229 | }; 230 | 231 | /*! 232 | * ### .assert(expression, message, negateMessage, expected, actual) 233 | * 234 | * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. 235 | * 236 | * @name assert 237 | * @param {Philosophical} expression to be tested 238 | * @param {String} message to display if fails 239 | * @param {String} negatedMessage to display if negated expression fails 240 | * @param {Mixed} expected value (remember to check for negation) 241 | * @param {Mixed} actual (optional) will default to `this.obj` 242 | * @api private 243 | */ 244 | 245 | Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) { 246 | var ok = util.test(this, arguments); 247 | if (true !== showDiff) showDiff = false; 248 | if (true !== Assertion.showDiff) showDiff = false; 249 | 250 | if (!ok) { 251 | var msg = util.getMessage(this, arguments) 252 | , actual = util.getActual(this, arguments); 253 | throw new AssertionError({ 254 | message: msg 255 | , actual: actual 256 | , expected: expected 257 | , stackStartFunction: (Assertion.includeStack) ? this.assert : flag(this, 'ssfi') 258 | , showDiff: showDiff 259 | }); 260 | } 261 | }; 262 | 263 | /*! 264 | * ### ._obj 265 | * 266 | * Quick reference to stored `actual` value for plugin developers. 267 | * 268 | * @api private 269 | */ 270 | 271 | Object.defineProperty(Assertion.prototype, '_obj', 272 | { get: function () { 273 | return flag(this, 'object'); 274 | } 275 | , set: function (val) { 276 | flag(this, 'object', val); 277 | } 278 | }); 279 | 280 | }); // module: chai/assertion.js 281 | 282 | require.register("chai/core/assertions.js", function(module, exports, require){ 283 | /*! 284 | * chai 285 | * http://chaijs.com 286 | * Copyright(c) 2011-2013 Jake Luer 287 | * MIT Licensed 288 | */ 289 | 290 | module.exports = function (chai, _) { 291 | var Assertion = chai.Assertion 292 | , toString = Object.prototype.toString 293 | , flag = _.flag; 294 | 295 | /** 296 | * ### Language Chains 297 | * 298 | * The following are provide as chainable getters to 299 | * improve the readability of your assertions. They 300 | * do not provide an testing capability unless they 301 | * have been overwritten by a plugin. 302 | * 303 | * **Chains** 304 | * 305 | * - to 306 | * - be 307 | * - been 308 | * - is 309 | * - that 310 | * - and 311 | * - have 312 | * - with 313 | * - at 314 | * - of 315 | * 316 | * @name language chains 317 | * @api public 318 | */ 319 | 320 | [ 'to', 'be', 'been' 321 | , 'is', 'and', 'have' 322 | , 'with', 'that', 'at' 323 | , 'of' ].forEach(function (chain) { 324 | Assertion.addProperty(chain, function () { 325 | return this; 326 | }); 327 | }); 328 | 329 | /** 330 | * ### .not 331 | * 332 | * Negates any of assertions following in the chain. 333 | * 334 | * expect(foo).to.not.equal('bar'); 335 | * expect(goodFn).to.not.throw(Error); 336 | * expect({ foo: 'baz' }).to.have.property('foo') 337 | * .and.not.equal('bar'); 338 | * 339 | * @name not 340 | * @api public 341 | */ 342 | 343 | Assertion.addProperty('not', function () { 344 | flag(this, 'negate', true); 345 | }); 346 | 347 | /** 348 | * ### .deep 349 | * 350 | * Sets the `deep` flag, later used by the `equal` and 351 | * `property` assertions. 352 | * 353 | * expect(foo).to.deep.equal({ bar: 'baz' }); 354 | * expect({ foo: { bar: { baz: 'quux' } } }) 355 | * .to.have.deep.property('foo.bar.baz', 'quux'); 356 | * 357 | * @name deep 358 | * @api public 359 | */ 360 | 361 | Assertion.addProperty('deep', function () { 362 | flag(this, 'deep', true); 363 | }); 364 | 365 | /** 366 | * ### .a(type) 367 | * 368 | * The `a` and `an` assertions are aliases that can be 369 | * used either as language chains or to assert a value's 370 | * type. 371 | * 372 | * // typeof 373 | * expect('test').to.be.a('string'); 374 | * expect({ foo: 'bar' }).to.be.an('object'); 375 | * expect(null).to.be.a('null'); 376 | * expect(undefined).to.be.an('undefined'); 377 | * 378 | * // language chain 379 | * expect(foo).to.be.an.instanceof(Foo); 380 | * 381 | * @name a 382 | * @alias an 383 | * @param {String} type 384 | * @param {String} message _optional_ 385 | * @api public 386 | */ 387 | 388 | function an (type, msg) { 389 | if (msg) flag(this, 'message', msg); 390 | type = type.toLowerCase(); 391 | var obj = flag(this, 'object') 392 | , article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a '; 393 | 394 | this.assert( 395 | type === _.type(obj) 396 | , 'expected #{this} to be ' + article + type 397 | , 'expected #{this} not to be ' + article + type 398 | ); 399 | } 400 | 401 | Assertion.addChainableMethod('an', an); 402 | Assertion.addChainableMethod('a', an); 403 | 404 | /** 405 | * ### .include(value) 406 | * 407 | * The `include` and `contain` assertions can be used as either property 408 | * based language chains or as methods to assert the inclusion of an object 409 | * in an array or a substring in a string. When used as language chains, 410 | * they toggle the `contain` flag for the `keys` assertion. 411 | * 412 | * expect([1,2,3]).to.include(2); 413 | * expect('foobar').to.contain('foo'); 414 | * expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); 415 | * 416 | * @name include 417 | * @alias contain 418 | * @param {Object|String|Number} obj 419 | * @param {String} message _optional_ 420 | * @api public 421 | */ 422 | 423 | function includeChainingBehavior () { 424 | flag(this, 'contains', true); 425 | } 426 | 427 | function include (val, msg) { 428 | if (msg) flag(this, 'message', msg); 429 | var obj = flag(this, 'object') 430 | this.assert( 431 | ~obj.indexOf(val) 432 | , 'expected #{this} to include ' + _.inspect(val) 433 | , 'expected #{this} to not include ' + _.inspect(val)); 434 | } 435 | 436 | Assertion.addChainableMethod('include', include, includeChainingBehavior); 437 | Assertion.addChainableMethod('contain', include, includeChainingBehavior); 438 | 439 | /** 440 | * ### .ok 441 | * 442 | * Asserts that the target is truthy. 443 | * 444 | * expect('everthing').to.be.ok; 445 | * expect(1).to.be.ok; 446 | * expect(false).to.not.be.ok; 447 | * expect(undefined).to.not.be.ok; 448 | * expect(null).to.not.be.ok; 449 | * 450 | * @name ok 451 | * @api public 452 | */ 453 | 454 | Assertion.addProperty('ok', function () { 455 | this.assert( 456 | flag(this, 'object') 457 | , 'expected #{this} to be truthy' 458 | , 'expected #{this} to be falsy'); 459 | }); 460 | 461 | /** 462 | * ### .true 463 | * 464 | * Asserts that the target is `true`. 465 | * 466 | * expect(true).to.be.true; 467 | * expect(1).to.not.be.true; 468 | * 469 | * @name true 470 | * @api public 471 | */ 472 | 473 | Assertion.addProperty('true', function () { 474 | this.assert( 475 | true === flag(this, 'object') 476 | , 'expected #{this} to be true' 477 | , 'expected #{this} to be false' 478 | , this.negate ? false : true 479 | ); 480 | }); 481 | 482 | /** 483 | * ### .false 484 | * 485 | * Asserts that the target is `false`. 486 | * 487 | * expect(false).to.be.false; 488 | * expect(0).to.not.be.false; 489 | * 490 | * @name false 491 | * @api public 492 | */ 493 | 494 | Assertion.addProperty('false', function () { 495 | this.assert( 496 | false === flag(this, 'object') 497 | , 'expected #{this} to be false' 498 | , 'expected #{this} to be true' 499 | , this.negate ? true : false 500 | ); 501 | }); 502 | 503 | /** 504 | * ### .null 505 | * 506 | * Asserts that the target is `null`. 507 | * 508 | * expect(null).to.be.null; 509 | * expect(undefined).not.to.be.null; 510 | * 511 | * @name null 512 | * @api public 513 | */ 514 | 515 | Assertion.addProperty('null', function () { 516 | this.assert( 517 | null === flag(this, 'object') 518 | , 'expected #{this} to be null' 519 | , 'expected #{this} not to be null' 520 | ); 521 | }); 522 | 523 | /** 524 | * ### .undefined 525 | * 526 | * Asserts that the target is `undefined`. 527 | * 528 | * expect(undefined).to.be.undefined; 529 | * expect(null).to.not.be.undefined; 530 | * 531 | * @name undefined 532 | * @api public 533 | */ 534 | 535 | Assertion.addProperty('undefined', function () { 536 | this.assert( 537 | undefined === flag(this, 'object') 538 | , 'expected #{this} to be undefined' 539 | , 'expected #{this} not to be undefined' 540 | ); 541 | }); 542 | 543 | /** 544 | * ### .exist 545 | * 546 | * Asserts that the target is neither `null` nor `undefined`. 547 | * 548 | * var foo = 'hi' 549 | * , bar = null 550 | * , baz; 551 | * 552 | * expect(foo).to.exist; 553 | * expect(bar).to.not.exist; 554 | * expect(baz).to.not.exist; 555 | * 556 | * @name exist 557 | * @api public 558 | */ 559 | 560 | Assertion.addProperty('exist', function () { 561 | this.assert( 562 | null != flag(this, 'object') 563 | , 'expected #{this} to exist' 564 | , 'expected #{this} to not exist' 565 | ); 566 | }); 567 | 568 | 569 | /** 570 | * ### .empty 571 | * 572 | * Asserts that the target's length is `0`. For arrays, it checks 573 | * the `length` property. For objects, it gets the count of 574 | * enumerable keys. 575 | * 576 | * expect([]).to.be.empty; 577 | * expect('').to.be.empty; 578 | * expect({}).to.be.empty; 579 | * 580 | * @name empty 581 | * @api public 582 | */ 583 | 584 | Assertion.addProperty('empty', function () { 585 | var obj = flag(this, 'object') 586 | , expected = obj; 587 | 588 | if (Array.isArray(obj) || 'string' === typeof object) { 589 | expected = obj.length; 590 | } else if (typeof obj === 'object') { 591 | expected = Object.keys(obj).length; 592 | } 593 | 594 | this.assert( 595 | !expected 596 | , 'expected #{this} to be empty' 597 | , 'expected #{this} not to be empty' 598 | ); 599 | }); 600 | 601 | /** 602 | * ### .arguments 603 | * 604 | * Asserts that the target is an arguments object. 605 | * 606 | * function test () { 607 | * expect(arguments).to.be.arguments; 608 | * } 609 | * 610 | * @name arguments 611 | * @alias Arguments 612 | * @api public 613 | */ 614 | 615 | function checkArguments () { 616 | var obj = flag(this, 'object') 617 | , type = Object.prototype.toString.call(obj); 618 | this.assert( 619 | '[object Arguments]' === type 620 | , 'expected #{this} to be arguments but got ' + type 621 | , 'expected #{this} to not be arguments' 622 | ); 623 | } 624 | 625 | Assertion.addProperty('arguments', checkArguments); 626 | Assertion.addProperty('Arguments', checkArguments); 627 | 628 | /** 629 | * ### .equal(value) 630 | * 631 | * Asserts that the target is strictly equal (`===`) to `value`. 632 | * Alternately, if the `deep` flag is set, asserts that 633 | * the target is deeply equal to `value`. 634 | * 635 | * expect('hello').to.equal('hello'); 636 | * expect(42).to.equal(42); 637 | * expect(1).to.not.equal(true); 638 | * expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' }); 639 | * expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); 640 | * 641 | * @name equal 642 | * @alias equals 643 | * @alias eq 644 | * @alias deep.equal 645 | * @param {Mixed} value 646 | * @param {String} message _optional_ 647 | * @api public 648 | */ 649 | 650 | function assertEqual (val, msg) { 651 | if (msg) flag(this, 'message', msg); 652 | var obj = flag(this, 'object'); 653 | if (flag(this, 'deep')) { 654 | return this.eql(val); 655 | } else { 656 | this.assert( 657 | val === obj 658 | , 'expected #{this} to equal #{exp}' 659 | , 'expected #{this} to not equal #{exp}' 660 | , val 661 | , this._obj 662 | , true 663 | ); 664 | } 665 | } 666 | 667 | Assertion.addMethod('equal', assertEqual); 668 | Assertion.addMethod('equals', assertEqual); 669 | Assertion.addMethod('eq', assertEqual); 670 | 671 | /** 672 | * ### .eql(value) 673 | * 674 | * Asserts that the target is deeply equal to `value`. 675 | * 676 | * expect({ foo: 'bar' }).to.eql({ foo: 'bar' }); 677 | * expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]); 678 | * 679 | * @name eql 680 | * @alias eqls 681 | * @param {Mixed} value 682 | * @param {String} message _optional_ 683 | * @api public 684 | */ 685 | 686 | function assertEql(obj, msg) { 687 | if (msg) flag(this, 'message', msg); 688 | this.assert( 689 | _.eql(obj, flag(this, 'object')) 690 | , 'expected #{this} to deeply equal #{exp}' 691 | , 'expected #{this} to not deeply equal #{exp}' 692 | , obj 693 | , this._obj 694 | , true 695 | ); 696 | } 697 | 698 | Assertion.addMethod('eql', assertEql); 699 | Assertion.addMethod('eqls', assertEql); 700 | 701 | /** 702 | * ### .above(value) 703 | * 704 | * Asserts that the target is greater than `value`. 705 | * 706 | * expect(10).to.be.above(5); 707 | * 708 | * Can also be used in conjunction with `length` to 709 | * assert a minimum length. The benefit being a 710 | * more informative error message than if the length 711 | * was supplied directly. 712 | * 713 | * expect('foo').to.have.length.above(2); 714 | * expect([ 1, 2, 3 ]).to.have.length.above(2); 715 | * 716 | * @name above 717 | * @alias gt 718 | * @alias greaterThan 719 | * @param {Number} value 720 | * @param {String} message _optional_ 721 | * @api public 722 | */ 723 | 724 | function assertAbove (n, msg) { 725 | if (msg) flag(this, 'message', msg); 726 | var obj = flag(this, 'object'); 727 | if (flag(this, 'doLength')) { 728 | new Assertion(obj, msg).to.have.property('length'); 729 | var len = obj.length; 730 | this.assert( 731 | len > n 732 | , 'expected #{this} to have a length above #{exp} but got #{act}' 733 | , 'expected #{this} to not have a length above #{exp}' 734 | , n 735 | , len 736 | ); 737 | } else { 738 | this.assert( 739 | obj > n 740 | , 'expected #{this} to be above ' + n 741 | , 'expected #{this} to be at most ' + n 742 | ); 743 | } 744 | } 745 | 746 | Assertion.addMethod('above', assertAbove); 747 | Assertion.addMethod('gt', assertAbove); 748 | Assertion.addMethod('greaterThan', assertAbove); 749 | 750 | /** 751 | * ### .least(value) 752 | * 753 | * Asserts that the target is greater than or equal to `value`. 754 | * 755 | * expect(10).to.be.at.least(10); 756 | * 757 | * Can also be used in conjunction with `length` to 758 | * assert a minimum length. The benefit being a 759 | * more informative error message than if the length 760 | * was supplied directly. 761 | * 762 | * expect('foo').to.have.length.of.at.least(2); 763 | * expect([ 1, 2, 3 ]).to.have.length.of.at.least(3); 764 | * 765 | * @name least 766 | * @alias gte 767 | * @param {Number} value 768 | * @param {String} message _optional_ 769 | * @api public 770 | */ 771 | 772 | function assertLeast (n, msg) { 773 | if (msg) flag(this, 'message', msg); 774 | var obj = flag(this, 'object'); 775 | if (flag(this, 'doLength')) { 776 | new Assertion(obj, msg).to.have.property('length'); 777 | var len = obj.length; 778 | this.assert( 779 | len >= n 780 | , 'expected #{this} to have a length at least #{exp} but got #{act}' 781 | , 'expected #{this} to have a length below #{exp}' 782 | , n 783 | , len 784 | ); 785 | } else { 786 | this.assert( 787 | obj >= n 788 | , 'expected #{this} to be at least ' + n 789 | , 'expected #{this} to be below ' + n 790 | ); 791 | } 792 | } 793 | 794 | Assertion.addMethod('least', assertLeast); 795 | Assertion.addMethod('gte', assertLeast); 796 | 797 | /** 798 | * ### .below(value) 799 | * 800 | * Asserts that the target is less than `value`. 801 | * 802 | * expect(5).to.be.below(10); 803 | * 804 | * Can also be used in conjunction with `length` to 805 | * assert a maximum length. The benefit being a 806 | * more informative error message than if the length 807 | * was supplied directly. 808 | * 809 | * expect('foo').to.have.length.below(4); 810 | * expect([ 1, 2, 3 ]).to.have.length.below(4); 811 | * 812 | * @name below 813 | * @alias lt 814 | * @alias lessThan 815 | * @param {Number} value 816 | * @param {String} message _optional_ 817 | * @api public 818 | */ 819 | 820 | function assertBelow (n, msg) { 821 | if (msg) flag(this, 'message', msg); 822 | var obj = flag(this, 'object'); 823 | if (flag(this, 'doLength')) { 824 | new Assertion(obj, msg).to.have.property('length'); 825 | var len = obj.length; 826 | this.assert( 827 | len < n 828 | , 'expected #{this} to have a length below #{exp} but got #{act}' 829 | , 'expected #{this} to not have a length below #{exp}' 830 | , n 831 | , len 832 | ); 833 | } else { 834 | this.assert( 835 | obj < n 836 | , 'expected #{this} to be below ' + n 837 | , 'expected #{this} to be at least ' + n 838 | ); 839 | } 840 | } 841 | 842 | Assertion.addMethod('below', assertBelow); 843 | Assertion.addMethod('lt', assertBelow); 844 | Assertion.addMethod('lessThan', assertBelow); 845 | 846 | /** 847 | * ### .most(value) 848 | * 849 | * Asserts that the target is less than or equal to `value`. 850 | * 851 | * expect(5).to.be.at.most(5); 852 | * 853 | * Can also be used in conjunction with `length` to 854 | * assert a maximum length. The benefit being a 855 | * more informative error message than if the length 856 | * was supplied directly. 857 | * 858 | * expect('foo').to.have.length.of.at.most(4); 859 | * expect([ 1, 2, 3 ]).to.have.length.of.at.most(3); 860 | * 861 | * @name most 862 | * @alias lte 863 | * @param {Number} value 864 | * @param {String} message _optional_ 865 | * @api public 866 | */ 867 | 868 | function assertMost (n, msg) { 869 | if (msg) flag(this, 'message', msg); 870 | var obj = flag(this, 'object'); 871 | if (flag(this, 'doLength')) { 872 | new Assertion(obj, msg).to.have.property('length'); 873 | var len = obj.length; 874 | this.assert( 875 | len <= n 876 | , 'expected #{this} to have a length at most #{exp} but got #{act}' 877 | , 'expected #{this} to have a length above #{exp}' 878 | , n 879 | , len 880 | ); 881 | } else { 882 | this.assert( 883 | obj <= n 884 | , 'expected #{this} to be at most ' + n 885 | , 'expected #{this} to be above ' + n 886 | ); 887 | } 888 | } 889 | 890 | Assertion.addMethod('most', assertMost); 891 | Assertion.addMethod('lte', assertMost); 892 | 893 | /** 894 | * ### .within(start, finish) 895 | * 896 | * Asserts that the target is within a range. 897 | * 898 | * expect(7).to.be.within(5,10); 899 | * 900 | * Can also be used in conjunction with `length` to 901 | * assert a length range. The benefit being a 902 | * more informative error message than if the length 903 | * was supplied directly. 904 | * 905 | * expect('foo').to.have.length.within(2,4); 906 | * expect([ 1, 2, 3 ]).to.have.length.within(2,4); 907 | * 908 | * @name within 909 | * @param {Number} start lowerbound inclusive 910 | * @param {Number} finish upperbound inclusive 911 | * @param {String} message _optional_ 912 | * @api public 913 | */ 914 | 915 | Assertion.addMethod('within', function (start, finish, msg) { 916 | if (msg) flag(this, 'message', msg); 917 | var obj = flag(this, 'object') 918 | , range = start + '..' + finish; 919 | if (flag(this, 'doLength')) { 920 | new Assertion(obj, msg).to.have.property('length'); 921 | var len = obj.length; 922 | this.assert( 923 | len >= start && len <= finish 924 | , 'expected #{this} to have a length within ' + range 925 | , 'expected #{this} to not have a length within ' + range 926 | ); 927 | } else { 928 | this.assert( 929 | obj >= start && obj <= finish 930 | , 'expected #{this} to be within ' + range 931 | , 'expected #{this} to not be within ' + range 932 | ); 933 | } 934 | }); 935 | 936 | /** 937 | * ### .instanceof(constructor) 938 | * 939 | * Asserts that the target is an instance of `constructor`. 940 | * 941 | * var Tea = function (name) { this.name = name; } 942 | * , Chai = new Tea('chai'); 943 | * 944 | * expect(Chai).to.be.an.instanceof(Tea); 945 | * expect([ 1, 2, 3 ]).to.be.instanceof(Array); 946 | * 947 | * @name instanceof 948 | * @param {Constructor} constructor 949 | * @param {String} message _optional_ 950 | * @alias instanceOf 951 | * @api public 952 | */ 953 | 954 | function assertInstanceOf (constructor, msg) { 955 | if (msg) flag(this, 'message', msg); 956 | var name = _.getName(constructor); 957 | this.assert( 958 | flag(this, 'object') instanceof constructor 959 | , 'expected #{this} to be an instance of ' + name 960 | , 'expected #{this} to not be an instance of ' + name 961 | ); 962 | }; 963 | 964 | Assertion.addMethod('instanceof', assertInstanceOf); 965 | Assertion.addMethod('instanceOf', assertInstanceOf); 966 | 967 | /** 968 | * ### .property(name, [value]) 969 | * 970 | * Asserts that the target has a property `name`, optionally asserting that 971 | * the value of that property is strictly equal to `value`. 972 | * If the `deep` flag is set, you can use dot- and bracket-notation for deep 973 | * references into objects and arrays. 974 | * 975 | * // simple referencing 976 | * var obj = { foo: 'bar' }; 977 | * expect(obj).to.have.property('foo'); 978 | * expect(obj).to.have.property('foo', 'bar'); 979 | * 980 | * // deep referencing 981 | * var deepObj = { 982 | * green: { tea: 'matcha' } 983 | * , teas: [ 'chai', 'matcha', { tea: 'konacha' } ] 984 | * }; 985 | 986 | * expect(deepObj).to.have.deep.property('green.tea', 'matcha'); 987 | * expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); 988 | * expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); 989 | * 990 | * You can also use an array as the starting point of a `deep.property` 991 | * assertion, or traverse nested arrays. 992 | * 993 | * var arr = [ 994 | * [ 'chai', 'matcha', 'konacha' ] 995 | * , [ { tea: 'chai' } 996 | * , { tea: 'matcha' } 997 | * , { tea: 'konacha' } ] 998 | * ]; 999 | * 1000 | * expect(arr).to.have.deep.property('[0][1]', 'matcha'); 1001 | * expect(arr).to.have.deep.property('[1][2].tea', 'konacha'); 1002 | * 1003 | * Furthermore, `property` changes the subject of the assertion 1004 | * to be the value of that property from the original object. This 1005 | * permits for further chainable assertions on that property. 1006 | * 1007 | * expect(obj).to.have.property('foo') 1008 | * .that.is.a('string'); 1009 | * expect(deepObj).to.have.property('green') 1010 | * .that.is.an('object') 1011 | * .that.deep.equals({ tea: 'matcha' }); 1012 | * expect(deepObj).to.have.property('teas') 1013 | * .that.is.an('array') 1014 | * .with.deep.property('[2]') 1015 | * .that.deep.equals({ tea: 'konacha' }); 1016 | * 1017 | * @name property 1018 | * @alias deep.property 1019 | * @param {String} name 1020 | * @param {Mixed} value (optional) 1021 | * @param {String} message _optional_ 1022 | * @returns value of property for chaining 1023 | * @api public 1024 | */ 1025 | 1026 | Assertion.addMethod('property', function (name, val, msg) { 1027 | if (msg) flag(this, 'message', msg); 1028 | 1029 | var descriptor = flag(this, 'deep') ? 'deep property ' : 'property ' 1030 | , negate = flag(this, 'negate') 1031 | , obj = flag(this, 'object') 1032 | , value = flag(this, 'deep') 1033 | ? _.getPathValue(name, obj) 1034 | : obj[name]; 1035 | 1036 | if (negate && undefined !== val) { 1037 | if (undefined === value) { 1038 | msg = (msg != null) ? msg + ': ' : ''; 1039 | throw new Error(msg + _.inspect(obj) + ' has no ' + descriptor + _.inspect(name)); 1040 | } 1041 | } else { 1042 | this.assert( 1043 | undefined !== value 1044 | , 'expected #{this} to have a ' + descriptor + _.inspect(name) 1045 | , 'expected #{this} to not have ' + descriptor + _.inspect(name)); 1046 | } 1047 | 1048 | if (undefined !== val) { 1049 | this.assert( 1050 | val === value 1051 | , 'expected #{this} to have a ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' 1052 | , 'expected #{this} to not have a ' + descriptor + _.inspect(name) + ' of #{act}' 1053 | , val 1054 | , value 1055 | ); 1056 | } 1057 | 1058 | flag(this, 'object', value); 1059 | }); 1060 | 1061 | 1062 | /** 1063 | * ### .ownProperty(name) 1064 | * 1065 | * Asserts that the target has an own property `name`. 1066 | * 1067 | * expect('test').to.have.ownProperty('length'); 1068 | * 1069 | * @name ownProperty 1070 | * @alias haveOwnProperty 1071 | * @param {String} name 1072 | * @param {String} message _optional_ 1073 | * @api public 1074 | */ 1075 | 1076 | function assertOwnProperty (name, msg) { 1077 | if (msg) flag(this, 'message', msg); 1078 | var obj = flag(this, 'object'); 1079 | this.assert( 1080 | obj.hasOwnProperty(name) 1081 | , 'expected #{this} to have own property ' + _.inspect(name) 1082 | , 'expected #{this} to not have own property ' + _.inspect(name) 1083 | ); 1084 | } 1085 | 1086 | Assertion.addMethod('ownProperty', assertOwnProperty); 1087 | Assertion.addMethod('haveOwnProperty', assertOwnProperty); 1088 | 1089 | /** 1090 | * ### .length(value) 1091 | * 1092 | * Asserts that the target's `length` property has 1093 | * the expected value. 1094 | * 1095 | * expect([ 1, 2, 3]).to.have.length(3); 1096 | * expect('foobar').to.have.length(6); 1097 | * 1098 | * Can also be used as a chain precursor to a value 1099 | * comparison for the length property. 1100 | * 1101 | * expect('foo').to.have.length.above(2); 1102 | * expect([ 1, 2, 3 ]).to.have.length.above(2); 1103 | * expect('foo').to.have.length.below(4); 1104 | * expect([ 1, 2, 3 ]).to.have.length.below(4); 1105 | * expect('foo').to.have.length.within(2,4); 1106 | * expect([ 1, 2, 3 ]).to.have.length.within(2,4); 1107 | * 1108 | * @name length 1109 | * @alias lengthOf 1110 | * @param {Number} length 1111 | * @param {String} message _optional_ 1112 | * @api public 1113 | */ 1114 | 1115 | function assertLengthChain () { 1116 | flag(this, 'doLength', true); 1117 | } 1118 | 1119 | function assertLength (n, msg) { 1120 | if (msg) flag(this, 'message', msg); 1121 | var obj = flag(this, 'object'); 1122 | new Assertion(obj, msg).to.have.property('length'); 1123 | var len = obj.length; 1124 | 1125 | this.assert( 1126 | len == n 1127 | , 'expected #{this} to have a length of #{exp} but got #{act}' 1128 | , 'expected #{this} to not have a length of #{act}' 1129 | , n 1130 | , len 1131 | ); 1132 | } 1133 | 1134 | Assertion.addChainableMethod('length', assertLength, assertLengthChain); 1135 | Assertion.addMethod('lengthOf', assertLength, assertLengthChain); 1136 | 1137 | /** 1138 | * ### .match(regexp) 1139 | * 1140 | * Asserts that the target matches a regular expression. 1141 | * 1142 | * expect('foobar').to.match(/^foo/); 1143 | * 1144 | * @name match 1145 | * @param {RegExp} RegularExpression 1146 | * @param {String} message _optional_ 1147 | * @api public 1148 | */ 1149 | 1150 | Assertion.addMethod('match', function (re, msg) { 1151 | if (msg) flag(this, 'message', msg); 1152 | var obj = flag(this, 'object'); 1153 | this.assert( 1154 | re.exec(obj) 1155 | , 'expected #{this} to match ' + re 1156 | , 'expected #{this} not to match ' + re 1157 | ); 1158 | }); 1159 | 1160 | /** 1161 | * ### .string(string) 1162 | * 1163 | * Asserts that the string target contains another string. 1164 | * 1165 | * expect('foobar').to.have.string('bar'); 1166 | * 1167 | * @name string 1168 | * @param {String} string 1169 | * @param {String} message _optional_ 1170 | * @api public 1171 | */ 1172 | 1173 | Assertion.addMethod('string', function (str, msg) { 1174 | if (msg) flag(this, 'message', msg); 1175 | var obj = flag(this, 'object'); 1176 | new Assertion(obj, msg).is.a('string'); 1177 | 1178 | this.assert( 1179 | ~obj.indexOf(str) 1180 | , 'expected #{this} to contain ' + _.inspect(str) 1181 | , 'expected #{this} to not contain ' + _.inspect(str) 1182 | ); 1183 | }); 1184 | 1185 | 1186 | /** 1187 | * ### .keys(key1, [key2], [...]) 1188 | * 1189 | * Asserts that the target has exactly the given keys, or 1190 | * asserts the inclusion of some keys when using the 1191 | * `include` or `contain` modifiers. 1192 | * 1193 | * expect({ foo: 1, bar: 2 }).to.have.keys(['foo', 'bar']); 1194 | * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('foo', 'bar'); 1195 | * 1196 | * @name keys 1197 | * @alias key 1198 | * @param {String...|Array} keys 1199 | * @api public 1200 | */ 1201 | 1202 | function assertKeys (keys) { 1203 | var obj = flag(this, 'object') 1204 | , str 1205 | , ok = true; 1206 | 1207 | keys = keys instanceof Array 1208 | ? keys 1209 | : Array.prototype.slice.call(arguments); 1210 | 1211 | if (!keys.length) throw new Error('keys required'); 1212 | 1213 | var actual = Object.keys(obj) 1214 | , len = keys.length; 1215 | 1216 | // Inclusion 1217 | ok = keys.every(function(key){ 1218 | return ~actual.indexOf(key); 1219 | }); 1220 | 1221 | // Strict 1222 | if (!flag(this, 'negate') && !flag(this, 'contains')) { 1223 | ok = ok && keys.length == actual.length; 1224 | } 1225 | 1226 | // Key string 1227 | if (len > 1) { 1228 | keys = keys.map(function(key){ 1229 | return _.inspect(key); 1230 | }); 1231 | var last = keys.pop(); 1232 | str = keys.join(', ') + ', and ' + last; 1233 | } else { 1234 | str = _.inspect(keys[0]); 1235 | } 1236 | 1237 | // Form 1238 | str = (len > 1 ? 'keys ' : 'key ') + str; 1239 | 1240 | // Have / include 1241 | str = (flag(this, 'contains') ? 'contain ' : 'have ') + str; 1242 | 1243 | // Assertion 1244 | this.assert( 1245 | ok 1246 | , 'expected #{this} to ' + str 1247 | , 'expected #{this} to not ' + str 1248 | ); 1249 | } 1250 | 1251 | Assertion.addMethod('keys', assertKeys); 1252 | Assertion.addMethod('key', assertKeys); 1253 | 1254 | /** 1255 | * ### .throw(constructor) 1256 | * 1257 | * Asserts that the function target will throw a specific error, or specific type of error 1258 | * (as determined using `instanceof`), optionally with a RegExp or string inclusion test 1259 | * for the error's message. 1260 | * 1261 | * var err = new ReferenceError('This is a bad function.'); 1262 | * var fn = function () { throw err; } 1263 | * expect(fn).to.throw(ReferenceError); 1264 | * expect(fn).to.throw(Error); 1265 | * expect(fn).to.throw(/bad function/); 1266 | * expect(fn).to.not.throw('good function'); 1267 | * expect(fn).to.throw(ReferenceError, /bad function/); 1268 | * expect(fn).to.throw(err); 1269 | * expect(fn).to.not.throw(new RangeError('Out of range.')); 1270 | * 1271 | * Please note that when a throw expectation is negated, it will check each 1272 | * parameter independently, starting with error constructor type. The appropriate way 1273 | * to check for the existence of a type of error but for a message that does not match 1274 | * is to use `and`. 1275 | * 1276 | * expect(fn).to.throw(ReferenceError) 1277 | * .and.not.throw(/good function/); 1278 | * 1279 | * @name throw 1280 | * @alias throws 1281 | * @alias Throw 1282 | * @param {ErrorConstructor} constructor 1283 | * @param {String|RegExp} expected error message 1284 | * @param {String} message _optional_ 1285 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 1286 | * @api public 1287 | */ 1288 | 1289 | function assertThrows (constructor, errMsg, msg) { 1290 | if (msg) flag(this, 'message', msg); 1291 | var obj = flag(this, 'object'); 1292 | new Assertion(obj, msg).is.a('function'); 1293 | 1294 | var thrown = false 1295 | , desiredError = null 1296 | , name = null 1297 | , thrownError = null; 1298 | 1299 | if (arguments.length === 0) { 1300 | errMsg = null; 1301 | constructor = null; 1302 | } else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) { 1303 | errMsg = constructor; 1304 | constructor = null; 1305 | } else if (constructor && constructor instanceof Error) { 1306 | desiredError = constructor; 1307 | constructor = null; 1308 | errMsg = null; 1309 | } else if (typeof constructor === 'function') { 1310 | name = (new constructor()).name; 1311 | } else { 1312 | constructor = null; 1313 | } 1314 | 1315 | try { 1316 | obj(); 1317 | } catch (err) { 1318 | // first, check desired error 1319 | if (desiredError) { 1320 | this.assert( 1321 | err === desiredError 1322 | , 'expected #{this} to throw #{exp} but #{act} was thrown' 1323 | , 'expected #{this} to not throw #{exp}' 1324 | , desiredError 1325 | , err 1326 | ); 1327 | 1328 | return this; 1329 | } 1330 | // next, check constructor 1331 | if (constructor) { 1332 | this.assert( 1333 | err instanceof constructor 1334 | , 'expected #{this} to throw #{exp} but #{act} was thrown' 1335 | , 'expected #{this} to not throw #{exp} but #{act} was thrown' 1336 | , name 1337 | , err 1338 | ); 1339 | 1340 | if (!errMsg) return this; 1341 | } 1342 | // next, check message 1343 | var message = 'object' === _.type(err) && "message" in err 1344 | ? err.message 1345 | : '' + err; 1346 | 1347 | if ((message != null) && errMsg && errMsg instanceof RegExp) { 1348 | this.assert( 1349 | errMsg.exec(message) 1350 | , 'expected #{this} to throw error matching #{exp} but got #{act}' 1351 | , 'expected #{this} to throw error not matching #{exp}' 1352 | , errMsg 1353 | , message 1354 | ); 1355 | 1356 | return this; 1357 | } else if ((message != null) && errMsg && 'string' === typeof errMsg) { 1358 | this.assert( 1359 | ~message.indexOf(errMsg) 1360 | , 'expected #{this} to throw error including #{exp} but got #{act}' 1361 | , 'expected #{this} to throw error not including #{act}' 1362 | , errMsg 1363 | , message 1364 | ); 1365 | 1366 | return this; 1367 | } else { 1368 | thrown = true; 1369 | thrownError = err; 1370 | } 1371 | } 1372 | 1373 | var actuallyGot = '' 1374 | , expectedThrown = name !== null 1375 | ? name 1376 | : desiredError 1377 | ? '#{exp}' //_.inspect(desiredError) 1378 | : 'an error'; 1379 | 1380 | if (thrown) { 1381 | actuallyGot = ' but #{act} was thrown' 1382 | } 1383 | 1384 | this.assert( 1385 | thrown === true 1386 | , 'expected #{this} to throw ' + expectedThrown + actuallyGot 1387 | , 'expected #{this} to not throw ' + expectedThrown + actuallyGot 1388 | , desiredError 1389 | , thrownError 1390 | ); 1391 | }; 1392 | 1393 | Assertion.addMethod('throw', assertThrows); 1394 | Assertion.addMethod('throws', assertThrows); 1395 | Assertion.addMethod('Throw', assertThrows); 1396 | 1397 | /** 1398 | * ### .respondTo(method) 1399 | * 1400 | * Asserts that the object or class target will respond to a method. 1401 | * 1402 | * Klass.prototype.bar = function(){}; 1403 | * expect(Klass).to.respondTo('bar'); 1404 | * expect(obj).to.respondTo('bar'); 1405 | * 1406 | * To check if a constructor will respond to a static function, 1407 | * set the `itself` flag. 1408 | * 1409 | * Klass.baz = function(){}; 1410 | * expect(Klass).itself.to.respondTo('baz'); 1411 | * 1412 | * @name respondTo 1413 | * @param {String} method 1414 | * @param {String} message _optional_ 1415 | * @api public 1416 | */ 1417 | 1418 | Assertion.addMethod('respondTo', function (method, msg) { 1419 | if (msg) flag(this, 'message', msg); 1420 | var obj = flag(this, 'object') 1421 | , itself = flag(this, 'itself') 1422 | , context = ('function' === _.type(obj) && !itself) 1423 | ? obj.prototype[method] 1424 | : obj[method]; 1425 | 1426 | this.assert( 1427 | 'function' === typeof context 1428 | , 'expected #{this} to respond to ' + _.inspect(method) 1429 | , 'expected #{this} to not respond to ' + _.inspect(method) 1430 | ); 1431 | }); 1432 | 1433 | /** 1434 | * ### .itself 1435 | * 1436 | * Sets the `itself` flag, later used by the `respondTo` assertion. 1437 | * 1438 | * function Foo() {} 1439 | * Foo.bar = function() {} 1440 | * Foo.prototype.baz = function() {} 1441 | * 1442 | * expect(Foo).itself.to.respondTo('bar'); 1443 | * expect(Foo).itself.not.to.respondTo('baz'); 1444 | * 1445 | * @name itself 1446 | * @api public 1447 | */ 1448 | 1449 | Assertion.addProperty('itself', function () { 1450 | flag(this, 'itself', true); 1451 | }); 1452 | 1453 | /** 1454 | * ### .satisfy(method) 1455 | * 1456 | * Asserts that the target passes a given truth test. 1457 | * 1458 | * expect(1).to.satisfy(function(num) { return num > 0; }); 1459 | * 1460 | * @name satisfy 1461 | * @param {Function} matcher 1462 | * @param {String} message _optional_ 1463 | * @api public 1464 | */ 1465 | 1466 | Assertion.addMethod('satisfy', function (matcher, msg) { 1467 | if (msg) flag(this, 'message', msg); 1468 | var obj = flag(this, 'object'); 1469 | this.assert( 1470 | matcher(obj) 1471 | , 'expected #{this} to satisfy ' + _.objDisplay(matcher) 1472 | , 'expected #{this} to not satisfy' + _.objDisplay(matcher) 1473 | , this.negate ? false : true 1474 | , matcher(obj) 1475 | ); 1476 | }); 1477 | 1478 | /** 1479 | * ### .closeTo(expected, delta) 1480 | * 1481 | * Asserts that the target is equal `expected`, to within a +/- `delta` range. 1482 | * 1483 | * expect(1.5).to.be.closeTo(1, 0.5); 1484 | * 1485 | * @name closeTo 1486 | * @param {Number} expected 1487 | * @param {Number} delta 1488 | * @param {String} message _optional_ 1489 | * @api public 1490 | */ 1491 | 1492 | Assertion.addMethod('closeTo', function (expected, delta, msg) { 1493 | if (msg) flag(this, 'message', msg); 1494 | var obj = flag(this, 'object'); 1495 | this.assert( 1496 | Math.abs(obj - expected) <= delta 1497 | , 'expected #{this} to be close to ' + expected + ' +/- ' + delta 1498 | , 'expected #{this} not to be close to ' + expected + ' +/- ' + delta 1499 | ); 1500 | }); 1501 | 1502 | }; 1503 | 1504 | }); // module: chai/core/assertions.js 1505 | 1506 | require.register("chai/error.js", function(module, exports, require){ 1507 | /*! 1508 | * chai 1509 | * Copyright(c) 2011-2013 Jake Luer 1510 | * MIT Licensed 1511 | */ 1512 | 1513 | /*! 1514 | * Main export 1515 | */ 1516 | 1517 | module.exports = AssertionError; 1518 | 1519 | /** 1520 | * # AssertionError (constructor) 1521 | * 1522 | * Create a new assertion error based on the Javascript 1523 | * `Error` prototype. 1524 | * 1525 | * **Options** 1526 | * - message 1527 | * - actual 1528 | * - expected 1529 | * - operator 1530 | * - startStackFunction 1531 | * 1532 | * @param {Object} options 1533 | * @api public 1534 | */ 1535 | 1536 | function AssertionError (options) { 1537 | options = options || {}; 1538 | this.message = options.message; 1539 | this.actual = options.actual; 1540 | this.expected = options.expected; 1541 | this.operator = options.operator; 1542 | this.showDiff = options.showDiff; 1543 | 1544 | if (options.stackStartFunction && Error.captureStackTrace) { 1545 | var stackStartFunction = options.stackStartFunction; 1546 | Error.captureStackTrace(this, stackStartFunction); 1547 | } 1548 | } 1549 | 1550 | /*! 1551 | * Inherit from Error 1552 | */ 1553 | 1554 | AssertionError.prototype = Object.create(Error.prototype); 1555 | AssertionError.prototype.name = 'AssertionError'; 1556 | AssertionError.prototype.constructor = AssertionError; 1557 | 1558 | /** 1559 | * # toString() 1560 | * 1561 | * Override default to string method 1562 | */ 1563 | 1564 | AssertionError.prototype.toString = function() { 1565 | return this.message; 1566 | }; 1567 | 1568 | }); // module: chai/error.js 1569 | 1570 | require.register("chai/interface/assert.js", function(module, exports, require){ 1571 | /*! 1572 | * chai 1573 | * Copyright(c) 2011-2013 Jake Luer 1574 | * MIT Licensed 1575 | */ 1576 | 1577 | 1578 | module.exports = function (chai, util) { 1579 | 1580 | /*! 1581 | * Chai dependencies. 1582 | */ 1583 | 1584 | var Assertion = chai.Assertion 1585 | , flag = util.flag; 1586 | 1587 | /*! 1588 | * Module export. 1589 | */ 1590 | 1591 | /** 1592 | * ### assert(expression, message) 1593 | * 1594 | * Write your own test expressions. 1595 | * 1596 | * assert('foo' !== 'bar', 'foo is not bar'); 1597 | * assert(Array.isArray([]), 'empty arrays are arrays'); 1598 | * 1599 | * @param {Mixed} expression to test for truthiness 1600 | * @param {String} message to display on error 1601 | * @name assert 1602 | * @api public 1603 | */ 1604 | 1605 | var assert = chai.assert = function (express, errmsg) { 1606 | var test = new Assertion(null); 1607 | test.assert( 1608 | express 1609 | , errmsg 1610 | , '[ negation message unavailable ]' 1611 | ); 1612 | }; 1613 | 1614 | /** 1615 | * ### .fail(actual, expected, [message], [operator]) 1616 | * 1617 | * Throw a failure. Node.js `assert` module-compatible. 1618 | * 1619 | * @name fail 1620 | * @param {Mixed} actual 1621 | * @param {Mixed} expected 1622 | * @param {String} message 1623 | * @param {String} operator 1624 | * @api public 1625 | */ 1626 | 1627 | assert.fail = function (actual, expected, message, operator) { 1628 | throw new chai.AssertionError({ 1629 | actual: actual 1630 | , expected: expected 1631 | , message: message 1632 | , operator: operator 1633 | , stackStartFunction: assert.fail 1634 | }); 1635 | }; 1636 | 1637 | /** 1638 | * ### .ok(object, [message]) 1639 | * 1640 | * Asserts that `object` is truthy. 1641 | * 1642 | * assert.ok('everything', 'everything is ok'); 1643 | * assert.ok(false, 'this will fail'); 1644 | * 1645 | * @name ok 1646 | * @param {Mixed} object to test 1647 | * @param {String} message 1648 | * @api public 1649 | */ 1650 | 1651 | assert.ok = function (val, msg) { 1652 | new Assertion(val, msg).is.ok; 1653 | }; 1654 | 1655 | /** 1656 | * ### .equal(actual, expected, [message]) 1657 | * 1658 | * Asserts non-strict equality (`==`) of `actual` and `expected`. 1659 | * 1660 | * assert.equal(3, '3', '== coerces values to strings'); 1661 | * 1662 | * @name equal 1663 | * @param {Mixed} actual 1664 | * @param {Mixed} expected 1665 | * @param {String} message 1666 | * @api public 1667 | */ 1668 | 1669 | assert.equal = function (act, exp, msg) { 1670 | var test = new Assertion(act, msg); 1671 | 1672 | test.assert( 1673 | exp == flag(test, 'object') 1674 | , 'expected #{this} to equal #{exp}' 1675 | , 'expected #{this} to not equal #{act}' 1676 | , exp 1677 | , act 1678 | ); 1679 | }; 1680 | 1681 | /** 1682 | * ### .notEqual(actual, expected, [message]) 1683 | * 1684 | * Asserts non-strict inequality (`!=`) of `actual` and `expected`. 1685 | * 1686 | * assert.notEqual(3, 4, 'these numbers are not equal'); 1687 | * 1688 | * @name notEqual 1689 | * @param {Mixed} actual 1690 | * @param {Mixed} expected 1691 | * @param {String} message 1692 | * @api public 1693 | */ 1694 | 1695 | assert.notEqual = function (act, exp, msg) { 1696 | var test = new Assertion(act, msg); 1697 | 1698 | test.assert( 1699 | exp != flag(test, 'object') 1700 | , 'expected #{this} to not equal #{exp}' 1701 | , 'expected #{this} to equal #{act}' 1702 | , exp 1703 | , act 1704 | ); 1705 | }; 1706 | 1707 | /** 1708 | * ### .strictEqual(actual, expected, [message]) 1709 | * 1710 | * Asserts strict equality (`===`) of `actual` and `expected`. 1711 | * 1712 | * assert.strictEqual(true, true, 'these booleans are strictly equal'); 1713 | * 1714 | * @name strictEqual 1715 | * @param {Mixed} actual 1716 | * @param {Mixed} expected 1717 | * @param {String} message 1718 | * @api public 1719 | */ 1720 | 1721 | assert.strictEqual = function (act, exp, msg) { 1722 | new Assertion(act, msg).to.equal(exp); 1723 | }; 1724 | 1725 | /** 1726 | * ### .notStrictEqual(actual, expected, [message]) 1727 | * 1728 | * Asserts strict inequality (`!==`) of `actual` and `expected`. 1729 | * 1730 | * assert.notStrictEqual(3, '3', 'no coercion for strict equality'); 1731 | * 1732 | * @name notStrictEqual 1733 | * @param {Mixed} actual 1734 | * @param {Mixed} expected 1735 | * @param {String} message 1736 | * @api public 1737 | */ 1738 | 1739 | assert.notStrictEqual = function (act, exp, msg) { 1740 | new Assertion(act, msg).to.not.equal(exp); 1741 | }; 1742 | 1743 | /** 1744 | * ### .deepEqual(actual, expected, [message]) 1745 | * 1746 | * Asserts that `actual` is deeply equal to `expected`. 1747 | * 1748 | * assert.deepEqual({ tea: 'green' }, { tea: 'green' }); 1749 | * 1750 | * @name deepEqual 1751 | * @param {Mixed} actual 1752 | * @param {Mixed} expected 1753 | * @param {String} message 1754 | * @api public 1755 | */ 1756 | 1757 | assert.deepEqual = function (act, exp, msg) { 1758 | new Assertion(act, msg).to.eql(exp); 1759 | }; 1760 | 1761 | /** 1762 | * ### .notDeepEqual(actual, expected, [message]) 1763 | * 1764 | * Assert that `actual` is not deeply equal to `expected`. 1765 | * 1766 | * assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); 1767 | * 1768 | * @name notDeepEqual 1769 | * @param {Mixed} actual 1770 | * @param {Mixed} expected 1771 | * @param {String} message 1772 | * @api public 1773 | */ 1774 | 1775 | assert.notDeepEqual = function (act, exp, msg) { 1776 | new Assertion(act, msg).to.not.eql(exp); 1777 | }; 1778 | 1779 | /** 1780 | * ### .isTrue(value, [message]) 1781 | * 1782 | * Asserts that `value` is true. 1783 | * 1784 | * var teaServed = true; 1785 | * assert.isTrue(teaServed, 'the tea has been served'); 1786 | * 1787 | * @name isTrue 1788 | * @param {Mixed} value 1789 | * @param {String} message 1790 | * @api public 1791 | */ 1792 | 1793 | assert.isTrue = function (val, msg) { 1794 | new Assertion(val, msg).is['true']; 1795 | }; 1796 | 1797 | /** 1798 | * ### .isFalse(value, [message]) 1799 | * 1800 | * Asserts that `value` is false. 1801 | * 1802 | * var teaServed = false; 1803 | * assert.isFalse(teaServed, 'no tea yet? hmm...'); 1804 | * 1805 | * @name isFalse 1806 | * @param {Mixed} value 1807 | * @param {String} message 1808 | * @api public 1809 | */ 1810 | 1811 | assert.isFalse = function (val, msg) { 1812 | new Assertion(val, msg).is['false']; 1813 | }; 1814 | 1815 | /** 1816 | * ### .isNull(value, [message]) 1817 | * 1818 | * Asserts that `value` is null. 1819 | * 1820 | * assert.isNull(err, 'there was no error'); 1821 | * 1822 | * @name isNull 1823 | * @param {Mixed} value 1824 | * @param {String} message 1825 | * @api public 1826 | */ 1827 | 1828 | assert.isNull = function (val, msg) { 1829 | new Assertion(val, msg).to.equal(null); 1830 | }; 1831 | 1832 | /** 1833 | * ### .isNotNull(value, [message]) 1834 | * 1835 | * Asserts that `value` is not null. 1836 | * 1837 | * var tea = 'tasty chai'; 1838 | * assert.isNotNull(tea, 'great, time for tea!'); 1839 | * 1840 | * @name isNotNull 1841 | * @param {Mixed} value 1842 | * @param {String} message 1843 | * @api public 1844 | */ 1845 | 1846 | assert.isNotNull = function (val, msg) { 1847 | new Assertion(val, msg).to.not.equal(null); 1848 | }; 1849 | 1850 | /** 1851 | * ### .isUndefined(value, [message]) 1852 | * 1853 | * Asserts that `value` is `undefined`. 1854 | * 1855 | * var tea; 1856 | * assert.isUndefined(tea, 'no tea defined'); 1857 | * 1858 | * @name isUndefined 1859 | * @param {Mixed} value 1860 | * @param {String} message 1861 | * @api public 1862 | */ 1863 | 1864 | assert.isUndefined = function (val, msg) { 1865 | new Assertion(val, msg).to.equal(undefined); 1866 | }; 1867 | 1868 | /** 1869 | * ### .isDefined(value, [message]) 1870 | * 1871 | * Asserts that `value` is not `undefined`. 1872 | * 1873 | * var tea = 'cup of chai'; 1874 | * assert.isDefined(tea, 'tea has been defined'); 1875 | * 1876 | * @name isUndefined 1877 | * @param {Mixed} value 1878 | * @param {String} message 1879 | * @api public 1880 | */ 1881 | 1882 | assert.isDefined = function (val, msg) { 1883 | new Assertion(val, msg).to.not.equal(undefined); 1884 | }; 1885 | 1886 | /** 1887 | * ### .isFunction(value, [message]) 1888 | * 1889 | * Asserts that `value` is a function. 1890 | * 1891 | * function serveTea() { return 'cup of tea'; }; 1892 | * assert.isFunction(serveTea, 'great, we can have tea now'); 1893 | * 1894 | * @name isFunction 1895 | * @param {Mixed} value 1896 | * @param {String} message 1897 | * @api public 1898 | */ 1899 | 1900 | assert.isFunction = function (val, msg) { 1901 | new Assertion(val, msg).to.be.a('function'); 1902 | }; 1903 | 1904 | /** 1905 | * ### .isNotFunction(value, [message]) 1906 | * 1907 | * Asserts that `value` is _not_ a function. 1908 | * 1909 | * var serveTea = [ 'heat', 'pour', 'sip' ]; 1910 | * assert.isNotFunction(serveTea, 'great, we have listed the steps'); 1911 | * 1912 | * @name isNotFunction 1913 | * @param {Mixed} value 1914 | * @param {String} message 1915 | * @api public 1916 | */ 1917 | 1918 | assert.isNotFunction = function (val, msg) { 1919 | new Assertion(val, msg).to.not.be.a('function'); 1920 | }; 1921 | 1922 | /** 1923 | * ### .isObject(value, [message]) 1924 | * 1925 | * Asserts that `value` is an object (as revealed by 1926 | * `Object.prototype.toString`). 1927 | * 1928 | * var selection = { name: 'Chai', serve: 'with spices' }; 1929 | * assert.isObject(selection, 'tea selection is an object'); 1930 | * 1931 | * @name isObject 1932 | * @param {Mixed} value 1933 | * @param {String} message 1934 | * @api public 1935 | */ 1936 | 1937 | assert.isObject = function (val, msg) { 1938 | new Assertion(val, msg).to.be.a('object'); 1939 | }; 1940 | 1941 | /** 1942 | * ### .isNotObject(value, [message]) 1943 | * 1944 | * Asserts that `value` is _not_ an object. 1945 | * 1946 | * var selection = 'chai' 1947 | * assert.isObject(selection, 'tea selection is not an object'); 1948 | * assert.isObject(null, 'null is not an object'); 1949 | * 1950 | * @name isNotObject 1951 | * @param {Mixed} value 1952 | * @param {String} message 1953 | * @api public 1954 | */ 1955 | 1956 | assert.isNotObject = function (val, msg) { 1957 | new Assertion(val, msg).to.not.be.a('object'); 1958 | }; 1959 | 1960 | /** 1961 | * ### .isArray(value, [message]) 1962 | * 1963 | * Asserts that `value` is an array. 1964 | * 1965 | * var menu = [ 'green', 'chai', 'oolong' ]; 1966 | * assert.isArray(menu, 'what kind of tea do we want?'); 1967 | * 1968 | * @name isArray 1969 | * @param {Mixed} value 1970 | * @param {String} message 1971 | * @api public 1972 | */ 1973 | 1974 | assert.isArray = function (val, msg) { 1975 | new Assertion(val, msg).to.be.an('array'); 1976 | }; 1977 | 1978 | /** 1979 | * ### .isNotArray(value, [message]) 1980 | * 1981 | * Asserts that `value` is _not_ an array. 1982 | * 1983 | * var menu = 'green|chai|oolong'; 1984 | * assert.isNotArray(menu, 'what kind of tea do we want?'); 1985 | * 1986 | * @name isNotArray 1987 | * @param {Mixed} value 1988 | * @param {String} message 1989 | * @api public 1990 | */ 1991 | 1992 | assert.isNotArray = function (val, msg) { 1993 | new Assertion(val, msg).to.not.be.an('array'); 1994 | }; 1995 | 1996 | /** 1997 | * ### .isString(value, [message]) 1998 | * 1999 | * Asserts that `value` is a string. 2000 | * 2001 | * var teaOrder = 'chai'; 2002 | * assert.isString(teaOrder, 'order placed'); 2003 | * 2004 | * @name isString 2005 | * @param {Mixed} value 2006 | * @param {String} message 2007 | * @api public 2008 | */ 2009 | 2010 | assert.isString = function (val, msg) { 2011 | new Assertion(val, msg).to.be.a('string'); 2012 | }; 2013 | 2014 | /** 2015 | * ### .isNotString(value, [message]) 2016 | * 2017 | * Asserts that `value` is _not_ a string. 2018 | * 2019 | * var teaOrder = 4; 2020 | * assert.isNotString(teaOrder, 'order placed'); 2021 | * 2022 | * @name isNotString 2023 | * @param {Mixed} value 2024 | * @param {String} message 2025 | * @api public 2026 | */ 2027 | 2028 | assert.isNotString = function (val, msg) { 2029 | new Assertion(val, msg).to.not.be.a('string'); 2030 | }; 2031 | 2032 | /** 2033 | * ### .isNumber(value, [message]) 2034 | * 2035 | * Asserts that `value` is a number. 2036 | * 2037 | * var cups = 2; 2038 | * assert.isNumber(cups, 'how many cups'); 2039 | * 2040 | * @name isNumber 2041 | * @param {Number} value 2042 | * @param {String} message 2043 | * @api public 2044 | */ 2045 | 2046 | assert.isNumber = function (val, msg) { 2047 | new Assertion(val, msg).to.be.a('number'); 2048 | }; 2049 | 2050 | /** 2051 | * ### .isNotNumber(value, [message]) 2052 | * 2053 | * Asserts that `value` is _not_ a number. 2054 | * 2055 | * var cups = '2 cups please'; 2056 | * assert.isNotNumber(cups, 'how many cups'); 2057 | * 2058 | * @name isNotNumber 2059 | * @param {Mixed} value 2060 | * @param {String} message 2061 | * @api public 2062 | */ 2063 | 2064 | assert.isNotNumber = function (val, msg) { 2065 | new Assertion(val, msg).to.not.be.a('number'); 2066 | }; 2067 | 2068 | /** 2069 | * ### .isBoolean(value, [message]) 2070 | * 2071 | * Asserts that `value` is a boolean. 2072 | * 2073 | * var teaReady = true 2074 | * , teaServed = false; 2075 | * 2076 | * assert.isBoolean(teaReady, 'is the tea ready'); 2077 | * assert.isBoolean(teaServed, 'has tea been served'); 2078 | * 2079 | * @name isBoolean 2080 | * @param {Mixed} value 2081 | * @param {String} message 2082 | * @api public 2083 | */ 2084 | 2085 | assert.isBoolean = function (val, msg) { 2086 | new Assertion(val, msg).to.be.a('boolean'); 2087 | }; 2088 | 2089 | /** 2090 | * ### .isNotBoolean(value, [message]) 2091 | * 2092 | * Asserts that `value` is _not_ a boolean. 2093 | * 2094 | * var teaReady = 'yep' 2095 | * , teaServed = 'nope'; 2096 | * 2097 | * assert.isNotBoolean(teaReady, 'is the tea ready'); 2098 | * assert.isNotBoolean(teaServed, 'has tea been served'); 2099 | * 2100 | * @name isNotBoolean 2101 | * @param {Mixed} value 2102 | * @param {String} message 2103 | * @api public 2104 | */ 2105 | 2106 | assert.isNotBoolean = function (val, msg) { 2107 | new Assertion(val, msg).to.not.be.a('boolean'); 2108 | }; 2109 | 2110 | /** 2111 | * ### .typeOf(value, name, [message]) 2112 | * 2113 | * Asserts that `value`'s type is `name`, as determined by 2114 | * `Object.prototype.toString`. 2115 | * 2116 | * assert.typeOf({ tea: 'chai' }, 'object', 'we have an object'); 2117 | * assert.typeOf(['chai', 'jasmine'], 'array', 'we have an array'); 2118 | * assert.typeOf('tea', 'string', 'we have a string'); 2119 | * assert.typeOf(/tea/, 'regexp', 'we have a regular expression'); 2120 | * assert.typeOf(null, 'null', 'we have a null'); 2121 | * assert.typeOf(undefined, 'undefined', 'we have an undefined'); 2122 | * 2123 | * @name typeOf 2124 | * @param {Mixed} value 2125 | * @param {String} name 2126 | * @param {String} message 2127 | * @api public 2128 | */ 2129 | 2130 | assert.typeOf = function (val, type, msg) { 2131 | new Assertion(val, msg).to.be.a(type); 2132 | }; 2133 | 2134 | /** 2135 | * ### .notTypeOf(value, name, [message]) 2136 | * 2137 | * Asserts that `value`'s type is _not_ `name`, as determined by 2138 | * `Object.prototype.toString`. 2139 | * 2140 | * assert.notTypeOf('tea', 'number', 'strings are not numbers'); 2141 | * 2142 | * @name notTypeOf 2143 | * @param {Mixed} value 2144 | * @param {String} typeof name 2145 | * @param {String} message 2146 | * @api public 2147 | */ 2148 | 2149 | assert.notTypeOf = function (val, type, msg) { 2150 | new Assertion(val, msg).to.not.be.a(type); 2151 | }; 2152 | 2153 | /** 2154 | * ### .instanceOf(object, constructor, [message]) 2155 | * 2156 | * Asserts that `value` is an instance of `constructor`. 2157 | * 2158 | * var Tea = function (name) { this.name = name; } 2159 | * , chai = new Tea('chai'); 2160 | * 2161 | * assert.instanceOf(chai, Tea, 'chai is an instance of tea'); 2162 | * 2163 | * @name instanceOf 2164 | * @param {Object} object 2165 | * @param {Constructor} constructor 2166 | * @param {String} message 2167 | * @api public 2168 | */ 2169 | 2170 | assert.instanceOf = function (val, type, msg) { 2171 | new Assertion(val, msg).to.be.instanceOf(type); 2172 | }; 2173 | 2174 | /** 2175 | * ### .notInstanceOf(object, constructor, [message]) 2176 | * 2177 | * Asserts `value` is not an instance of `constructor`. 2178 | * 2179 | * var Tea = function (name) { this.name = name; } 2180 | * , chai = new String('chai'); 2181 | * 2182 | * assert.notInstanceOf(chai, Tea, 'chai is not an instance of tea'); 2183 | * 2184 | * @name notInstanceOf 2185 | * @param {Object} object 2186 | * @param {Constructor} constructor 2187 | * @param {String} message 2188 | * @api public 2189 | */ 2190 | 2191 | assert.notInstanceOf = function (val, type, msg) { 2192 | new Assertion(val, msg).to.not.be.instanceOf(type); 2193 | }; 2194 | 2195 | /** 2196 | * ### .include(haystack, needle, [message]) 2197 | * 2198 | * Asserts that `haystack` includes `needle`. Works 2199 | * for strings and arrays. 2200 | * 2201 | * assert.include('foobar', 'bar', 'foobar contains string "bar"'); 2202 | * assert.include([ 1, 2, 3 ], 3, 'array contains value'); 2203 | * 2204 | * @name include 2205 | * @param {Array|String} haystack 2206 | * @param {Mixed} needle 2207 | * @param {String} message 2208 | * @api public 2209 | */ 2210 | 2211 | assert.include = function (exp, inc, msg) { 2212 | var obj = new Assertion(exp, msg); 2213 | 2214 | if (Array.isArray(exp)) { 2215 | obj.to.include(inc); 2216 | } else if ('string' === typeof exp) { 2217 | obj.to.contain.string(inc); 2218 | } 2219 | }; 2220 | 2221 | /** 2222 | * ### .match(value, regexp, [message]) 2223 | * 2224 | * Asserts that `value` matches the regular expression `regexp`. 2225 | * 2226 | * assert.match('foobar', /^foo/, 'regexp matches'); 2227 | * 2228 | * @name match 2229 | * @param {Mixed} value 2230 | * @param {RegExp} regexp 2231 | * @param {String} message 2232 | * @api public 2233 | */ 2234 | 2235 | assert.match = function (exp, re, msg) { 2236 | new Assertion(exp, msg).to.match(re); 2237 | }; 2238 | 2239 | /** 2240 | * ### .notMatch(value, regexp, [message]) 2241 | * 2242 | * Asserts that `value` does not match the regular expression `regexp`. 2243 | * 2244 | * assert.notMatch('foobar', /^foo/, 'regexp does not match'); 2245 | * 2246 | * @name notMatch 2247 | * @param {Mixed} value 2248 | * @param {RegExp} regexp 2249 | * @param {String} message 2250 | * @api public 2251 | */ 2252 | 2253 | assert.notMatch = function (exp, re, msg) { 2254 | new Assertion(exp, msg).to.not.match(re); 2255 | }; 2256 | 2257 | /** 2258 | * ### .property(object, property, [message]) 2259 | * 2260 | * Asserts that `object` has a property named by `property`. 2261 | * 2262 | * assert.property({ tea: { green: 'matcha' }}, 'tea'); 2263 | * 2264 | * @name property 2265 | * @param {Object} object 2266 | * @param {String} property 2267 | * @param {String} message 2268 | * @api public 2269 | */ 2270 | 2271 | assert.property = function (obj, prop, msg) { 2272 | new Assertion(obj, msg).to.have.property(prop); 2273 | }; 2274 | 2275 | /** 2276 | * ### .notProperty(object, property, [message]) 2277 | * 2278 | * Asserts that `object` does _not_ have a property named by `property`. 2279 | * 2280 | * assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); 2281 | * 2282 | * @name notProperty 2283 | * @param {Object} object 2284 | * @param {String} property 2285 | * @param {String} message 2286 | * @api public 2287 | */ 2288 | 2289 | assert.notProperty = function (obj, prop, msg) { 2290 | new Assertion(obj, msg).to.not.have.property(prop); 2291 | }; 2292 | 2293 | /** 2294 | * ### .deepProperty(object, property, [message]) 2295 | * 2296 | * Asserts that `object` has a property named by `property`, which can be a 2297 | * string using dot- and bracket-notation for deep reference. 2298 | * 2299 | * assert.deepProperty({ tea: { green: 'matcha' }}, 'tea.green'); 2300 | * 2301 | * @name deepProperty 2302 | * @param {Object} object 2303 | * @param {String} property 2304 | * @param {String} message 2305 | * @api public 2306 | */ 2307 | 2308 | assert.deepProperty = function (obj, prop, msg) { 2309 | new Assertion(obj, msg).to.have.deep.property(prop); 2310 | }; 2311 | 2312 | /** 2313 | * ### .notDeepProperty(object, property, [message]) 2314 | * 2315 | * Asserts that `object` does _not_ have a property named by `property`, which 2316 | * can be a string using dot- and bracket-notation for deep reference. 2317 | * 2318 | * assert.notDeepProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); 2319 | * 2320 | * @name notDeepProperty 2321 | * @param {Object} object 2322 | * @param {String} property 2323 | * @param {String} message 2324 | * @api public 2325 | */ 2326 | 2327 | assert.notDeepProperty = function (obj, prop, msg) { 2328 | new Assertion(obj, msg).to.not.have.deep.property(prop); 2329 | }; 2330 | 2331 | /** 2332 | * ### .propertyVal(object, property, value, [message]) 2333 | * 2334 | * Asserts that `object` has a property named by `property` with value given 2335 | * by `value`. 2336 | * 2337 | * assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); 2338 | * 2339 | * @name propertyVal 2340 | * @param {Object} object 2341 | * @param {String} property 2342 | * @param {Mixed} value 2343 | * @param {String} message 2344 | * @api public 2345 | */ 2346 | 2347 | assert.propertyVal = function (obj, prop, val, msg) { 2348 | new Assertion(obj, msg).to.have.property(prop, val); 2349 | }; 2350 | 2351 | /** 2352 | * ### .propertyNotVal(object, property, value, [message]) 2353 | * 2354 | * Asserts that `object` has a property named by `property`, but with a value 2355 | * different from that given by `value`. 2356 | * 2357 | * assert.propertyNotVal({ tea: 'is good' }, 'tea', 'is bad'); 2358 | * 2359 | * @name propertyNotVal 2360 | * @param {Object} object 2361 | * @param {String} property 2362 | * @param {Mixed} value 2363 | * @param {String} message 2364 | * @api public 2365 | */ 2366 | 2367 | assert.propertyNotVal = function (obj, prop, val, msg) { 2368 | new Assertion(obj, msg).to.not.have.property(prop, val); 2369 | }; 2370 | 2371 | /** 2372 | * ### .deepPropertyVal(object, property, value, [message]) 2373 | * 2374 | * Asserts that `object` has a property named by `property` with value given 2375 | * by `value`. `property` can use dot- and bracket-notation for deep 2376 | * reference. 2377 | * 2378 | * assert.deepPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); 2379 | * 2380 | * @name deepPropertyVal 2381 | * @param {Object} object 2382 | * @param {String} property 2383 | * @param {Mixed} value 2384 | * @param {String} message 2385 | * @api public 2386 | */ 2387 | 2388 | assert.deepPropertyVal = function (obj, prop, val, msg) { 2389 | new Assertion(obj, msg).to.have.deep.property(prop, val); 2390 | }; 2391 | 2392 | /** 2393 | * ### .deepPropertyNotVal(object, property, value, [message]) 2394 | * 2395 | * Asserts that `object` has a property named by `property`, but with a value 2396 | * different from that given by `value`. `property` can use dot- and 2397 | * bracket-notation for deep reference. 2398 | * 2399 | * assert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); 2400 | * 2401 | * @name deepPropertyNotVal 2402 | * @param {Object} object 2403 | * @param {String} property 2404 | * @param {Mixed} value 2405 | * @param {String} message 2406 | * @api public 2407 | */ 2408 | 2409 | assert.deepPropertyNotVal = function (obj, prop, val, msg) { 2410 | new Assertion(obj, msg).to.not.have.deep.property(prop, val); 2411 | }; 2412 | 2413 | /** 2414 | * ### .lengthOf(object, length, [message]) 2415 | * 2416 | * Asserts that `object` has a `length` property with the expected value. 2417 | * 2418 | * assert.lengthOf([1,2,3], 3, 'array has length of 3'); 2419 | * assert.lengthOf('foobar', 5, 'string has length of 6'); 2420 | * 2421 | * @name lengthOf 2422 | * @param {Mixed} object 2423 | * @param {Number} length 2424 | * @param {String} message 2425 | * @api public 2426 | */ 2427 | 2428 | assert.lengthOf = function (exp, len, msg) { 2429 | new Assertion(exp, msg).to.have.length(len); 2430 | }; 2431 | 2432 | /** 2433 | * ### .throws(function, [constructor/string/regexp], [string/regexp], [message]) 2434 | * 2435 | * Asserts that `function` will throw an error that is an instance of 2436 | * `constructor`, or alternately that it will throw an error with message 2437 | * matching `regexp`. 2438 | * 2439 | * assert.throw(fn, 'function throws a reference error'); 2440 | * assert.throw(fn, /function throws a reference error/); 2441 | * assert.throw(fn, ReferenceError); 2442 | * assert.throw(fn, ReferenceError, 'function throws a reference error'); 2443 | * assert.throw(fn, ReferenceError, /function throws a reference error/); 2444 | * 2445 | * @name throws 2446 | * @alias throw 2447 | * @alias Throw 2448 | * @param {Function} function 2449 | * @param {ErrorConstructor} constructor 2450 | * @param {RegExp} regexp 2451 | * @param {String} message 2452 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 2453 | * @api public 2454 | */ 2455 | 2456 | assert.Throw = function (fn, errt, errs, msg) { 2457 | if ('string' === typeof errt || errt instanceof RegExp) { 2458 | errs = errt; 2459 | errt = null; 2460 | } 2461 | 2462 | new Assertion(fn, msg).to.Throw(errt, errs); 2463 | }; 2464 | 2465 | /** 2466 | * ### .doesNotThrow(function, [constructor/regexp], [message]) 2467 | * 2468 | * Asserts that `function` will _not_ throw an error that is an instance of 2469 | * `constructor`, or alternately that it will not throw an error with message 2470 | * matching `regexp`. 2471 | * 2472 | * assert.doesNotThrow(fn, Error, 'function does not throw'); 2473 | * 2474 | * @name doesNotThrow 2475 | * @param {Function} function 2476 | * @param {ErrorConstructor} constructor 2477 | * @param {RegExp} regexp 2478 | * @param {String} message 2479 | * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types 2480 | * @api public 2481 | */ 2482 | 2483 | assert.doesNotThrow = function (fn, type, msg) { 2484 | if ('string' === typeof type) { 2485 | msg = type; 2486 | type = null; 2487 | } 2488 | 2489 | new Assertion(fn, msg).to.not.Throw(type); 2490 | }; 2491 | 2492 | /** 2493 | * ### .operator(val1, operator, val2, [message]) 2494 | * 2495 | * Compares two values using `operator`. 2496 | * 2497 | * assert.operator(1, '<', 2, 'everything is ok'); 2498 | * assert.operator(1, '>', 2, 'this will fail'); 2499 | * 2500 | * @name operator 2501 | * @param {Mixed} val1 2502 | * @param {String} operator 2503 | * @param {Mixed} val2 2504 | * @param {String} message 2505 | * @api public 2506 | */ 2507 | 2508 | assert.operator = function (val, operator, val2, msg) { 2509 | if (!~['==', '===', '>', '>=', '<', '<=', '!=', '!=='].indexOf(operator)) { 2510 | throw new Error('Invalid operator "' + operator + '"'); 2511 | } 2512 | var test = new Assertion(eval(val + operator + val2), msg); 2513 | test.assert( 2514 | true === flag(test, 'object') 2515 | , 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) 2516 | , 'expected ' + util.inspect(val) + ' to not be ' + operator + ' ' + util.inspect(val2) ); 2517 | }; 2518 | 2519 | /** 2520 | * ### .closeTo(actual, expected, delta, [message]) 2521 | * 2522 | * Asserts that the target is equal `expected`, to within a +/- `delta` range. 2523 | * 2524 | * assert.closeTo(1.5, 1, 0.5, 'numbers are close'); 2525 | * 2526 | * @name closeTo 2527 | * @param {Number} actual 2528 | * @param {Number} expected 2529 | * @param {Number} delta 2530 | * @param {String} message 2531 | * @api public 2532 | */ 2533 | 2534 | assert.closeTo = function (act, exp, delta, msg) { 2535 | new Assertion(act, msg).to.be.closeTo(exp, delta); 2536 | }; 2537 | 2538 | /*! 2539 | * Undocumented / untested 2540 | */ 2541 | 2542 | assert.ifError = function (val, msg) { 2543 | new Assertion(val, msg).to.not.be.ok; 2544 | }; 2545 | 2546 | /*! 2547 | * Aliases. 2548 | */ 2549 | 2550 | (function alias(name, as){ 2551 | assert[as] = assert[name]; 2552 | return alias; 2553 | }) 2554 | ('Throw', 'throw') 2555 | ('Throw', 'throws'); 2556 | }; 2557 | 2558 | }); // module: chai/interface/assert.js 2559 | 2560 | require.register("chai/interface/expect.js", function(module, exports, require){ 2561 | /*! 2562 | * chai 2563 | * Copyright(c) 2011-2013 Jake Luer 2564 | * MIT Licensed 2565 | */ 2566 | 2567 | module.exports = function (chai, util) { 2568 | chai.expect = function (val, message) { 2569 | return new chai.Assertion(val, message); 2570 | }; 2571 | }; 2572 | 2573 | 2574 | }); // module: chai/interface/expect.js 2575 | 2576 | require.register("chai/interface/should.js", function(module, exports, require){ 2577 | /*! 2578 | * chai 2579 | * Copyright(c) 2011-2013 Jake Luer 2580 | * MIT Licensed 2581 | */ 2582 | 2583 | module.exports = function (chai, util) { 2584 | var Assertion = chai.Assertion; 2585 | 2586 | function loadShould () { 2587 | // modify Object.prototype to have `should` 2588 | Object.defineProperty(Object.prototype, 'should', 2589 | { 2590 | set: function (value) { 2591 | // See https://github.com/chaijs/chai/issues/86: this makes 2592 | // `whatever.should = someValue` actually set `someValue`, which is 2593 | // especially useful for `global.should = require('chai').should()`. 2594 | // 2595 | // Note that we have to use [[DefineProperty]] instead of [[Put]] 2596 | // since otherwise we would trigger this very setter! 2597 | Object.defineProperty(this, 'should', { 2598 | value: value, 2599 | enumerable: true, 2600 | configurable: true, 2601 | writable: true 2602 | }); 2603 | } 2604 | , get: function(){ 2605 | if (this instanceof String || this instanceof Number) { 2606 | return new Assertion(this.constructor(this)); 2607 | } else if (this instanceof Boolean) { 2608 | return new Assertion(this == true); 2609 | } 2610 | return new Assertion(this); 2611 | } 2612 | , configurable: true 2613 | }); 2614 | 2615 | var should = {}; 2616 | 2617 | should.equal = function (val1, val2, msg) { 2618 | new Assertion(val1, msg).to.equal(val2); 2619 | }; 2620 | 2621 | should.Throw = function (fn, errt, errs, msg) { 2622 | new Assertion(fn, msg).to.Throw(errt, errs); 2623 | }; 2624 | 2625 | should.exist = function (val, msg) { 2626 | new Assertion(val, msg).to.exist; 2627 | } 2628 | 2629 | // negation 2630 | should.not = {} 2631 | 2632 | should.not.equal = function (val1, val2, msg) { 2633 | new Assertion(val1, msg).to.not.equal(val2); 2634 | }; 2635 | 2636 | should.not.Throw = function (fn, errt, errs, msg) { 2637 | new Assertion(fn, msg).to.not.Throw(errt, errs); 2638 | }; 2639 | 2640 | should.not.exist = function (val, msg) { 2641 | new Assertion(val, msg).to.not.exist; 2642 | } 2643 | 2644 | should['throw'] = should['Throw']; 2645 | should.not['throw'] = should.not['Throw']; 2646 | 2647 | return should; 2648 | }; 2649 | 2650 | chai.should = loadShould; 2651 | chai.Should = loadShould; 2652 | }; 2653 | 2654 | }); // module: chai/interface/should.js 2655 | 2656 | require.register("chai/utils/addChainableMethod.js", function(module, exports, require){ 2657 | /*! 2658 | * Chai - addChainingMethod utility 2659 | * Copyright(c) 2012-2013 Jake Luer 2660 | * MIT Licensed 2661 | */ 2662 | 2663 | /*! 2664 | * Module dependencies 2665 | */ 2666 | 2667 | var transferFlags = require('./transferFlags'); 2668 | 2669 | /*! 2670 | * Module variables 2671 | */ 2672 | 2673 | // Check whether `__proto__` is supported 2674 | var hasProtoSupport = '__proto__' in Object; 2675 | 2676 | // Without `__proto__` support, this module will need to add properties to a function. 2677 | // However, some Function.prototype methods cannot be overwritten, 2678 | // and there seems no easy cross-platform way to detect them (@see chaijs/chai/issues/69). 2679 | var excludeNames = /^(?:length|name|arguments|caller)$/; 2680 | 2681 | /** 2682 | * ### addChainableMethod (ctx, name, method, chainingBehavior) 2683 | * 2684 | * Adds a method to an object, such that the method can also be chained. 2685 | * 2686 | * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { 2687 | * var obj = utils.flag(this, 'object'); 2688 | * new chai.Assertion(obj).to.be.equal(str); 2689 | * }); 2690 | * 2691 | * Can also be accessed directly from `chai.Assertion`. 2692 | * 2693 | * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); 2694 | * 2695 | * The result can then be used as both a method assertion, executing both `method` and 2696 | * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. 2697 | * 2698 | * expect(fooStr).to.be.foo('bar'); 2699 | * expect(fooStr).to.be.foo.equal('foo'); 2700 | * 2701 | * @param {Object} ctx object to which the method is added 2702 | * @param {String} name of method to add 2703 | * @param {Function} method function to be used for `name`, when called 2704 | * @param {Function} chainingBehavior function to be called every time the property is accessed 2705 | * @name addChainableMethod 2706 | * @api public 2707 | */ 2708 | 2709 | module.exports = function (ctx, name, method, chainingBehavior) { 2710 | if (typeof chainingBehavior !== 'function') 2711 | chainingBehavior = function () { }; 2712 | 2713 | Object.defineProperty(ctx, name, 2714 | { get: function () { 2715 | chainingBehavior.call(this); 2716 | 2717 | var assert = function () { 2718 | var result = method.apply(this, arguments); 2719 | return result === undefined ? this : result; 2720 | }; 2721 | 2722 | // Use `__proto__` if available 2723 | if (hasProtoSupport) { 2724 | assert.__proto__ = this; 2725 | } 2726 | // Otherwise, redefine all properties (slow!) 2727 | else { 2728 | var asserterNames = Object.getOwnPropertyNames(ctx); 2729 | asserterNames.forEach(function (asserterName) { 2730 | if (!excludeNames.test(asserterName)) { 2731 | var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); 2732 | Object.defineProperty(assert, asserterName, pd); 2733 | } 2734 | }); 2735 | } 2736 | 2737 | transferFlags(this, assert); 2738 | return assert; 2739 | } 2740 | , configurable: true 2741 | }); 2742 | }; 2743 | 2744 | }); // module: chai/utils/addChainableMethod.js 2745 | 2746 | require.register("chai/utils/addMethod.js", function(module, exports, require){ 2747 | /*! 2748 | * Chai - addMethod utility 2749 | * Copyright(c) 2012-2013 Jake Luer 2750 | * MIT Licensed 2751 | */ 2752 | 2753 | /** 2754 | * ### .addMethod (ctx, name, method) 2755 | * 2756 | * Adds a method to the prototype of an object. 2757 | * 2758 | * utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { 2759 | * var obj = utils.flag(this, 'object'); 2760 | * new chai.Assertion(obj).to.be.equal(str); 2761 | * }); 2762 | * 2763 | * Can also be accessed directly from `chai.Assertion`. 2764 | * 2765 | * chai.Assertion.addMethod('foo', fn); 2766 | * 2767 | * Then can be used as any other assertion. 2768 | * 2769 | * expect(fooStr).to.be.foo('bar'); 2770 | * 2771 | * @param {Object} ctx object to which the method is added 2772 | * @param {String} name of method to add 2773 | * @param {Function} method function to be used for name 2774 | * @name addMethod 2775 | * @api public 2776 | */ 2777 | 2778 | module.exports = function (ctx, name, method) { 2779 | ctx[name] = function () { 2780 | var result = method.apply(this, arguments); 2781 | return result === undefined ? this : result; 2782 | }; 2783 | }; 2784 | 2785 | }); // module: chai/utils/addMethod.js 2786 | 2787 | require.register("chai/utils/addProperty.js", function(module, exports, require){ 2788 | /*! 2789 | * Chai - addProperty utility 2790 | * Copyright(c) 2012-2013 Jake Luer 2791 | * MIT Licensed 2792 | */ 2793 | 2794 | /** 2795 | * ### addProperty (ctx, name, getter) 2796 | * 2797 | * Adds a property to the prototype of an object. 2798 | * 2799 | * utils.addProperty(chai.Assertion.prototype, 'foo', function () { 2800 | * var obj = utils.flag(this, 'object'); 2801 | * new chai.Assertion(obj).to.be.instanceof(Foo); 2802 | * }); 2803 | * 2804 | * Can also be accessed directly from `chai.Assertion`. 2805 | * 2806 | * chai.Assertion.addProperty('foo', fn); 2807 | * 2808 | * Then can be used as any other assertion. 2809 | * 2810 | * expect(myFoo).to.be.foo; 2811 | * 2812 | * @param {Object} ctx object to which the property is added 2813 | * @param {String} name of property to add 2814 | * @param {Function} getter function to be used for name 2815 | * @name addProperty 2816 | * @api public 2817 | */ 2818 | 2819 | module.exports = function (ctx, name, getter) { 2820 | Object.defineProperty(ctx, name, 2821 | { get: function () { 2822 | var result = getter.call(this); 2823 | return result === undefined ? this : result; 2824 | } 2825 | , configurable: true 2826 | }); 2827 | }; 2828 | 2829 | }); // module: chai/utils/addProperty.js 2830 | 2831 | require.register("chai/utils/eql.js", function(module, exports, require){ 2832 | // This is (almost) directly from Node.js assert 2833 | // https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/assert.js 2834 | 2835 | module.exports = _deepEqual; 2836 | 2837 | var getEnumerableProperties = require('./getEnumerableProperties'); 2838 | 2839 | // for the browser 2840 | var Buffer; 2841 | try { 2842 | Buffer = require('buffer').Buffer; 2843 | } catch (ex) { 2844 | Buffer = { 2845 | isBuffer: function () { return false; } 2846 | }; 2847 | } 2848 | 2849 | function _deepEqual(actual, expected, memos) { 2850 | 2851 | // 7.1. All identical values are equivalent, as determined by ===. 2852 | if (actual === expected) { 2853 | return true; 2854 | 2855 | } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { 2856 | if (actual.length != expected.length) return false; 2857 | 2858 | for (var i = 0; i < actual.length; i++) { 2859 | if (actual[i] !== expected[i]) return false; 2860 | } 2861 | 2862 | return true; 2863 | 2864 | // 7.2. If the expected value is a Date object, the actual value is 2865 | // equivalent if it is also a Date object that refers to the same time. 2866 | } else if (actual instanceof Date && expected instanceof Date) { 2867 | return actual.getTime() === expected.getTime(); 2868 | 2869 | // 7.3. Other pairs that do not both pass typeof value == 'object', 2870 | // equivalence is determined by ==. 2871 | } else if (typeof actual != 'object' && typeof expected != 'object') { 2872 | return actual === expected; 2873 | 2874 | // 7.4. For all other Object pairs, including Array objects, equivalence is 2875 | // determined by having the same number of owned properties (as verified 2876 | // with Object.prototype.hasOwnProperty.call), the same set of keys 2877 | // (although not necessarily the same order), equivalent values for every 2878 | // corresponding key, and an identical 'prototype' property. Note: this 2879 | // accounts for both named and indexed properties on Arrays. 2880 | } else { 2881 | return objEquiv(actual, expected, memos); 2882 | } 2883 | } 2884 | 2885 | function isUndefinedOrNull(value) { 2886 | return value === null || value === undefined; 2887 | } 2888 | 2889 | function isArguments(object) { 2890 | return Object.prototype.toString.call(object) == '[object Arguments]'; 2891 | } 2892 | 2893 | function objEquiv(a, b, memos) { 2894 | if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) 2895 | return false; 2896 | 2897 | // an identical 'prototype' property. 2898 | if (a.prototype !== b.prototype) return false; 2899 | 2900 | // check if we have already compared a and b 2901 | var i; 2902 | if (memos) { 2903 | for(i = 0; i < memos.length; i++) { 2904 | if ((memos[i][0] === a && memos[i][1] === b) || 2905 | (memos[i][0] === b && memos[i][1] === a)) 2906 | return true; 2907 | } 2908 | } else { 2909 | memos = []; 2910 | } 2911 | 2912 | //~~~I've managed to break Object.keys through screwy arguments passing. 2913 | // Converting to array solves the problem. 2914 | if (isArguments(a)) { 2915 | if (!isArguments(b)) { 2916 | return false; 2917 | } 2918 | a = pSlice.call(a); 2919 | b = pSlice.call(b); 2920 | return _deepEqual(a, b, memos); 2921 | } 2922 | try { 2923 | var ka = getEnumerableProperties(a), 2924 | kb = getEnumerableProperties(b), 2925 | key; 2926 | } catch (e) {//happens when one is a string literal and the other isn't 2927 | return false; 2928 | } 2929 | 2930 | // having the same number of owned properties (keys incorporates 2931 | // hasOwnProperty) 2932 | if (ka.length != kb.length) 2933 | return false; 2934 | 2935 | //the same set of keys (although not necessarily the same order), 2936 | ka.sort(); 2937 | kb.sort(); 2938 | //~~~cheap key test 2939 | for (i = ka.length - 1; i >= 0; i--) { 2940 | if (ka[i] != kb[i]) 2941 | return false; 2942 | } 2943 | 2944 | // remember objects we have compared to guard against circular references 2945 | memos.push([ a, b ]); 2946 | 2947 | //equivalent values for every corresponding key, and 2948 | //~~~possibly expensive deep test 2949 | for (i = ka.length - 1; i >= 0; i--) { 2950 | key = ka[i]; 2951 | if (!_deepEqual(a[key], b[key], memos)) return false; 2952 | } 2953 | 2954 | return true; 2955 | } 2956 | 2957 | }); // module: chai/utils/eql.js 2958 | 2959 | require.register("chai/utils/flag.js", function(module, exports, require){ 2960 | /*! 2961 | * Chai - flag utility 2962 | * Copyright(c) 2012-2013 Jake Luer 2963 | * MIT Licensed 2964 | */ 2965 | 2966 | /** 2967 | * ### flag(object ,key, [value]) 2968 | * 2969 | * Get or set a flag value on an object. If a 2970 | * value is provided it will be set, else it will 2971 | * return the currently set value or `undefined` if 2972 | * the value is not set. 2973 | * 2974 | * utils.flag(this, 'foo', 'bar'); // setter 2975 | * utils.flag(this, 'foo'); // getter, returns `bar` 2976 | * 2977 | * @param {Object} object (constructed Assertion 2978 | * @param {String} key 2979 | * @param {Mixed} value (optional) 2980 | * @name flag 2981 | * @api private 2982 | */ 2983 | 2984 | module.exports = function (obj, key, value) { 2985 | var flags = obj.__flags || (obj.__flags = Object.create(null)); 2986 | if (arguments.length === 3) { 2987 | flags[key] = value; 2988 | } else { 2989 | return flags[key]; 2990 | } 2991 | }; 2992 | 2993 | }); // module: chai/utils/flag.js 2994 | 2995 | require.register("chai/utils/getActual.js", function(module, exports, require){ 2996 | /*! 2997 | * Chai - getActual utility 2998 | * Copyright(c) 2012-2013 Jake Luer 2999 | * MIT Licensed 3000 | */ 3001 | 3002 | /** 3003 | * # getActual(object, [actual]) 3004 | * 3005 | * Returns the `actual` value for an Assertion 3006 | * 3007 | * @param {Object} object (constructed Assertion) 3008 | * @param {Arguments} chai.Assertion.prototype.assert arguments 3009 | */ 3010 | 3011 | module.exports = function (obj, args) { 3012 | var actual = args[4]; 3013 | return 'undefined' !== typeof actual ? actual : obj._obj; 3014 | }; 3015 | 3016 | }); // module: chai/utils/getActual.js 3017 | 3018 | require.register("chai/utils/getEnumerableProperties.js", function(module, exports, require){ 3019 | /*! 3020 | * Chai - getEnumerableProperties utility 3021 | * Copyright(c) 2012-2013 Jake Luer 3022 | * MIT Licensed 3023 | */ 3024 | 3025 | /** 3026 | * ### .getEnumerableProperties(object) 3027 | * 3028 | * This allows the retrieval of enumerable property names of an object, 3029 | * inherited or not. 3030 | * 3031 | * @param {Object} object 3032 | * @returns {Array} 3033 | * @name getEnumerableProperties 3034 | * @api public 3035 | */ 3036 | 3037 | module.exports = function getEnumerableProperties(object) { 3038 | var result = []; 3039 | for (var name in object) { 3040 | result.push(name); 3041 | } 3042 | return result; 3043 | }; 3044 | 3045 | }); // module: chai/utils/getEnumerableProperties.js 3046 | 3047 | require.register("chai/utils/getMessage.js", function(module, exports, require){ 3048 | /*! 3049 | * Chai - message composition utility 3050 | * Copyright(c) 2012-2013 Jake Luer 3051 | * MIT Licensed 3052 | */ 3053 | 3054 | /*! 3055 | * Module dependancies 3056 | */ 3057 | 3058 | var flag = require('./flag') 3059 | , getActual = require('./getActual') 3060 | , inspect = require('./inspect') 3061 | , objDisplay = require('./objDisplay'); 3062 | 3063 | /** 3064 | * ### .getMessage(object, message, negateMessage) 3065 | * 3066 | * Construct the error message based on flags 3067 | * and template tags. Template tags will return 3068 | * a stringified inspection of the object referenced. 3069 | * 3070 | * Messsage template tags: 3071 | * - `#{this}` current asserted object 3072 | * - `#{act}` actual value 3073 | * - `#{exp}` expected value 3074 | * 3075 | * @param {Object} object (constructed Assertion) 3076 | * @param {Arguments} chai.Assertion.prototype.assert arguments 3077 | * @name getMessage 3078 | * @api public 3079 | */ 3080 | 3081 | module.exports = function (obj, args) { 3082 | var negate = flag(obj, 'negate') 3083 | , val = flag(obj, 'object') 3084 | , expected = args[3] 3085 | , actual = getActual(obj, args) 3086 | , msg = negate ? args[2] : args[1] 3087 | , flagMsg = flag(obj, 'message'); 3088 | 3089 | msg = msg || ''; 3090 | msg = msg 3091 | .replace(/#{this}/g, objDisplay(val)) 3092 | .replace(/#{act}/g, objDisplay(actual)) 3093 | .replace(/#{exp}/g, objDisplay(expected)); 3094 | 3095 | return flagMsg ? flagMsg + ': ' + msg : msg; 3096 | }; 3097 | 3098 | }); // module: chai/utils/getMessage.js 3099 | 3100 | require.register("chai/utils/getName.js", function(module, exports, require){ 3101 | /*! 3102 | * Chai - getName utility 3103 | * Copyright(c) 2012-2013 Jake Luer 3104 | * MIT Licensed 3105 | */ 3106 | 3107 | /** 3108 | * # getName(func) 3109 | * 3110 | * Gets the name of a function, in a cross-browser way. 3111 | * 3112 | * @param {Function} a function (usually a constructor) 3113 | */ 3114 | 3115 | module.exports = function (func) { 3116 | if (func.name) return func.name; 3117 | 3118 | var match = /^\s?function ([^(]*)\(/.exec(func); 3119 | return match && match[1] ? match[1] : ""; 3120 | }; 3121 | 3122 | }); // module: chai/utils/getName.js 3123 | 3124 | require.register("chai/utils/getPathValue.js", function(module, exports, require){ 3125 | /*! 3126 | * Chai - getPathValue utility 3127 | * Copyright(c) 2012-2013 Jake Luer 3128 | * @see https://github.com/logicalparadox/filtr 3129 | * MIT Licensed 3130 | */ 3131 | 3132 | /** 3133 | * ### .getPathValue(path, object) 3134 | * 3135 | * This allows the retrieval of values in an 3136 | * object given a string path. 3137 | * 3138 | * var obj = { 3139 | * prop1: { 3140 | * arr: ['a', 'b', 'c'] 3141 | * , str: 'Hello' 3142 | * } 3143 | * , prop2: { 3144 | * arr: [ { nested: 'Universe' } ] 3145 | * , str: 'Hello again!' 3146 | * } 3147 | * } 3148 | * 3149 | * The following would be the results. 3150 | * 3151 | * getPathValue('prop1.str', obj); // Hello 3152 | * getPathValue('prop1.att[2]', obj); // b 3153 | * getPathValue('prop2.arr[0].nested', obj); // Universe 3154 | * 3155 | * @param {String} path 3156 | * @param {Object} object 3157 | * @returns {Object} value or `undefined` 3158 | * @name getPathValue 3159 | * @api public 3160 | */ 3161 | 3162 | var getPathValue = module.exports = function (path, obj) { 3163 | var parsed = parsePath(path); 3164 | return _getPathValue(parsed, obj); 3165 | }; 3166 | 3167 | /*! 3168 | * ## parsePath(path) 3169 | * 3170 | * Helper function used to parse string object 3171 | * paths. Use in conjunction with `_getPathValue`. 3172 | * 3173 | * var parsed = parsePath('myobject.property.subprop'); 3174 | * 3175 | * ### Paths: 3176 | * 3177 | * * Can be as near infinitely deep and nested 3178 | * * Arrays are also valid using the formal `myobject.document[3].property`. 3179 | * 3180 | * @param {String} path 3181 | * @returns {Object} parsed 3182 | * @api private 3183 | */ 3184 | 3185 | function parsePath (path) { 3186 | var str = path.replace(/\[/g, '.[') 3187 | , parts = str.match(/(\\\.|[^.]+?)+/g); 3188 | return parts.map(function (value) { 3189 | var re = /\[(\d+)\]$/ 3190 | , mArr = re.exec(value) 3191 | if (mArr) return { i: parseFloat(mArr[1]) }; 3192 | else return { p: value }; 3193 | }); 3194 | }; 3195 | 3196 | /*! 3197 | * ## _getPathValue(parsed, obj) 3198 | * 3199 | * Helper companion function for `.parsePath` that returns 3200 | * the value located at the parsed address. 3201 | * 3202 | * var value = getPathValue(parsed, obj); 3203 | * 3204 | * @param {Object} parsed definition from `parsePath`. 3205 | * @param {Object} object to search against 3206 | * @returns {Object|Undefined} value 3207 | * @api private 3208 | */ 3209 | 3210 | function _getPathValue (parsed, obj) { 3211 | var tmp = obj 3212 | , res; 3213 | for (var i = 0, l = parsed.length; i < l; i++) { 3214 | var part = parsed[i]; 3215 | if (tmp) { 3216 | if ('undefined' !== typeof part.p) 3217 | tmp = tmp[part.p]; 3218 | else if ('undefined' !== typeof part.i) 3219 | tmp = tmp[part.i]; 3220 | if (i == (l - 1)) res = tmp; 3221 | } else { 3222 | res = undefined; 3223 | } 3224 | } 3225 | return res; 3226 | }; 3227 | 3228 | }); // module: chai/utils/getPathValue.js 3229 | 3230 | require.register("chai/utils/getProperties.js", function(module, exports, require){ 3231 | /*! 3232 | * Chai - getProperties utility 3233 | * Copyright(c) 2012-2013 Jake Luer 3234 | * MIT Licensed 3235 | */ 3236 | 3237 | /** 3238 | * ### .getProperties(object) 3239 | * 3240 | * This allows the retrieval of property names of an object, enumerable or not, 3241 | * inherited or not. 3242 | * 3243 | * @param {Object} object 3244 | * @returns {Array} 3245 | * @name getProperties 3246 | * @api public 3247 | */ 3248 | 3249 | module.exports = function getProperties(object) { 3250 | var result = Object.getOwnPropertyNames(subject); 3251 | 3252 | function addProperty(property) { 3253 | if (result.indexOf(property) === -1) { 3254 | result.push(property); 3255 | } 3256 | } 3257 | 3258 | var proto = Object.getPrototypeOf(subject); 3259 | while (proto !== null) { 3260 | Object.getOwnPropertyNames(proto).forEach(addProperty); 3261 | proto = Object.getPrototypeOf(proto); 3262 | } 3263 | 3264 | return result; 3265 | }; 3266 | 3267 | }); // module: chai/utils/getProperties.js 3268 | 3269 | require.register("chai/utils/index.js", function(module, exports, require){ 3270 | /*! 3271 | * chai 3272 | * Copyright(c) 2011 Jake Luer 3273 | * MIT Licensed 3274 | */ 3275 | 3276 | /*! 3277 | * Main exports 3278 | */ 3279 | 3280 | var exports = module.exports = {}; 3281 | 3282 | /*! 3283 | * test utility 3284 | */ 3285 | 3286 | exports.test = require('./test'); 3287 | 3288 | /*! 3289 | * type utility 3290 | */ 3291 | 3292 | exports.type = require('./type'); 3293 | 3294 | /*! 3295 | * message utility 3296 | */ 3297 | 3298 | exports.getMessage = require('./getMessage'); 3299 | 3300 | /*! 3301 | * actual utility 3302 | */ 3303 | 3304 | exports.getActual = require('./getActual'); 3305 | 3306 | /*! 3307 | * Inspect util 3308 | */ 3309 | 3310 | exports.inspect = require('./inspect'); 3311 | 3312 | /*! 3313 | * Object Display util 3314 | */ 3315 | 3316 | exports.objDisplay = require('./objDisplay'); 3317 | 3318 | /*! 3319 | * Flag utility 3320 | */ 3321 | 3322 | exports.flag = require('./flag'); 3323 | 3324 | /*! 3325 | * Flag transferring utility 3326 | */ 3327 | 3328 | exports.transferFlags = require('./transferFlags'); 3329 | 3330 | /*! 3331 | * Deep equal utility 3332 | */ 3333 | 3334 | exports.eql = require('./eql'); 3335 | 3336 | /*! 3337 | * Deep path value 3338 | */ 3339 | 3340 | exports.getPathValue = require('./getPathValue'); 3341 | 3342 | /*! 3343 | * Function name 3344 | */ 3345 | 3346 | exports.getName = require('./getName'); 3347 | 3348 | /*! 3349 | * add Property 3350 | */ 3351 | 3352 | exports.addProperty = require('./addProperty'); 3353 | 3354 | /*! 3355 | * add Method 3356 | */ 3357 | 3358 | exports.addMethod = require('./addMethod'); 3359 | 3360 | /*! 3361 | * overwrite Property 3362 | */ 3363 | 3364 | exports.overwriteProperty = require('./overwriteProperty'); 3365 | 3366 | /*! 3367 | * overwrite Method 3368 | */ 3369 | 3370 | exports.overwriteMethod = require('./overwriteMethod'); 3371 | 3372 | /*! 3373 | * Add a chainable method 3374 | */ 3375 | 3376 | exports.addChainableMethod = require('./addChainableMethod'); 3377 | 3378 | 3379 | }); // module: chai/utils/index.js 3380 | 3381 | require.register("chai/utils/inspect.js", function(module, exports, require){ 3382 | // This is (almost) directly from Node.js utils 3383 | // https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js 3384 | 3385 | var getName = require('./getName'); 3386 | var getProperties = require('./getProperties'); 3387 | var getEnumerableProperties = require('./getEnumerableProperties'); 3388 | 3389 | module.exports = inspect; 3390 | 3391 | /** 3392 | * Echos the value of a value. Trys to print the value out 3393 | * in the best way possible given the different types. 3394 | * 3395 | * @param {Object} obj The object to print out. 3396 | * @param {Boolean} showHidden Flag that shows hidden (not enumerable) 3397 | * properties of objects. 3398 | * @param {Number} depth Depth in which to descend in object. Default is 2. 3399 | * @param {Boolean} colors Flag to turn on ANSI escape codes to color the 3400 | * output. Default is false (no coloring). 3401 | */ 3402 | function inspect(obj, showHidden, depth, colors) { 3403 | var ctx = { 3404 | showHidden: showHidden, 3405 | seen: [], 3406 | stylize: function (str) { return str; } 3407 | }; 3408 | return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); 3409 | } 3410 | 3411 | // https://gist.github.com/1044128/ 3412 | var getOuterHTML = function(element) { 3413 | if ('outerHTML' in element) return element.outerHTML; 3414 | var ns = "http://www.w3.org/1999/xhtml"; 3415 | var container = document.createElementNS(ns, '_'); 3416 | var elemProto = (window.HTMLElement || window.Element).prototype; 3417 | var xmlSerializer = new XMLSerializer(); 3418 | var html; 3419 | if (document.xmlVersion) { 3420 | return xmlSerializer.serializeToString(element); 3421 | } else { 3422 | container.appendChild(element.cloneNode(false)); 3423 | html = container.innerHTML.replace('><', '>' + element.innerHTML + '<'); 3424 | container.innerHTML = ''; 3425 | return html; 3426 | } 3427 | }; 3428 | 3429 | // Returns true if object is a DOM element. 3430 | var isDOMElement = function (object) { 3431 | if (typeof HTMLElement === 'object') { 3432 | return object instanceof HTMLElement; 3433 | } else { 3434 | return object && 3435 | typeof object === 'object' && 3436 | object.nodeType === 1 && 3437 | typeof object.nodeName === 'string'; 3438 | } 3439 | }; 3440 | 3441 | function formatValue(ctx, value, recurseTimes) { 3442 | // Provide a hook for user-specified inspect functions. 3443 | // Check that value is an object with an inspect function on it 3444 | if (value && typeof value.inspect === 'function' && 3445 | // Filter out the util module, it's inspect function is special 3446 | value.inspect !== exports.inspect && 3447 | // Also filter out any prototype objects using the circular check. 3448 | !(value.constructor && value.constructor.prototype === value)) { 3449 | return value.inspect(recurseTimes); 3450 | } 3451 | 3452 | // Primitive types cannot have properties 3453 | var primitive = formatPrimitive(ctx, value); 3454 | if (primitive) { 3455 | return primitive; 3456 | } 3457 | 3458 | // If it's DOM elem, get outer HTML. 3459 | if (isDOMElement(value)) { 3460 | return getOuterHTML(value); 3461 | } 3462 | 3463 | // Look up the keys of the object. 3464 | var visibleKeys = getEnumerableProperties(value); 3465 | var keys = ctx.showHidden ? getProperties(value) : visibleKeys; 3466 | 3467 | // Some type of object without properties can be shortcutted. 3468 | // In IE, errors have a single `stack` property, or if they are vanilla `Error`, 3469 | // a `stack` plus `description` property; ignore those for consistency. 3470 | if (keys.length === 0 || (isError(value) && ( 3471 | (keys.length === 1 && keys[0] === 'stack') || 3472 | (keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') 3473 | ))) { 3474 | if (typeof value === 'function') { 3475 | var name = getName(value); 3476 | var nameSuffix = name ? ': ' + name : ''; 3477 | return ctx.stylize('[Function' + nameSuffix + ']', 'special'); 3478 | } 3479 | if (isRegExp(value)) { 3480 | return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 3481 | } 3482 | if (isDate(value)) { 3483 | return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); 3484 | } 3485 | if (isError(value)) { 3486 | return formatError(value); 3487 | } 3488 | } 3489 | 3490 | var base = '', array = false, braces = ['{', '}']; 3491 | 3492 | // Make Array say that they are Array 3493 | if (isArray(value)) { 3494 | array = true; 3495 | braces = ['[', ']']; 3496 | } 3497 | 3498 | // Make functions say that they are functions 3499 | if (typeof value === 'function') { 3500 | var name = getName(value); 3501 | var nameSuffix = name ? ': ' + name : ''; 3502 | base = ' [Function' + nameSuffix + ']'; 3503 | } 3504 | 3505 | // Make RegExps say that they are RegExps 3506 | if (isRegExp(value)) { 3507 | base = ' ' + RegExp.prototype.toString.call(value); 3508 | } 3509 | 3510 | // Make dates with properties first say the date 3511 | if (isDate(value)) { 3512 | base = ' ' + Date.prototype.toUTCString.call(value); 3513 | } 3514 | 3515 | // Make error with message first say the error 3516 | if (isError(value)) { 3517 | return formatError(value); 3518 | } 3519 | 3520 | if (keys.length === 0 && (!array || value.length == 0)) { 3521 | return braces[0] + base + braces[1]; 3522 | } 3523 | 3524 | if (recurseTimes < 0) { 3525 | if (isRegExp(value)) { 3526 | return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); 3527 | } else { 3528 | return ctx.stylize('[Object]', 'special'); 3529 | } 3530 | } 3531 | 3532 | ctx.seen.push(value); 3533 | 3534 | var output; 3535 | if (array) { 3536 | output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); 3537 | } else { 3538 | output = keys.map(function(key) { 3539 | return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); 3540 | }); 3541 | } 3542 | 3543 | ctx.seen.pop(); 3544 | 3545 | return reduceToSingleString(output, base, braces); 3546 | } 3547 | 3548 | 3549 | function formatPrimitive(ctx, value) { 3550 | switch (typeof value) { 3551 | case 'undefined': 3552 | return ctx.stylize('undefined', 'undefined'); 3553 | 3554 | case 'string': 3555 | var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') 3556 | .replace(/'/g, "\\'") 3557 | .replace(/\\"/g, '"') + '\''; 3558 | return ctx.stylize(simple, 'string'); 3559 | 3560 | case 'number': 3561 | return ctx.stylize('' + value, 'number'); 3562 | 3563 | case 'boolean': 3564 | return ctx.stylize('' + value, 'boolean'); 3565 | } 3566 | // For some reason typeof null is "object", so special case here. 3567 | if (value === null) { 3568 | return ctx.stylize('null', 'null'); 3569 | } 3570 | } 3571 | 3572 | 3573 | function formatError(value) { 3574 | return '[' + Error.prototype.toString.call(value) + ']'; 3575 | } 3576 | 3577 | 3578 | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { 3579 | var output = []; 3580 | for (var i = 0, l = value.length; i < l; ++i) { 3581 | if (Object.prototype.hasOwnProperty.call(value, String(i))) { 3582 | output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 3583 | String(i), true)); 3584 | } else { 3585 | output.push(''); 3586 | } 3587 | } 3588 | keys.forEach(function(key) { 3589 | if (!key.match(/^\d+$/)) { 3590 | output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, 3591 | key, true)); 3592 | } 3593 | }); 3594 | return output; 3595 | } 3596 | 3597 | 3598 | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { 3599 | var name, str; 3600 | if (value.__lookupGetter__) { 3601 | if (value.__lookupGetter__(key)) { 3602 | if (value.__lookupSetter__(key)) { 3603 | str = ctx.stylize('[Getter/Setter]', 'special'); 3604 | } else { 3605 | str = ctx.stylize('[Getter]', 'special'); 3606 | } 3607 | } else { 3608 | if (value.__lookupSetter__(key)) { 3609 | str = ctx.stylize('[Setter]', 'special'); 3610 | } 3611 | } 3612 | } 3613 | if (visibleKeys.indexOf(key) < 0) { 3614 | name = '[' + key + ']'; 3615 | } 3616 | if (!str) { 3617 | if (ctx.seen.indexOf(value[key]) < 0) { 3618 | if (recurseTimes === null) { 3619 | str = formatValue(ctx, value[key], null); 3620 | } else { 3621 | str = formatValue(ctx, value[key], recurseTimes - 1); 3622 | } 3623 | if (str.indexOf('\n') > -1) { 3624 | if (array) { 3625 | str = str.split('\n').map(function(line) { 3626 | return ' ' + line; 3627 | }).join('\n').substr(2); 3628 | } else { 3629 | str = '\n' + str.split('\n').map(function(line) { 3630 | return ' ' + line; 3631 | }).join('\n'); 3632 | } 3633 | } 3634 | } else { 3635 | str = ctx.stylize('[Circular]', 'special'); 3636 | } 3637 | } 3638 | if (typeof name === 'undefined') { 3639 | if (array && key.match(/^\d+$/)) { 3640 | return str; 3641 | } 3642 | name = JSON.stringify('' + key); 3643 | if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { 3644 | name = name.substr(1, name.length - 2); 3645 | name = ctx.stylize(name, 'name'); 3646 | } else { 3647 | name = name.replace(/'/g, "\\'") 3648 | .replace(/\\"/g, '"') 3649 | .replace(/(^"|"$)/g, "'"); 3650 | name = ctx.stylize(name, 'string'); 3651 | } 3652 | } 3653 | 3654 | return name + ': ' + str; 3655 | } 3656 | 3657 | 3658 | function reduceToSingleString(output, base, braces) { 3659 | var numLinesEst = 0; 3660 | var length = output.reduce(function(prev, cur) { 3661 | numLinesEst++; 3662 | if (cur.indexOf('\n') >= 0) numLinesEst++; 3663 | return prev + cur.length + 1; 3664 | }, 0); 3665 | 3666 | if (length > 60) { 3667 | return braces[0] + 3668 | (base === '' ? '' : base + '\n ') + 3669 | ' ' + 3670 | output.join(',\n ') + 3671 | ' ' + 3672 | braces[1]; 3673 | } 3674 | 3675 | return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; 3676 | } 3677 | 3678 | function isArray(ar) { 3679 | return Array.isArray(ar) || 3680 | (typeof ar === 'object' && objectToString(ar) === '[object Array]'); 3681 | } 3682 | 3683 | function isRegExp(re) { 3684 | return typeof re === 'object' && objectToString(re) === '[object RegExp]'; 3685 | } 3686 | 3687 | function isDate(d) { 3688 | return typeof d === 'object' && objectToString(d) === '[object Date]'; 3689 | } 3690 | 3691 | function isError(e) { 3692 | return typeof e === 'object' && objectToString(e) === '[object Error]'; 3693 | } 3694 | 3695 | function objectToString(o) { 3696 | return Object.prototype.toString.call(o); 3697 | } 3698 | 3699 | }); // module: chai/utils/inspect.js 3700 | 3701 | require.register("chai/utils/objDisplay.js", function(module, exports, require){ 3702 | /*! 3703 | * Chai - flag utility 3704 | * Copyright(c) 2012-2013 Jake Luer 3705 | * MIT Licensed 3706 | */ 3707 | 3708 | /*! 3709 | * Module dependancies 3710 | */ 3711 | 3712 | var inspect = require('./inspect'); 3713 | 3714 | /** 3715 | * ### .objDisplay (object) 3716 | * 3717 | * Determines if an object or an array matches 3718 | * criteria to be inspected in-line for error 3719 | * messages or should be truncated. 3720 | * 3721 | * @param {Mixed} javascript object to inspect 3722 | * @name objDisplay 3723 | * @api public 3724 | */ 3725 | 3726 | module.exports = function (obj) { 3727 | var str = inspect(obj) 3728 | , type = Object.prototype.toString.call(obj); 3729 | 3730 | if (str.length >= 40) { 3731 | if (type === '[object Function]') { 3732 | return !obj.name || obj.name === '' 3733 | ? '[Function]' 3734 | : '[Function: ' + obj.name + ']'; 3735 | } else if (type === '[object Array]') { 3736 | return '[ Array(' + obj.length + ') ]'; 3737 | } else if (type === '[object Object]') { 3738 | var keys = Object.keys(obj) 3739 | , kstr = keys.length > 2 3740 | ? keys.splice(0, 2).join(', ') + ', ...' 3741 | : keys.join(', '); 3742 | return '{ Object (' + kstr + ') }'; 3743 | } else { 3744 | return str; 3745 | } 3746 | } else { 3747 | return str; 3748 | } 3749 | }; 3750 | 3751 | }); // module: chai/utils/objDisplay.js 3752 | 3753 | require.register("chai/utils/overwriteMethod.js", function(module, exports, require){ 3754 | /*! 3755 | * Chai - overwriteMethod utility 3756 | * Copyright(c) 2012-2013 Jake Luer 3757 | * MIT Licensed 3758 | */ 3759 | 3760 | /** 3761 | * ### overwriteMethod (ctx, name, fn) 3762 | * 3763 | * Overwites an already existing method and provides 3764 | * access to previous function. Must return function 3765 | * to be used for name. 3766 | * 3767 | * utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { 3768 | * return function (str) { 3769 | * var obj = utils.flag(this, 'object'); 3770 | * if (obj instanceof Foo) { 3771 | * new chai.Assertion(obj.value).to.equal(str); 3772 | * } else { 3773 | * _super.apply(this, arguments); 3774 | * } 3775 | * } 3776 | * }); 3777 | * 3778 | * Can also be accessed directly from `chai.Assertion`. 3779 | * 3780 | * chai.Assertion.overwriteMethod('foo', fn); 3781 | * 3782 | * Then can be used as any other assertion. 3783 | * 3784 | * expect(myFoo).to.equal('bar'); 3785 | * 3786 | * @param {Object} ctx object whose method is to be overwritten 3787 | * @param {String} name of method to overwrite 3788 | * @param {Function} method function that returns a function to be used for name 3789 | * @name overwriteMethod 3790 | * @api public 3791 | */ 3792 | 3793 | module.exports = function (ctx, name, method) { 3794 | var _method = ctx[name] 3795 | , _super = function () { return this; }; 3796 | 3797 | if (_method && 'function' === typeof _method) 3798 | _super = _method; 3799 | 3800 | ctx[name] = function () { 3801 | var result = method(_super).apply(this, arguments); 3802 | return result === undefined ? this : result; 3803 | } 3804 | }; 3805 | 3806 | }); // module: chai/utils/overwriteMethod.js 3807 | 3808 | require.register("chai/utils/overwriteProperty.js", function(module, exports, require){ 3809 | /*! 3810 | * Chai - overwriteProperty utility 3811 | * Copyright(c) 2012-2013 Jake Luer 3812 | * MIT Licensed 3813 | */ 3814 | 3815 | /** 3816 | * ### overwriteProperty (ctx, name, fn) 3817 | * 3818 | * Overwites an already existing property getter and provides 3819 | * access to previous value. Must return function to use as getter. 3820 | * 3821 | * utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { 3822 | * return function () { 3823 | * var obj = utils.flag(this, 'object'); 3824 | * if (obj instanceof Foo) { 3825 | * new chai.Assertion(obj.name).to.equal('bar'); 3826 | * } else { 3827 | * _super.call(this); 3828 | * } 3829 | * } 3830 | * }); 3831 | * 3832 | * 3833 | * Can also be accessed directly from `chai.Assertion`. 3834 | * 3835 | * chai.Assertion.overwriteProperty('foo', fn); 3836 | * 3837 | * Then can be used as any other assertion. 3838 | * 3839 | * expect(myFoo).to.be.ok; 3840 | * 3841 | * @param {Object} ctx object whose property is to be overwritten 3842 | * @param {String} name of property to overwrite 3843 | * @param {Function} getter function that returns a getter function to be used for name 3844 | * @name overwriteProperty 3845 | * @api public 3846 | */ 3847 | 3848 | module.exports = function (ctx, name, getter) { 3849 | var _get = Object.getOwnPropertyDescriptor(ctx, name) 3850 | , _super = function () {}; 3851 | 3852 | if (_get && 'function' === typeof _get.get) 3853 | _super = _get.get 3854 | 3855 | Object.defineProperty(ctx, name, 3856 | { get: function () { 3857 | var result = getter(_super).call(this); 3858 | return result === undefined ? this : result; 3859 | } 3860 | , configurable: true 3861 | }); 3862 | }; 3863 | 3864 | }); // module: chai/utils/overwriteProperty.js 3865 | 3866 | require.register("chai/utils/test.js", function(module, exports, require){ 3867 | /*! 3868 | * Chai - test utility 3869 | * Copyright(c) 2012-2013 Jake Luer 3870 | * MIT Licensed 3871 | */ 3872 | 3873 | /*! 3874 | * Module dependancies 3875 | */ 3876 | 3877 | var flag = require('./flag'); 3878 | 3879 | /** 3880 | * # test(object, expression) 3881 | * 3882 | * Test and object for expression. 3883 | * 3884 | * @param {Object} object (constructed Assertion) 3885 | * @param {Arguments} chai.Assertion.prototype.assert arguments 3886 | */ 3887 | 3888 | module.exports = function (obj, args) { 3889 | var negate = flag(obj, 'negate') 3890 | , expr = args[0]; 3891 | return negate ? !expr : expr; 3892 | }; 3893 | 3894 | }); // module: chai/utils/test.js 3895 | 3896 | require.register("chai/utils/transferFlags.js", function(module, exports, require){ 3897 | /*! 3898 | * Chai - transferFlags utility 3899 | * Copyright(c) 2012-2013 Jake Luer 3900 | * MIT Licensed 3901 | */ 3902 | 3903 | /** 3904 | * ### transferFlags(assertion, object, includeAll = true) 3905 | * 3906 | * Transfer all the flags for `assertion` to `object`. If 3907 | * `includeAll` is set to `false`, then the base Chai 3908 | * assertion flags (namely `object`, `ssfi`, and `message`) 3909 | * will not be transferred. 3910 | * 3911 | * 3912 | * var newAssertion = new Assertion(); 3913 | * utils.transferFlags(assertion, newAssertion); 3914 | * 3915 | * var anotherAsseriton = new Assertion(myObj); 3916 | * utils.transferFlags(assertion, anotherAssertion, false); 3917 | * 3918 | * @param {Assertion} assertion the assertion to transfer the flags from 3919 | * @param {Object} object the object to transfer the flags too; usually a new assertion 3920 | * @param {Boolean} includeAll 3921 | * @name getAllFlags 3922 | * @api private 3923 | */ 3924 | 3925 | module.exports = function (assertion, object, includeAll) { 3926 | var flags = assertion.__flags || (assertion.__flags = Object.create(null)); 3927 | 3928 | if (!object.__flags) { 3929 | object.__flags = Object.create(null); 3930 | } 3931 | 3932 | includeAll = arguments.length === 3 ? includeAll : true; 3933 | 3934 | for (var flag in flags) { 3935 | if (includeAll || 3936 | (flag !== 'object' && flag !== 'ssfi' && flag != 'message')) { 3937 | object.__flags[flag] = flags[flag]; 3938 | } 3939 | } 3940 | }; 3941 | 3942 | }); // module: chai/utils/transferFlags.js 3943 | 3944 | require.register("chai/utils/type.js", function(module, exports, require){ 3945 | /*! 3946 | * Chai - type utility 3947 | * Copyright(c) 2012-2013 Jake Luer 3948 | * MIT Licensed 3949 | */ 3950 | 3951 | /*! 3952 | * Detectable javascript natives 3953 | */ 3954 | 3955 | var natives = { 3956 | '[object Arguments]': 'arguments' 3957 | , '[object Array]': 'array' 3958 | , '[object Date]': 'date' 3959 | , '[object Function]': 'function' 3960 | , '[object Number]': 'number' 3961 | , '[object RegExp]': 'regexp' 3962 | , '[object String]': 'string' 3963 | }; 3964 | 3965 | /** 3966 | * ### type(object) 3967 | * 3968 | * Better implementation of `typeof` detection that can 3969 | * be used cross-browser. Handles the inconsistencies of 3970 | * Array, `null`, and `undefined` detection. 3971 | * 3972 | * utils.type({}) // 'object' 3973 | * utils.type(null) // `null' 3974 | * utils.type(undefined) // `undefined` 3975 | * utils.type([]) // `array` 3976 | * 3977 | * @param {Mixed} object to detect type of 3978 | * @name type 3979 | * @api private 3980 | */ 3981 | 3982 | module.exports = function (obj) { 3983 | var str = Object.prototype.toString.call(obj); 3984 | if (natives[str]) return natives[str]; 3985 | if (obj === null) return 'null'; 3986 | if (obj === undefined) return 'undefined'; 3987 | if (obj === Object(obj)) return 'object'; 3988 | return typeof obj; 3989 | }; 3990 | 3991 | }); // module: chai/utils/type.js 3992 | 3993 | require.alias("./chai.js", "chai"); 3994 | 3995 | return require('chai'); 3996 | }); -------------------------------------------------------------------------------- /lib/mocha/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 5 | padding: 60px 50px; 6 | } 7 | 8 | #mocha ul, #mocha li { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | #mocha ul { 14 | list-style: none; 15 | } 16 | 17 | #mocha h1, #mocha h2 { 18 | margin: 0; 19 | } 20 | 21 | #mocha h1 { 22 | margin-top: 15px; 23 | font-size: 1em; 24 | font-weight: 200; 25 | } 26 | 27 | #mocha h1 a { 28 | text-decoration: none; 29 | color: inherit; 30 | } 31 | 32 | #mocha h1 a:hover { 33 | text-decoration: underline; 34 | } 35 | 36 | #mocha .suite .suite h1 { 37 | margin-top: 0; 38 | font-size: .8em; 39 | } 40 | 41 | .hidden { 42 | display: none; 43 | } 44 | 45 | #mocha h2 { 46 | font-size: 12px; 47 | font-weight: normal; 48 | cursor: pointer; 49 | } 50 | 51 | #mocha .suite { 52 | margin-left: 15px; 53 | } 54 | 55 | #mocha .test { 56 | margin-left: 15px; 57 | overflow: hidden; 58 | } 59 | 60 | #mocha .test.pending:hover h2::after { 61 | content: '(pending)'; 62 | font-family: arial; 63 | } 64 | 65 | #mocha .test.pass.medium .duration { 66 | background: #C09853; 67 | } 68 | 69 | #mocha .test.pass.slow .duration { 70 | background: #B94A48; 71 | } 72 | 73 | #mocha .test.pass::before { 74 | content: '✓'; 75 | font-size: 12px; 76 | display: block; 77 | float: left; 78 | margin-right: 5px; 79 | color: #00d6b2; 80 | } 81 | 82 | #mocha .test.pass .duration { 83 | font-size: 9px; 84 | margin-left: 5px; 85 | padding: 2px 5px; 86 | color: white; 87 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 88 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 89 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 90 | -webkit-border-radius: 5px; 91 | -moz-border-radius: 5px; 92 | -ms-border-radius: 5px; 93 | -o-border-radius: 5px; 94 | border-radius: 5px; 95 | } 96 | 97 | #mocha .test.pass.fast .duration { 98 | display: none; 99 | } 100 | 101 | #mocha .test.pending { 102 | color: #0b97c4; 103 | } 104 | 105 | #mocha .test.pending::before { 106 | content: '◦'; 107 | color: #0b97c4; 108 | } 109 | 110 | #mocha .test.fail { 111 | color: #c00; 112 | } 113 | 114 | #mocha .test.fail pre { 115 | color: black; 116 | } 117 | 118 | #mocha .test.fail::before { 119 | content: '✖'; 120 | font-size: 12px; 121 | display: block; 122 | float: left; 123 | margin-right: 5px; 124 | color: #c00; 125 | } 126 | 127 | #mocha .test pre.error { 128 | color: #c00; 129 | max-height: 300px; 130 | overflow: auto; 131 | } 132 | 133 | #mocha .test pre { 134 | display: block; 135 | float: left; 136 | clear: left; 137 | font: 12px/1.5 monaco, monospace; 138 | margin: 5px; 139 | padding: 15px; 140 | border: 1px solid #eee; 141 | border-bottom-color: #ddd; 142 | -webkit-border-radius: 3px; 143 | -webkit-box-shadow: 0 1px 3px #eee; 144 | -moz-border-radius: 3px; 145 | -moz-box-shadow: 0 1px 3px #eee; 146 | } 147 | 148 | #mocha .test h2 { 149 | position: relative; 150 | } 151 | 152 | #mocha .test a.replay { 153 | position: absolute; 154 | top: 3px; 155 | right: 0; 156 | text-decoration: none; 157 | vertical-align: middle; 158 | display: block; 159 | width: 15px; 160 | height: 15px; 161 | line-height: 15px; 162 | text-align: center; 163 | background: #eee; 164 | font-size: 15px; 165 | -moz-border-radius: 15px; 166 | border-radius: 15px; 167 | -webkit-transition: opacity 200ms; 168 | -moz-transition: opacity 200ms; 169 | transition: opacity 200ms; 170 | opacity: 0.3; 171 | color: #888; 172 | } 173 | 174 | #mocha .test:hover a.replay { 175 | opacity: 1; 176 | } 177 | 178 | #mocha-report.pass .test.fail { 179 | display: none; 180 | } 181 | 182 | #mocha-report.fail .test.pass { 183 | display: none; 184 | } 185 | 186 | #mocha-error { 187 | color: #c00; 188 | font-size: 1.5 em; 189 | font-weight: 100; 190 | letter-spacing: 1px; 191 | } 192 | 193 | #mocha-stats { 194 | position: fixed; 195 | top: 15px; 196 | right: 10px; 197 | font-size: 12px; 198 | margin: 0; 199 | color: #888; 200 | } 201 | 202 | #mocha-stats .progress { 203 | float: right; 204 | padding-top: 0; 205 | } 206 | 207 | #mocha-stats em { 208 | color: black; 209 | } 210 | 211 | #mocha-stats a { 212 | text-decoration: none; 213 | color: inherit; 214 | } 215 | 216 | #mocha-stats a:hover { 217 | border-bottom: 1px solid #eee; 218 | } 219 | 220 | #mocha-stats li { 221 | display: inline-block; 222 | margin: 0 5px; 223 | list-style: none; 224 | padding-top: 11px; 225 | } 226 | 227 | #mocha-stats canvas { 228 | width: 40px; 229 | height: 40px; 230 | } 231 | 232 | code .comment { color: #ddd } 233 | code .init { color: #2F6FAD } 234 | code .string { color: #5890AD } 235 | code .keyword { color: #8A6343 } 236 | code .number { color: #2F6FAD } 237 | 238 | @media screen and (max-device-width: 480px) { 239 | body { 240 | padding: 60px 0px; 241 | } 242 | 243 | #stats { 244 | position: absolute; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /lib/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.4.4 2 | // =================== 3 | 4 | // > http://underscorejs.org 5 | // > (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. 6 | // > Underscore may be freely distributed under the MIT license. 7 | 8 | // Baseline setup 9 | // -------------- 10 | (function() { 11 | 12 | // Establish the root object, `window` in the browser, or `global` on the server. 13 | var root = this; 14 | 15 | // Save the previous value of the `_` variable. 16 | var previousUnderscore = root._; 17 | 18 | // Establish the object that gets returned to break out of a loop iteration. 19 | var breaker = {}; 20 | 21 | // Save bytes in the minified (but not gzipped) version: 22 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; 23 | 24 | // Create quick reference variables for speed access to core prototypes. 25 | var push = ArrayProto.push, 26 | slice = ArrayProto.slice, 27 | concat = ArrayProto.concat, 28 | toString = ObjProto.toString, 29 | hasOwnProperty = ObjProto.hasOwnProperty; 30 | 31 | // All **ECMAScript 5** native function implementations that we hope to use 32 | // are declared here. 33 | var 34 | nativeForEach = ArrayProto.forEach, 35 | nativeMap = ArrayProto.map, 36 | nativeReduce = ArrayProto.reduce, 37 | nativeReduceRight = ArrayProto.reduceRight, 38 | nativeFilter = ArrayProto.filter, 39 | nativeEvery = ArrayProto.every, 40 | nativeSome = ArrayProto.some, 41 | nativeIndexOf = ArrayProto.indexOf, 42 | nativeLastIndexOf = ArrayProto.lastIndexOf, 43 | nativeIsArray = Array.isArray, 44 | nativeKeys = Object.keys, 45 | nativeBind = FuncProto.bind; 46 | 47 | // Create a safe reference to the Underscore object for use below. 48 | var _ = function(obj) { 49 | if (obj instanceof _) return obj; 50 | if (!(this instanceof _)) return new _(obj); 51 | this._wrapped = obj; 52 | }; 53 | 54 | // Export the Underscore object for **Node.js**, with 55 | // backwards-compatibility for the old `require()` API. If we're in 56 | // the browser, add `_` as a global object via a string identifier, 57 | // for Closure Compiler "advanced" mode. 58 | if (typeof exports !== 'undefined') { 59 | if (typeof module !== 'undefined' && module.exports) { 60 | exports = module.exports = _; 61 | } 62 | exports._ = _; 63 | } else { 64 | root._ = _; 65 | } 66 | 67 | // Current version. 68 | _.VERSION = '1.4.4'; 69 | 70 | // Collection Functions 71 | // -------------------- 72 | 73 | // The cornerstone, an `each` implementation, aka `forEach`. 74 | // Handles objects with the built-in `forEach`, arrays, and raw objects. 75 | // Delegates to **ECMAScript 5**'s native `forEach` if available. 76 | var each = _.each = _.forEach = function(obj, iterator, context) { 77 | if (obj == null) return; 78 | if (nativeForEach && obj.forEach === nativeForEach) { 79 | obj.forEach(iterator, context); 80 | } else if (obj.length === +obj.length) { 81 | for (var i = 0, l = obj.length; i < l; i++) { 82 | if (iterator.call(context, obj[i], i, obj) === breaker) return; 83 | } 84 | } else { 85 | for (var key in obj) { 86 | if (_.has(obj, key)) { 87 | if (iterator.call(context, obj[key], key, obj) === breaker) return; 88 | } 89 | } 90 | } 91 | }; 92 | 93 | // Return the results of applying the iterator to each element. 94 | // Delegates to **ECMAScript 5**'s native `map` if available. 95 | _.map = _.collect = function(obj, iterator, context) { 96 | var results = []; 97 | if (obj == null) return results; 98 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); 99 | each(obj, function(value, index, list) { 100 | results[results.length] = iterator.call(context, value, index, list); 101 | }); 102 | return results; 103 | }; 104 | 105 | var reduceError = 'Reduce of empty array with no initial value'; 106 | 107 | // **Reduce** builds up a single result from a list of values, aka `inject`, 108 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. 109 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { 110 | var initial = arguments.length > 2; 111 | if (obj == null) obj = []; 112 | if (nativeReduce && obj.reduce === nativeReduce) { 113 | if (context) iterator = _.bind(iterator, context); 114 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); 115 | } 116 | each(obj, function(value, index, list) { 117 | if (!initial) { 118 | memo = value; 119 | initial = true; 120 | } else { 121 | memo = iterator.call(context, memo, value, index, list); 122 | } 123 | }); 124 | if (!initial) throw new TypeError(reduceError); 125 | return memo; 126 | }; 127 | 128 | // The right-associative version of reduce, also known as `foldr`. 129 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available. 130 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) { 131 | var initial = arguments.length > 2; 132 | if (obj == null) obj = []; 133 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { 134 | if (context) iterator = _.bind(iterator, context); 135 | return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); 136 | } 137 | var length = obj.length; 138 | if (length !== +length) { 139 | var keys = _.keys(obj); 140 | length = keys.length; 141 | } 142 | each(obj, function(value, index, list) { 143 | index = keys ? keys[--length] : --length; 144 | if (!initial) { 145 | memo = obj[index]; 146 | initial = true; 147 | } else { 148 | memo = iterator.call(context, memo, obj[index], index, list); 149 | } 150 | }); 151 | if (!initial) throw new TypeError(reduceError); 152 | return memo; 153 | }; 154 | 155 | // Return the first value which passes a truth test. Aliased as `detect`. 156 | _.find = _.detect = function(obj, iterator, context) { 157 | var result; 158 | any(obj, function(value, index, list) { 159 | if (iterator.call(context, value, index, list)) { 160 | result = value; 161 | return true; 162 | } 163 | }); 164 | return result; 165 | }; 166 | 167 | // Return all the elements that pass a truth test. 168 | // Delegates to **ECMAScript 5**'s native `filter` if available. 169 | // Aliased as `select`. 170 | _.filter = _.select = function(obj, iterator, context) { 171 | var results = []; 172 | if (obj == null) return results; 173 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); 174 | each(obj, function(value, index, list) { 175 | if (iterator.call(context, value, index, list)) results[results.length] = value; 176 | }); 177 | return results; 178 | }; 179 | 180 | // Return all the elements for which a truth test fails. 181 | _.reject = function(obj, iterator, context) { 182 | return _.filter(obj, function(value, index, list) { 183 | return !iterator.call(context, value, index, list); 184 | }, context); 185 | }; 186 | 187 | // Determine whether all of the elements match a truth test. 188 | // Delegates to **ECMAScript 5**'s native `every` if available. 189 | // Aliased as `all`. 190 | _.every = _.all = function(obj, iterator, context) { 191 | iterator || (iterator = _.identity); 192 | var result = true; 193 | if (obj == null) return result; 194 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); 195 | each(obj, function(value, index, list) { 196 | if (!(result = result && iterator.call(context, value, index, list))) return breaker; 197 | }); 198 | return !!result; 199 | }; 200 | 201 | // Determine if at least one element in the object matches a truth test. 202 | // Delegates to **ECMAScript 5**'s native `some` if available. 203 | // Aliased as `any`. 204 | var any = _.some = _.any = function(obj, iterator, context) { 205 | iterator || (iterator = _.identity); 206 | var result = false; 207 | if (obj == null) return result; 208 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); 209 | each(obj, function(value, index, list) { 210 | if (result || (result = iterator.call(context, value, index, list))) return breaker; 211 | }); 212 | return !!result; 213 | }; 214 | 215 | // Determine if the array or object contains a given value (using `===`). 216 | // Aliased as `include`. 217 | _.contains = _.include = function(obj, target) { 218 | if (obj == null) return false; 219 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; 220 | return any(obj, function(value) { 221 | return value === target; 222 | }); 223 | }; 224 | 225 | // Invoke a method (with arguments) on every item in a collection. 226 | _.invoke = function(obj, method) { 227 | var args = slice.call(arguments, 2); 228 | var isFunc = _.isFunction(method); 229 | return _.map(obj, function(value) { 230 | return (isFunc ? method : value[method]).apply(value, args); 231 | }); 232 | }; 233 | 234 | // Convenience version of a common use case of `map`: fetching a property. 235 | _.pluck = function(obj, key) { 236 | return _.map(obj, function(value){ return value[key]; }); 237 | }; 238 | 239 | // Convenience version of a common use case of `filter`: selecting only objects 240 | // containing specific `key:value` pairs. 241 | _.where = function(obj, attrs, first) { 242 | if (_.isEmpty(attrs)) return first ? null : []; 243 | return _[first ? 'find' : 'filter'](obj, function(value) { 244 | for (var key in attrs) { 245 | if (attrs[key] !== value[key]) return false; 246 | } 247 | return true; 248 | }); 249 | }; 250 | 251 | // Convenience version of a common use case of `find`: getting the first object 252 | // containing specific `key:value` pairs. 253 | _.findWhere = function(obj, attrs) { 254 | return _.where(obj, attrs, true); 255 | }; 256 | 257 | // Return the maximum element or (element-based computation). 258 | // Can't optimize arrays of integers longer than 65,535 elements. 259 | // See: https://bugs.webkit.org/show_bug.cgi?id=80797 260 | _.max = function(obj, iterator, context) { 261 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 262 | return Math.max.apply(Math, obj); 263 | } 264 | if (!iterator && _.isEmpty(obj)) return -Infinity; 265 | var result = {computed : -Infinity, value: -Infinity}; 266 | each(obj, function(value, index, list) { 267 | var computed = iterator ? iterator.call(context, value, index, list) : value; 268 | computed >= result.computed && (result = {value : value, computed : computed}); 269 | }); 270 | return result.value; 271 | }; 272 | 273 | // Return the minimum element (or element-based computation). 274 | _.min = function(obj, iterator, context) { 275 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 276 | return Math.min.apply(Math, obj); 277 | } 278 | if (!iterator && _.isEmpty(obj)) return Infinity; 279 | var result = {computed : Infinity, value: Infinity}; 280 | each(obj, function(value, index, list) { 281 | var computed = iterator ? iterator.call(context, value, index, list) : value; 282 | computed < result.computed && (result = {value : value, computed : computed}); 283 | }); 284 | return result.value; 285 | }; 286 | 287 | // Shuffle an array. 288 | _.shuffle = function(obj) { 289 | var rand; 290 | var index = 0; 291 | var shuffled = []; 292 | each(obj, function(value) { 293 | rand = _.random(index++); 294 | shuffled[index - 1] = shuffled[rand]; 295 | shuffled[rand] = value; 296 | }); 297 | return shuffled; 298 | }; 299 | 300 | // An internal function to generate lookup iterators. 301 | var lookupIterator = function(value) { 302 | return _.isFunction(value) ? value : function(obj){ return obj[value]; }; 303 | }; 304 | 305 | // Sort the object's values by a criterion produced by an iterator. 306 | _.sortBy = function(obj, value, context) { 307 | var iterator = lookupIterator(value); 308 | return _.pluck(_.map(obj, function(value, index, list) { 309 | return { 310 | value : value, 311 | index : index, 312 | criteria : iterator.call(context, value, index, list) 313 | }; 314 | }).sort(function(left, right) { 315 | var a = left.criteria; 316 | var b = right.criteria; 317 | if (a !== b) { 318 | if (a > b || a === void 0) return 1; 319 | if (a < b || b === void 0) return -1; 320 | } 321 | return left.index < right.index ? -1 : 1; 322 | }), 'value'); 323 | }; 324 | 325 | // An internal function used for aggregate "group by" operations. 326 | var group = function(obj, value, context, behavior) { 327 | var result = {}; 328 | var iterator = lookupIterator(value || _.identity); 329 | each(obj, function(value, index) { 330 | var key = iterator.call(context, value, index, obj); 331 | behavior(result, key, value); 332 | }); 333 | return result; 334 | }; 335 | 336 | // Groups the object's values by a criterion. Pass either a string attribute 337 | // to group by, or a function that returns the criterion. 338 | _.groupBy = function(obj, value, context) { 339 | return group(obj, value, context, function(result, key, value) { 340 | (_.has(result, key) ? result[key] : (result[key] = [])).push(value); 341 | }); 342 | }; 343 | 344 | // Counts instances of an object that group by a certain criterion. Pass 345 | // either a string attribute to count by, or a function that returns the 346 | // criterion. 347 | _.countBy = function(obj, value, context) { 348 | return group(obj, value, context, function(result, key) { 349 | if (!_.has(result, key)) result[key] = 0; 350 | result[key]++; 351 | }); 352 | }; 353 | 354 | // Use a comparator function to figure out the smallest index at which 355 | // an object should be inserted so as to maintain order. Uses binary search. 356 | _.sortedIndex = function(array, obj, iterator, context) { 357 | iterator = iterator == null ? _.identity : lookupIterator(iterator); 358 | var value = iterator.call(context, obj); 359 | var low = 0, high = array.length; 360 | while (low < high) { 361 | var mid = (low + high) >>> 1; 362 | iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; 363 | } 364 | return low; 365 | }; 366 | 367 | // Safely convert anything iterable into a real, live array. 368 | _.toArray = function(obj) { 369 | if (!obj) return []; 370 | if (_.isArray(obj)) return slice.call(obj); 371 | if (obj.length === +obj.length) return _.map(obj, _.identity); 372 | return _.values(obj); 373 | }; 374 | 375 | // Return the number of elements in an object. 376 | _.size = function(obj) { 377 | if (obj == null) return 0; 378 | return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; 379 | }; 380 | 381 | // Array Functions 382 | // --------------- 383 | 384 | // Get the first element of an array. Passing **n** will return the first N 385 | // values in the array. Aliased as `head` and `take`. The **guard** check 386 | // allows it to work with `_.map`. 387 | _.first = _.head = _.take = function(array, n, guard) { 388 | if (array == null) return void 0; 389 | return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; 390 | }; 391 | 392 | // Returns everything but the last entry of the array. Especially useful on 393 | // the arguments object. Passing **n** will return all the values in 394 | // the array, excluding the last N. The **guard** check allows it to work with 395 | // `_.map`. 396 | _.initial = function(array, n, guard) { 397 | return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); 398 | }; 399 | 400 | // Get the last element of an array. Passing **n** will return the last N 401 | // values in the array. The **guard** check allows it to work with `_.map`. 402 | _.last = function(array, n, guard) { 403 | if (array == null) return void 0; 404 | if ((n != null) && !guard) { 405 | return slice.call(array, Math.max(array.length - n, 0)); 406 | } else { 407 | return array[array.length - 1]; 408 | } 409 | }; 410 | 411 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. 412 | // Especially useful on the arguments object. Passing an **n** will return 413 | // the rest N values in the array. The **guard** 414 | // check allows it to work with `_.map`. 415 | _.rest = _.tail = _.drop = function(array, n, guard) { 416 | return slice.call(array, (n == null) || guard ? 1 : n); 417 | }; 418 | 419 | // Trim out all falsy values from an array. 420 | _.compact = function(array) { 421 | return _.filter(array, _.identity); 422 | }; 423 | 424 | // Internal implementation of a recursive `flatten` function. 425 | var flatten = function(input, shallow, output) { 426 | each(input, function(value) { 427 | if (_.isArray(value)) { 428 | shallow ? push.apply(output, value) : flatten(value, shallow, output); 429 | } else { 430 | output.push(value); 431 | } 432 | }); 433 | return output; 434 | }; 435 | 436 | // Return a completely flattened version of an array. 437 | _.flatten = function(array, shallow) { 438 | return flatten(array, shallow, []); 439 | }; 440 | 441 | // Return a version of the array that does not contain the specified value(s). 442 | _.without = function(array) { 443 | return _.difference(array, slice.call(arguments, 1)); 444 | }; 445 | 446 | // Produce a duplicate-free version of the array. If the array has already 447 | // been sorted, you have the option of using a faster algorithm. 448 | // Aliased as `unique`. 449 | _.uniq = _.unique = function(array, isSorted, iterator, context) { 450 | if (_.isFunction(isSorted)) { 451 | context = iterator; 452 | iterator = isSorted; 453 | isSorted = false; 454 | } 455 | var initial = iterator ? _.map(array, iterator, context) : array; 456 | var results = []; 457 | var seen = []; 458 | each(initial, function(value, index) { 459 | if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { 460 | seen.push(value); 461 | results.push(array[index]); 462 | } 463 | }); 464 | return results; 465 | }; 466 | 467 | // Produce an array that contains the union: each distinct element from all of 468 | // the passed-in arrays. 469 | _.union = function() { 470 | return _.uniq(concat.apply(ArrayProto, arguments)); 471 | }; 472 | 473 | // Produce an array that contains every item shared between all the 474 | // passed-in arrays. 475 | _.intersection = function(array) { 476 | var rest = slice.call(arguments, 1); 477 | return _.filter(_.uniq(array), function(item) { 478 | return _.every(rest, function(other) { 479 | return _.indexOf(other, item) >= 0; 480 | }); 481 | }); 482 | }; 483 | 484 | // Take the difference between one array and a number of other arrays. 485 | // Only the elements present in just the first array will remain. 486 | _.difference = function(array) { 487 | var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); 488 | return _.filter(array, function(value){ return !_.contains(rest, value); }); 489 | }; 490 | 491 | // Zip together multiple lists into a single array -- elements that share 492 | // an index go together. 493 | _.zip = function() { 494 | var args = slice.call(arguments); 495 | var length = _.max(_.pluck(args, 'length')); 496 | var results = new Array(length); 497 | for (var i = 0; i < length; i++) { 498 | results[i] = _.pluck(args, "" + i); 499 | } 500 | return results; 501 | }; 502 | 503 | // Converts lists into objects. Pass either a single array of `[key, value]` 504 | // pairs, or two parallel arrays of the same length -- one of keys, and one of 505 | // the corresponding values. 506 | _.object = function(list, values) { 507 | if (list == null) return {}; 508 | var result = {}; 509 | for (var i = 0, l = list.length; i < l; i++) { 510 | if (values) { 511 | result[list[i]] = values[i]; 512 | } else { 513 | result[list[i][0]] = list[i][1]; 514 | } 515 | } 516 | return result; 517 | }; 518 | 519 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), 520 | // we need this function. Return the position of the first occurrence of an 521 | // item in an array, or -1 if the item is not included in the array. 522 | // Delegates to **ECMAScript 5**'s native `indexOf` if available. 523 | // If the array is large and already in sort order, pass `true` 524 | // for **isSorted** to use binary search. 525 | _.indexOf = function(array, item, isSorted) { 526 | if (array == null) return -1; 527 | var i = 0, l = array.length; 528 | if (isSorted) { 529 | if (typeof isSorted == 'number') { 530 | i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); 531 | } else { 532 | i = _.sortedIndex(array, item); 533 | return array[i] === item ? i : -1; 534 | } 535 | } 536 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); 537 | for (; i < l; i++) if (array[i] === item) return i; 538 | return -1; 539 | }; 540 | 541 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. 542 | _.lastIndexOf = function(array, item, from) { 543 | if (array == null) return -1; 544 | var hasIndex = from != null; 545 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { 546 | return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); 547 | } 548 | var i = (hasIndex ? from : array.length); 549 | while (i--) if (array[i] === item) return i; 550 | return -1; 551 | }; 552 | 553 | // Generate an integer Array containing an arithmetic progression. A port of 554 | // the native Python `range()` function. See 555 | // [the Python documentation](http://docs.python.org/library/functions.html#range). 556 | _.range = function(start, stop, step) { 557 | if (arguments.length <= 1) { 558 | stop = start || 0; 559 | start = 0; 560 | } 561 | step = arguments[2] || 1; 562 | 563 | var len = Math.max(Math.ceil((stop - start) / step), 0); 564 | var idx = 0; 565 | var range = new Array(len); 566 | 567 | while(idx < len) { 568 | range[idx++] = start; 569 | start += step; 570 | } 571 | 572 | return range; 573 | }; 574 | 575 | // Function (ahem) Functions 576 | // ------------------ 577 | 578 | // Create a function bound to a given object (assigning `this`, and arguments, 579 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if 580 | // available. 581 | _.bind = function(func, context) { 582 | if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); 583 | var args = slice.call(arguments, 2); 584 | return function() { 585 | return func.apply(context, args.concat(slice.call(arguments))); 586 | }; 587 | }; 588 | 589 | // Partially apply a function by creating a version that has had some of its 590 | // arguments pre-filled, without changing its dynamic `this` context. 591 | _.partial = function(func) { 592 | var args = slice.call(arguments, 1); 593 | return function() { 594 | return func.apply(this, args.concat(slice.call(arguments))); 595 | }; 596 | }; 597 | 598 | // Bind all of an object's methods to that object. Useful for ensuring that 599 | // all callbacks defined on an object belong to it. 600 | _.bindAll = function(obj) { 601 | var funcs = slice.call(arguments, 1); 602 | if (funcs.length === 0) funcs = _.functions(obj); 603 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); 604 | return obj; 605 | }; 606 | 607 | // Memoize an expensive function by storing its results. 608 | _.memoize = function(func, hasher) { 609 | var memo = {}; 610 | hasher || (hasher = _.identity); 611 | return function() { 612 | var key = hasher.apply(this, arguments); 613 | return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); 614 | }; 615 | }; 616 | 617 | // Delays a function for the given number of milliseconds, and then calls 618 | // it with the arguments supplied. 619 | _.delay = function(func, wait) { 620 | var args = slice.call(arguments, 2); 621 | return setTimeout(function(){ return func.apply(null, args); }, wait); 622 | }; 623 | 624 | // Defers a function, scheduling it to run after the current call stack has 625 | // cleared. 626 | _.defer = function(func) { 627 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); 628 | }; 629 | 630 | // Returns a function, that, when invoked, will only be triggered at most once 631 | // during a given window of time. 632 | _.throttle = function(func, wait) { 633 | var context, args, timeout, result; 634 | var previous = 0; 635 | var later = function() { 636 | previous = new Date; 637 | timeout = null; 638 | result = func.apply(context, args); 639 | }; 640 | return function() { 641 | var now = new Date; 642 | var remaining = wait - (now - previous); 643 | context = this; 644 | args = arguments; 645 | if (remaining <= 0) { 646 | clearTimeout(timeout); 647 | timeout = null; 648 | previous = now; 649 | result = func.apply(context, args); 650 | } else if (!timeout) { 651 | timeout = setTimeout(later, remaining); 652 | } 653 | return result; 654 | }; 655 | }; 656 | 657 | // Returns a function, that, as long as it continues to be invoked, will not 658 | // be triggered. The function will be called after it stops being called for 659 | // N milliseconds. If `immediate` is passed, trigger the function on the 660 | // leading edge, instead of the trailing. 661 | _.debounce = function(func, wait, immediate) { 662 | var timeout, result; 663 | return function() { 664 | var context = this, args = arguments; 665 | var later = function() { 666 | timeout = null; 667 | if (!immediate) result = func.apply(context, args); 668 | }; 669 | var callNow = immediate && !timeout; 670 | clearTimeout(timeout); 671 | timeout = setTimeout(later, wait); 672 | if (callNow) result = func.apply(context, args); 673 | return result; 674 | }; 675 | }; 676 | 677 | // Returns a function that will be executed at most one time, no matter how 678 | // often you call it. Useful for lazy initialization. 679 | _.once = function(func) { 680 | var ran = false, memo; 681 | return function() { 682 | if (ran) return memo; 683 | ran = true; 684 | memo = func.apply(this, arguments); 685 | func = null; 686 | return memo; 687 | }; 688 | }; 689 | 690 | // Returns the first function passed as an argument to the second, 691 | // allowing you to adjust arguments, run code before and after, and 692 | // conditionally execute the original function. 693 | _.wrap = function(func, wrapper) { 694 | return function() { 695 | var args = [func]; 696 | push.apply(args, arguments); 697 | return wrapper.apply(this, args); 698 | }; 699 | }; 700 | 701 | // Returns a function that is the composition of a list of functions, each 702 | // consuming the return value of the function that follows. 703 | _.compose = function() { 704 | var funcs = arguments; 705 | return function() { 706 | var args = arguments; 707 | for (var i = funcs.length - 1; i >= 0; i--) { 708 | args = [funcs[i].apply(this, args)]; 709 | } 710 | return args[0]; 711 | }; 712 | }; 713 | 714 | // Returns a function that will only be executed after being called N times. 715 | _.after = function(times, func) { 716 | if (times <= 0) return func(); 717 | return function() { 718 | if (--times < 1) { 719 | return func.apply(this, arguments); 720 | } 721 | }; 722 | }; 723 | 724 | // Object Functions 725 | // ---------------- 726 | 727 | // Retrieve the names of an object's properties. 728 | // Delegates to **ECMAScript 5**'s native `Object.keys` 729 | _.keys = nativeKeys || function(obj) { 730 | if (obj !== Object(obj)) throw new TypeError('Invalid object'); 731 | var keys = []; 732 | for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; 733 | return keys; 734 | }; 735 | 736 | // Retrieve the values of an object's properties. 737 | _.values = function(obj) { 738 | var values = []; 739 | for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); 740 | return values; 741 | }; 742 | 743 | // Convert an object into a list of `[key, value]` pairs. 744 | _.pairs = function(obj) { 745 | var pairs = []; 746 | for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); 747 | return pairs; 748 | }; 749 | 750 | // Invert the keys and values of an object. The values must be serializable. 751 | _.invert = function(obj) { 752 | var result = {}; 753 | for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; 754 | return result; 755 | }; 756 | 757 | // Return a sorted list of the function names available on the object. 758 | // Aliased as `methods` 759 | _.functions = _.methods = function(obj) { 760 | var names = []; 761 | for (var key in obj) { 762 | if (_.isFunction(obj[key])) names.push(key); 763 | } 764 | return names.sort(); 765 | }; 766 | 767 | // Extend a given object with all the properties in passed-in object(s). 768 | _.extend = function(obj) { 769 | each(slice.call(arguments, 1), function(source) { 770 | if (source) { 771 | for (var prop in source) { 772 | obj[prop] = source[prop]; 773 | } 774 | } 775 | }); 776 | return obj; 777 | }; 778 | 779 | // Return a copy of the object only containing the whitelisted properties. 780 | _.pick = function(obj) { 781 | var copy = {}; 782 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 783 | each(keys, function(key) { 784 | if (key in obj) copy[key] = obj[key]; 785 | }); 786 | return copy; 787 | }; 788 | 789 | // Return a copy of the object without the blacklisted properties. 790 | _.omit = function(obj) { 791 | var copy = {}; 792 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 793 | for (var key in obj) { 794 | if (!_.contains(keys, key)) copy[key] = obj[key]; 795 | } 796 | return copy; 797 | }; 798 | 799 | // Fill in a given object with default properties. 800 | _.defaults = function(obj) { 801 | each(slice.call(arguments, 1), function(source) { 802 | if (source) { 803 | for (var prop in source) { 804 | if (obj[prop] == null) obj[prop] = source[prop]; 805 | } 806 | } 807 | }); 808 | return obj; 809 | }; 810 | 811 | // Create a (shallow-cloned) duplicate of an object. 812 | _.clone = function(obj) { 813 | if (!_.isObject(obj)) return obj; 814 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj); 815 | }; 816 | 817 | // Invokes interceptor with the obj, and then returns obj. 818 | // The primary purpose of this method is to "tap into" a method chain, in 819 | // order to perform operations on intermediate results within the chain. 820 | _.tap = function(obj, interceptor) { 821 | interceptor(obj); 822 | return obj; 823 | }; 824 | 825 | // Internal recursive comparison function for `isEqual`. 826 | var eq = function(a, b, aStack, bStack) { 827 | // Identical objects are equal. `0 === -0`, but they aren't identical. 828 | // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. 829 | if (a === b) return a !== 0 || 1 / a == 1 / b; 830 | // A strict comparison is necessary because `null == undefined`. 831 | if (a == null || b == null) return a === b; 832 | // Unwrap any wrapped objects. 833 | if (a instanceof _) a = a._wrapped; 834 | if (b instanceof _) b = b._wrapped; 835 | // Compare `[[Class]]` names. 836 | var className = toString.call(a); 837 | if (className != toString.call(b)) return false; 838 | switch (className) { 839 | // Strings, numbers, dates, and booleans are compared by value. 840 | case '[object String]': 841 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 842 | // equivalent to `new String("5")`. 843 | return a == String(b); 844 | case '[object Number]': 845 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for 846 | // other numeric values. 847 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); 848 | case '[object Date]': 849 | case '[object Boolean]': 850 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their 851 | // millisecond representations. Note that invalid dates with millisecond representations 852 | // of `NaN` are not equivalent. 853 | return +a == +b; 854 | // RegExps are compared by their source patterns and flags. 855 | case '[object RegExp]': 856 | return a.source == b.source && 857 | a.global == b.global && 858 | a.multiline == b.multiline && 859 | a.ignoreCase == b.ignoreCase; 860 | } 861 | if (typeof a != 'object' || typeof b != 'object') return false; 862 | // Assume equality for cyclic structures. The algorithm for detecting cyclic 863 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 864 | var length = aStack.length; 865 | while (length--) { 866 | // Linear search. Performance is inversely proportional to the number of 867 | // unique nested structures. 868 | if (aStack[length] == a) return bStack[length] == b; 869 | } 870 | // Add the first object to the stack of traversed objects. 871 | aStack.push(a); 872 | bStack.push(b); 873 | var size = 0, result = true; 874 | // Recursively compare objects and arrays. 875 | if (className == '[object Array]') { 876 | // Compare array lengths to determine if a deep comparison is necessary. 877 | size = a.length; 878 | result = size == b.length; 879 | if (result) { 880 | // Deep compare the contents, ignoring non-numeric properties. 881 | while (size--) { 882 | if (!(result = eq(a[size], b[size], aStack, bStack))) break; 883 | } 884 | } 885 | } else { 886 | // Objects with different constructors are not equivalent, but `Object`s 887 | // from different frames are. 888 | var aCtor = a.constructor, bCtor = b.constructor; 889 | if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && 890 | _.isFunction(bCtor) && (bCtor instanceof bCtor))) { 891 | return false; 892 | } 893 | // Deep compare objects. 894 | for (var key in a) { 895 | if (_.has(a, key)) { 896 | // Count the expected number of properties. 897 | size++; 898 | // Deep compare each member. 899 | if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; 900 | } 901 | } 902 | // Ensure that both objects contain the same number of properties. 903 | if (result) { 904 | for (key in b) { 905 | if (_.has(b, key) && !(size--)) break; 906 | } 907 | result = !size; 908 | } 909 | } 910 | // Remove the first object from the stack of traversed objects. 911 | aStack.pop(); 912 | bStack.pop(); 913 | return result; 914 | }; 915 | 916 | // Perform a deep comparison to check if two objects are equal. 917 | _.isEqual = function(a, b) { 918 | return eq(a, b, [], []); 919 | }; 920 | 921 | // Is a given array, string, or object empty? 922 | // An "empty" object has no enumerable own-properties. 923 | _.isEmpty = function(obj) { 924 | if (obj == null) return true; 925 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; 926 | for (var key in obj) if (_.has(obj, key)) return false; 927 | return true; 928 | }; 929 | 930 | // Is a given value a DOM element? 931 | _.isElement = function(obj) { 932 | return !!(obj && obj.nodeType === 1); 933 | }; 934 | 935 | // Is a given value an array? 936 | // Delegates to ECMA5's native Array.isArray 937 | _.isArray = nativeIsArray || function(obj) { 938 | return toString.call(obj) == '[object Array]'; 939 | }; 940 | 941 | // Is a given variable an object? 942 | _.isObject = function(obj) { 943 | return obj === Object(obj); 944 | }; 945 | 946 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. 947 | each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { 948 | _['is' + name] = function(obj) { 949 | return toString.call(obj) == '[object ' + name + ']'; 950 | }; 951 | }); 952 | 953 | // Define a fallback version of the method in browsers (ahem, IE), where 954 | // there isn't any inspectable "Arguments" type. 955 | if (!_.isArguments(arguments)) { 956 | _.isArguments = function(obj) { 957 | return !!(obj && _.has(obj, 'callee')); 958 | }; 959 | } 960 | 961 | // Optimize `isFunction` if appropriate. 962 | if (typeof (/./) !== 'function') { 963 | _.isFunction = function(obj) { 964 | return typeof obj === 'function'; 965 | }; 966 | } 967 | 968 | // Is a given object a finite number? 969 | _.isFinite = function(obj) { 970 | return isFinite(obj) && !isNaN(parseFloat(obj)); 971 | }; 972 | 973 | // Is the given value `NaN`? (NaN is the only number which does not equal itself). 974 | _.isNaN = function(obj) { 975 | return _.isNumber(obj) && obj != +obj; 976 | }; 977 | 978 | // Is a given value a boolean? 979 | _.isBoolean = function(obj) { 980 | return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; 981 | }; 982 | 983 | // Is a given value equal to null? 984 | _.isNull = function(obj) { 985 | return obj === null; 986 | }; 987 | 988 | // Is a given variable undefined? 989 | _.isUndefined = function(obj) { 990 | return obj === void 0; 991 | }; 992 | 993 | // Shortcut function for checking if an object has a given property directly 994 | // on itself (in other words, not on a prototype). 995 | _.has = function(obj, key) { 996 | return hasOwnProperty.call(obj, key); 997 | }; 998 | 999 | // Utility Functions 1000 | // ----------------- 1001 | 1002 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its 1003 | // previous owner. Returns a reference to the Underscore object. 1004 | _.noConflict = function() { 1005 | root._ = previousUnderscore; 1006 | return this; 1007 | }; 1008 | 1009 | // Keep the identity function around for default iterators. 1010 | _.identity = function(value) { 1011 | return value; 1012 | }; 1013 | 1014 | // Run a function **n** times. 1015 | _.times = function(n, iterator, context) { 1016 | var accum = Array(n); 1017 | for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); 1018 | return accum; 1019 | }; 1020 | 1021 | // Return a random integer between min and max (inclusive). 1022 | _.random = function(min, max) { 1023 | if (max == null) { 1024 | max = min; 1025 | min = 0; 1026 | } 1027 | return min + Math.floor(Math.random() * (max - min + 1)); 1028 | }; 1029 | 1030 | // List of HTML entities for escaping. 1031 | var entityMap = { 1032 | escape: { 1033 | '&': '&', 1034 | '<': '<', 1035 | '>': '>', 1036 | '"': '"', 1037 | "'": ''', 1038 | '/': '/' 1039 | } 1040 | }; 1041 | entityMap.unescape = _.invert(entityMap.escape); 1042 | 1043 | // Regexes containing the keys and values listed immediately above. 1044 | var entityRegexes = { 1045 | escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), 1046 | unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') 1047 | }; 1048 | 1049 | // Functions for escaping and unescaping strings to/from HTML interpolation. 1050 | _.each(['escape', 'unescape'], function(method) { 1051 | _[method] = function(string) { 1052 | if (string == null) return ''; 1053 | return ('' + string).replace(entityRegexes[method], function(match) { 1054 | return entityMap[method][match]; 1055 | }); 1056 | }; 1057 | }); 1058 | 1059 | // If the value of the named property is a function then invoke it; 1060 | // otherwise, return it. 1061 | _.result = function(object, property) { 1062 | if (object == null) return null; 1063 | var value = object[property]; 1064 | return _.isFunction(value) ? value.call(object) : value; 1065 | }; 1066 | 1067 | // Add your own custom functions to the Underscore object. 1068 | _.mixin = function(obj) { 1069 | each(_.functions(obj), function(name){ 1070 | var func = _[name] = obj[name]; 1071 | _.prototype[name] = function() { 1072 | var args = [this._wrapped]; 1073 | push.apply(args, arguments); 1074 | return result.call(this, func.apply(_, args)); 1075 | }; 1076 | }); 1077 | }; 1078 | 1079 | // Generate a unique integer id (unique within the entire client session). 1080 | // Useful for temporary DOM ids. 1081 | var idCounter = 0; 1082 | _.uniqueId = function(prefix) { 1083 | var id = ++idCounter + ''; 1084 | return prefix ? prefix + id : id; 1085 | }; 1086 | 1087 | // By default, Underscore uses ERB-style template delimiters, change the 1088 | // following template settings to use alternative delimiters. 1089 | _.templateSettings = { 1090 | evaluate : /<%([\s\S]+?)%>/g, 1091 | interpolate : /<%=([\s\S]+?)%>/g, 1092 | escape : /<%-([\s\S]+?)%>/g 1093 | }; 1094 | 1095 | // When customizing `templateSettings`, if you don't want to define an 1096 | // interpolation, evaluation or escaping regex, we need one that is 1097 | // guaranteed not to match. 1098 | var noMatch = /(.)^/; 1099 | 1100 | // Certain characters need to be escaped so that they can be put into a 1101 | // string literal. 1102 | var escapes = { 1103 | "'": "'", 1104 | '\\': '\\', 1105 | '\r': 'r', 1106 | '\n': 'n', 1107 | '\t': 't', 1108 | '\u2028': 'u2028', 1109 | '\u2029': 'u2029' 1110 | }; 1111 | 1112 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 1113 | 1114 | // JavaScript micro-templating, similar to John Resig's implementation. 1115 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 1116 | // and correctly escapes quotes within interpolated code. 1117 | _.template = function(text, data, settings) { 1118 | var render; 1119 | settings = _.defaults({}, settings, _.templateSettings); 1120 | 1121 | // Combine delimiters into one regular expression via alternation. 1122 | var matcher = new RegExp([ 1123 | (settings.escape || noMatch).source, 1124 | (settings.interpolate || noMatch).source, 1125 | (settings.evaluate || noMatch).source 1126 | ].join('|') + '|$', 'g'); 1127 | 1128 | // Compile the template source, escaping string literals appropriately. 1129 | var index = 0; 1130 | var source = "__p+='"; 1131 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 1132 | source += text.slice(index, offset) 1133 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); 1134 | 1135 | if (escape) { 1136 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 1137 | } 1138 | if (interpolate) { 1139 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 1140 | } 1141 | if (evaluate) { 1142 | source += "';\n" + evaluate + "\n__p+='"; 1143 | } 1144 | index = offset + match.length; 1145 | return match; 1146 | }); 1147 | source += "';\n"; 1148 | 1149 | // If a variable is not specified, place data values in local scope. 1150 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 1151 | 1152 | source = "var __t,__p='',__j=Array.prototype.join," + 1153 | "print=function(){__p+=__j.call(arguments,'');};\n" + 1154 | source + "return __p;\n"; 1155 | 1156 | try { 1157 | render = new Function(settings.variable || 'obj', '_', source); 1158 | } catch (e) { 1159 | e.source = source; 1160 | throw e; 1161 | } 1162 | 1163 | if (data) return render(data, _); 1164 | var template = function(data) { 1165 | return render.call(this, data, _); 1166 | }; 1167 | 1168 | // Provide the compiled function source as a convenience for precompilation. 1169 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 1170 | 1171 | return template; 1172 | }; 1173 | 1174 | // Add a "chain" function, which will delegate to the wrapper. 1175 | _.chain = function(obj) { 1176 | return _(obj).chain(); 1177 | }; 1178 | 1179 | // OOP 1180 | // --------------- 1181 | // If Underscore is called as a function, it returns a wrapped object that 1182 | // can be used OO-style. This wrapper holds altered versions of all the 1183 | // underscore functions. Wrapped objects may be chained. 1184 | 1185 | // Helper function to continue chaining intermediate results. 1186 | var result = function(obj) { 1187 | return this._chain ? _(obj).chain() : obj; 1188 | }; 1189 | 1190 | // Add all of the Underscore functions to the wrapper object. 1191 | _.mixin(_); 1192 | 1193 | // Add all mutator Array functions to the wrapper. 1194 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 1195 | var method = ArrayProto[name]; 1196 | _.prototype[name] = function() { 1197 | var obj = this._wrapped; 1198 | method.apply(obj, arguments); 1199 | if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; 1200 | return result.call(this, obj); 1201 | }; 1202 | }); 1203 | 1204 | // Add all accessor Array functions to the wrapper. 1205 | each(['concat', 'join', 'slice'], function(name) { 1206 | var method = ArrayProto[name]; 1207 | _.prototype[name] = function() { 1208 | return result.call(this, method.apply(this._wrapped, arguments)); 1209 | }; 1210 | }); 1211 | 1212 | _.extend(_.prototype, { 1213 | 1214 | // Start chaining a wrapped Underscore object. 1215 | chain: function() { 1216 | this._chain = true; 1217 | return this; 1218 | }, 1219 | 1220 | // Extracts the result from a wrapped and chained object. 1221 | value: function() { 1222 | return this._wrapped; 1223 | } 1224 | 1225 | }); 1226 | 1227 | }).call(this); 1228 | -------------------------------------------------------------------------------- /spec/getElementsByClassNameSpec.js: -------------------------------------------------------------------------------- 1 | var htmlStrings = [ 2 | '
', 3 | '
', 4 | '
', 5 | '
', 6 | '
', 7 | '
', 8 | '
yay
' 9 | ]; 10 | 11 | describe('getElementsByClassName', function() { 12 | 13 | it('should match the results of calling the built-in function', function() { 14 | $('body').addClass('targetClassName'); 15 | htmlStrings.forEach(function(htmlString) { 16 | var $rootElement = $(htmlString); 17 | $('body').append($rootElement); 18 | 19 | var result = getElementsByClassName('targetClassName'); 20 | var expectedNodeList = document.getElementsByClassName('targetClassName'); 21 | var expectedArray = Array.prototype.slice.apply(expectedNodeList); 22 | var equality = _.isEqual(result, expectedArray); // why can't we use `===` here? 23 | expect(equality).to.equal(true); 24 | 25 | $rootElement.remove(); 26 | }); 27 | $('body').removeClass('targetClassName'); 28 | }); 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /spec/nthFibonacciSpec.js: -------------------------------------------------------------------------------- 1 | describe('nthFibonacci', function() { 2 | it('should properly compute the fibonacci sequence for the number given and return the result', function() { 3 | expect(nthFibonacci(1)).to.equal(1); 4 | expect(nthFibonacci(2)).to.equal(1); 5 | expect(nthFibonacci(3)).to.equal(2); 6 | expect(nthFibonacci(4)).to.equal(3); 7 | expect(nthFibonacci(5)).to.equal(5); 8 | }); 9 | }); -------------------------------------------------------------------------------- /spec/powerSpec.js: -------------------------------------------------------------------------------- 1 | describe('power', function() { 2 | it('should peform the exponent math and return the correct result', function() { 3 | expect(power(2, 2)).to.equal(4); 4 | expect(power(2, 3)).to.equal(8); 5 | expect(power(2, 4)).to.equal(16); 6 | expect(power(5, 3)).to.equal(125); 7 | expect(power(10, 3)).to.equal(1000); 8 | }); 9 | }); -------------------------------------------------------------------------------- /spec/sumArraySpec.js: -------------------------------------------------------------------------------- 1 | describe('sumArray', function() { 2 | it('should sum all numbers in the array and return the result', function() { 3 | expect(sumArray([1])).to.equal(1); 4 | expect(sumArray([2, 3])).to.equal(5); 5 | expect(sumArray([10, 20])).to.equal(30); 6 | expect(sumArray([2, 4, 6, 8])).to.equal(20); 7 | expect(sumArray([1, 2, 3, 4, 5])).to.equal(15); 8 | }); 9 | }); -------------------------------------------------------------------------------- /src/getElementsByClassName.js: -------------------------------------------------------------------------------- 1 | // If life was easy, we could just do things the easy way: 2 | // var getElementsByClassName = function (className) { 3 | // return document.getElementsByClassName(className); 4 | // }; 5 | 6 | // But instead we're going to implement it from scratch: 7 | 8 | 9 | var getElementsByClassName = function(className) { 10 | // Your code here 11 | }; -------------------------------------------------------------------------------- /src/nthFibonacci.js: -------------------------------------------------------------------------------- 1 | // A Fibonacci sequence is a list of numbers that begins with 0 and 1, and each 2 | // subsequent number is the sum of the previous two. 3 | 4 | // For example, the first five Fibonacci numbers are: 5 | // 0 1 1 2 3 6 | 7 | // If n were 4, your function should return 3; for 5, it should return 5. 8 | 9 | // Write a function that accepts a number, n, and returns the nth Fibonacci 10 | // number. Use a recursive solution to this problem; if you finish with time 11 | // left over, implement an iterative solution. 12 | 13 | // example usage: 14 | // nthFibonacci(2); // => 1 15 | // nthFibonacci(3); // => 2 16 | // nthFibonacci(4); // => 3 17 | // etc... 18 | 19 | var nthFibonacci = function(n) { 20 | // Your code here 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /src/power.js: -------------------------------------------------------------------------------- 1 | // Given two integers -- a base and an exponent -- write a 2 | // recursive function that computes the exponent math and 3 | // returns the result. 4 | 5 | // Exponent math is performed by multiplying the base by itself 6 | // x number of times, where x is the exponent given. 7 | 8 | // Examples: 9 | 10 | // power(2, 2) => 4 11 | // power(2, 4) => 16 12 | // power(5, 2) => 25 13 | // power(10, 3) => 1000 14 | 15 | var power = function(base, exponent) { 16 | // Your code here 17 | }; -------------------------------------------------------------------------------- /src/sumArray.js: -------------------------------------------------------------------------------- 1 | // Given an array of integers, write a recursive function 2 | // that returns the sum of all itegers in the array. 3 | 4 | // This type of problem is often solved using looping/iteration, 5 | // but can also be solved recursively. If you want, you can 6 | // first solve the problem using a loop and then refactor 7 | // to use recursion. 8 | 9 | // Remember to create a base case/exit condition to prevent 10 | // a stack overflow. 11 | 12 | // Examples: 13 | 14 | // sumArray([10, 20]) => 30 15 | // sumArray([2, 4, 6, 8]) => 20 16 | // sumArray([1, 2, 3, 4, 5]) => 15 17 | 18 | 19 | var sumArray = function(arr) { 20 | // Your code here 21 | }; --------------------------------------------------------------------------------