diff --git a/node_modules/.bin/grunt-istanbul b/node_modules/.bin/grunt-istanbul
new file mode 120000
index 0000000..32fa0e2
--- /dev/null
+++ b/node_modules/.bin/grunt-istanbul
@@ -0,0 +1 @@
+../grunt-istanbul/bin/grunt-istanbul
\ No newline at end of file
diff --git a/node_modules/grunt-contrib-clean/LICENSE-MIT b/node_modules/grunt-contrib-clean/LICENSE-MIT
new file mode 100644
index 0000000..22f852a
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/LICENSE-MIT
@@ -0,0 +1,22 @@
+Copyright (c) 2014 Tim Branyen, contributors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/grunt-contrib-clean/README.md b/node_modules/grunt-contrib-clean/README.md
new file mode 100644
index 0000000..4dc007d
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/README.md
@@ -0,0 +1,104 @@
+# grunt-contrib-clean v0.6.0 [![Build Status: Linux](https://travis-ci.org/gruntjs/grunt-contrib-clean.png?branch=master)](https://travis-ci.org/gruntjs/grunt-contrib-clean)
+
+> Clean files and folders.
+
+
+
+## Getting Started
+This plugin requires Grunt `~0.4.0`
+
+If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:
+
+```shell
+npm install grunt-contrib-clean --save-dev
+```
+
+Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:
+
+```js
+grunt.loadNpmTasks('grunt-contrib-clean');
+```
+
+*This plugin was designed to work with Grunt 0.4.x. If you're still using grunt v0.3.x it's strongly recommended that [you upgrade](http://gruntjs.com/upgrading-from-0.3-to-0.4), but in case you can't please use [v0.3.2](https://github.com/gruntjs/grunt-contrib-clean/tree/grunt-0.3-stable).*
+
+
+
+## Clean task
+_Run this task with the `grunt clean` command._
+
+Task targets, files and options may be specified according to the grunt [Configuring tasks](http://gruntjs.com/configuring-tasks) guide.
+
+*Due to the destructive nature of this task, always be cautious of the paths you clean.*
+### Options
+
+#### force
+Type: `Boolean`
+Default: false
+
+This overrides this task from blocking deletion of folders outside current working dir (CWD). Use with caution.
+
+#### no-write
+Type: `Boolean`
+Default: false
+
+Will log messages of what would happen if the task was ran but doesn't actually delete the files.
+
+### Usage Examples
+
+There are three formats you can use to run this task.
+
+#### Short
+
+```js
+clean: ["path/to/dir/one", "path/to/dir/two"]
+```
+
+#### Medium (specific targets with global options)
+
+```js
+clean: {
+ build: ["path/to/dir/one", "path/to/dir/two"],
+ release: ["path/to/another/dir/one", "path/to/another/dir/two"]
+},
+```
+
+#### Long (specific targets with per target options)
+
+```js
+clean: {
+ build: {
+ src: ["path/to/dir/one", "path/to/dir/two"]
+ }
+}
+```
+
+##### Skipping Files
+
+```js
+// Deletes all .js files, but skips min.js files
+clean: {
+ js: ["path/to/dir/*.js", "!path/to/dir/*.min.js"]
+}
+```
+
+"Compact" and "Files Array" formats support a few [additional properties](http://gruntjs.com/configuring-tasks#files)
+which help you deal with hidden files, process dynamic mappings and so on.
+
+
+## Release History
+
+ * 2014-07-27 v0.6.0 Less verbose output. README updates.
+ * 2013-07-15 v0.5.0 Use rimraf directly, version 2.2.1 to fix issue on Windows. Add no-write option to mimic grunt.file.delete behavior.
+ * 2013-04-16 v0.4.1 Check if file exists to avoid trying to delete a non-existent file.
+ * 2013-02-15 v0.4.0 First official release for Grunt 0.4.0.
+ * 2013-01-18 v0.4.0rc6 Updating grunt/gruntplugin dependencies to rc6. Changing in-development grunt/gruntplugin dependency versions from tilde version ranges to specific versions.
+ * 2013-01-09 v0.4.0rc5 Updating to work with grunt v0.4.0rc5. Switching to this.filesSrc api.
+ * 2012-12-07 v0.4.0a Conversion to grunt v0.4 conventions. Remove node v0.6 and grunt v0.3 support. Add force option to bypass CWD check.
+ * 2012-09-23 v0.3.0 Options no longer accepted from global config key.
+ * 2012-09-10 v0.2.0 Refactored from grunt-contrib into individual repo.
+
+---
+
+Task submitted by [Tim Branyen](http://tbranyen.com/)
+
+*This file was generated on Sun Jul 27 2014 17:36:23.*
diff --git a/node_modules/grunt-contrib-clean/node_modules/.bin/rimraf b/node_modules/grunt-contrib-clean/node_modules/.bin/rimraf
new file mode 120000
index 0000000..4cd49a4
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/.bin/rimraf
@@ -0,0 +1 @@
+../rimraf/bin.js
\ No newline at end of file
diff --git a/node_modules/grunt-contrib-clean/node_modules/rimraf/AUTHORS b/node_modules/grunt-contrib-clean/node_modules/rimraf/AUTHORS
new file mode 100644
index 0000000..247b754
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/rimraf/AUTHORS
@@ -0,0 +1,6 @@
+# Authors sorted by whether or not they're me.
+Isaac Z. Schlueter (http://blog.izs.me)
+Wayne Larsen (http://github.com/wvl)
+ritch
+Marcel Laverdet
+Yosef Dinerstein
diff --git a/node_modules/grunt-contrib-clean/node_modules/rimraf/LICENSE b/node_modules/grunt-contrib-clean/node_modules/rimraf/LICENSE
new file mode 100644
index 0000000..05a4010
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/rimraf/LICENSE
@@ -0,0 +1,23 @@
+Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/grunt-contrib-clean/node_modules/rimraf/README.md b/node_modules/grunt-contrib-clean/node_modules/rimraf/README.md
new file mode 100644
index 0000000..cd123b6
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/rimraf/README.md
@@ -0,0 +1,30 @@
+`rm -rf` for node.
+
+Install with `npm install rimraf`, or just drop rimraf.js somewhere.
+
+## API
+
+`rimraf(f, callback)`
+
+The callback will be called with an error if there is one. Certain
+errors are handled for you:
+
+* Windows: `EBUSY` and `ENOTEMPTY` - rimraf will back off a maximum of
+ `opts.maxBusyTries` times before giving up.
+* `ENOENT` - If the file doesn't exist, rimraf will return
+ successfully, since your desired outcome is already the case.
+
+## rimraf.sync
+
+It can remove stuff synchronously, too. But that's not so good. Use
+the async API. It's better.
+
+## CLI
+
+If installed with `npm install rimraf -g` it can be used as a global
+command `rimraf ` which is useful for cross platform support.
+
+## mkdirp
+
+If you need to create a directory recursively, check out
+[mkdirp](https://github.com/substack/node-mkdirp).
diff --git a/node_modules/grunt-contrib-clean/node_modules/rimraf/bin.js b/node_modules/grunt-contrib-clean/node_modules/rimraf/bin.js
new file mode 100755
index 0000000..29bfa8a
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/rimraf/bin.js
@@ -0,0 +1,33 @@
+#!/usr/bin/env node
+
+var rimraf = require('./')
+
+var help = false
+var dashdash = false
+var args = process.argv.slice(2).filter(function(arg) {
+ if (dashdash)
+ return !!arg
+ else if (arg === '--')
+ dashdash = true
+ else if (arg.match(/^(-+|\/)(h(elp)?|\?)$/))
+ help = true
+ else
+ return !!arg
+});
+
+if (help || args.length === 0) {
+ // If they didn't ask for help, then this is not a "success"
+ var log = help ? console.log : console.error
+ log('Usage: rimraf ')
+ log('')
+ log(' Deletes all files and folders at "path" recursively.')
+ log('')
+ log('Options:')
+ log('')
+ log(' -h, --help Display this usage info')
+ process.exit(help ? 0 : 1)
+} else {
+ args.forEach(function(arg) {
+ rimraf.sync(arg)
+ })
+}
diff --git a/node_modules/grunt-contrib-clean/node_modules/rimraf/package.json b/node_modules/grunt-contrib-clean/node_modules/rimraf/package.json
new file mode 100644
index 0000000..eba95e0
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/rimraf/package.json
@@ -0,0 +1,73 @@
+{
+ "name": "rimraf",
+ "version": "2.2.8",
+ "main": "rimraf.js",
+ "description": "A deep deletion module for node (like `rm -rf`)",
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "license": {
+ "type": "MIT",
+ "url": "https://github.com/isaacs/rimraf/raw/master/LICENSE"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/rimraf.git"
+ },
+ "scripts": {
+ "test": "cd test && bash run.sh"
+ },
+ "bin": {
+ "rimraf": "./bin.js"
+ },
+ "contributors": [
+ {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me"
+ },
+ {
+ "name": "Wayne Larsen",
+ "email": "wayne@larsen.st",
+ "url": "http://github.com/wvl"
+ },
+ {
+ "name": "ritch",
+ "email": "skawful@gmail.com"
+ },
+ {
+ "name": "Marcel Laverdet"
+ },
+ {
+ "name": "Yosef Dinerstein",
+ "email": "yosefd@microsoft.com"
+ }
+ ],
+ "bugs": {
+ "url": "https://github.com/isaacs/rimraf/issues"
+ },
+ "homepage": "https://github.com/isaacs/rimraf",
+ "_id": "rimraf@2.2.8",
+ "_shasum": "e439be2aaee327321952730f99a8929e4fc50582",
+ "_from": "rimraf@>=2.2.1 <2.3.0",
+ "_npmVersion": "1.4.10",
+ "_npmUser": {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ },
+ "maintainers": [
+ {
+ "name": "isaacs",
+ "email": "i@izs.me"
+ }
+ ],
+ "dist": {
+ "shasum": "e439be2aaee327321952730f99a8929e4fc50582",
+ "tarball": "http://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/node_modules/grunt-contrib-clean/node_modules/rimraf/rimraf.js b/node_modules/grunt-contrib-clean/node_modules/rimraf/rimraf.js
new file mode 100644
index 0000000..eb96c46
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/rimraf/rimraf.js
@@ -0,0 +1,248 @@
+module.exports = rimraf
+rimraf.sync = rimrafSync
+
+var assert = require("assert")
+var path = require("path")
+var fs = require("fs")
+
+// for EMFILE handling
+var timeout = 0
+exports.EMFILE_MAX = 1000
+exports.BUSYTRIES_MAX = 3
+
+var isWindows = (process.platform === "win32")
+
+function defaults (options) {
+ var methods = [
+ 'unlink',
+ 'chmod',
+ 'stat',
+ 'rmdir',
+ 'readdir'
+ ]
+ methods.forEach(function(m) {
+ options[m] = options[m] || fs[m]
+ m = m + 'Sync'
+ options[m] = options[m] || fs[m]
+ })
+}
+
+function rimraf (p, options, cb) {
+ if (typeof options === 'function') {
+ cb = options
+ options = {}
+ }
+ assert(p)
+ assert(options)
+ assert(typeof cb === 'function')
+
+ defaults(options)
+
+ if (!cb) throw new Error("No callback passed to rimraf()")
+
+ var busyTries = 0
+ rimraf_(p, options, function CB (er) {
+ if (er) {
+ if (isWindows && (er.code === "EBUSY" || er.code === "ENOTEMPTY") &&
+ busyTries < exports.BUSYTRIES_MAX) {
+ busyTries ++
+ var time = busyTries * 100
+ // try again, with the same exact callback as this one.
+ return setTimeout(function () {
+ rimraf_(p, options, CB)
+ }, time)
+ }
+
+ // this one won't happen if graceful-fs is used.
+ if (er.code === "EMFILE" && timeout < exports.EMFILE_MAX) {
+ return setTimeout(function () {
+ rimraf_(p, options, CB)
+ }, timeout ++)
+ }
+
+ // already gone
+ if (er.code === "ENOENT") er = null
+ }
+
+ timeout = 0
+ cb(er)
+ })
+}
+
+// Two possible strategies.
+// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
+// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
+//
+// Both result in an extra syscall when you guess wrong. However, there
+// are likely far more normal files in the world than directories. This
+// is based on the assumption that a the average number of files per
+// directory is >= 1.
+//
+// If anyone ever complains about this, then I guess the strategy could
+// be made configurable somehow. But until then, YAGNI.
+function rimraf_ (p, options, cb) {
+ assert(p)
+ assert(options)
+ assert(typeof cb === 'function')
+
+ options.unlink(p, function (er) {
+ if (er) {
+ if (er.code === "ENOENT")
+ return cb(null)
+ if (er.code === "EPERM")
+ return (isWindows)
+ ? fixWinEPERM(p, options, er, cb)
+ : rmdir(p, options, er, cb)
+ if (er.code === "EISDIR")
+ return rmdir(p, options, er, cb)
+ }
+ return cb(er)
+ })
+}
+
+function fixWinEPERM (p, options, er, cb) {
+ assert(p)
+ assert(options)
+ assert(typeof cb === 'function')
+ if (er)
+ assert(er instanceof Error)
+
+ options.chmod(p, 666, function (er2) {
+ if (er2)
+ cb(er2.code === "ENOENT" ? null : er)
+ else
+ options.stat(p, function(er3, stats) {
+ if (er3)
+ cb(er3.code === "ENOENT" ? null : er)
+ else if (stats.isDirectory())
+ rmdir(p, options, er, cb)
+ else
+ options.unlink(p, cb)
+ })
+ })
+}
+
+function fixWinEPERMSync (p, options, er) {
+ assert(p)
+ assert(options)
+ if (er)
+ assert(er instanceof Error)
+
+ try {
+ options.chmodSync(p, 666)
+ } catch (er2) {
+ if (er2.code === "ENOENT")
+ return
+ else
+ throw er
+ }
+
+ try {
+ var stats = options.statSync(p)
+ } catch (er3) {
+ if (er3.code === "ENOENT")
+ return
+ else
+ throw er
+ }
+
+ if (stats.isDirectory())
+ rmdirSync(p, options, er)
+ else
+ options.unlinkSync(p)
+}
+
+function rmdir (p, options, originalEr, cb) {
+ assert(p)
+ assert(options)
+ if (originalEr)
+ assert(originalEr instanceof Error)
+ assert(typeof cb === 'function')
+
+ // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
+ // if we guessed wrong, and it's not a directory, then
+ // raise the original error.
+ options.rmdir(p, function (er) {
+ if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM"))
+ rmkids(p, options, cb)
+ else if (er && er.code === "ENOTDIR")
+ cb(originalEr)
+ else
+ cb(er)
+ })
+}
+
+function rmkids(p, options, cb) {
+ assert(p)
+ assert(options)
+ assert(typeof cb === 'function')
+
+ options.readdir(p, function (er, files) {
+ if (er)
+ return cb(er)
+ var n = files.length
+ if (n === 0)
+ return options.rmdir(p, cb)
+ var errState
+ files.forEach(function (f) {
+ rimraf(path.join(p, f), options, function (er) {
+ if (errState)
+ return
+ if (er)
+ return cb(errState = er)
+ if (--n === 0)
+ options.rmdir(p, cb)
+ })
+ })
+ })
+}
+
+// this looks simpler, and is strictly *faster*, but will
+// tie up the JavaScript thread and fail on excessively
+// deep directory trees.
+function rimrafSync (p, options) {
+ options = options || {}
+ defaults(options)
+
+ assert(p)
+ assert(options)
+
+ try {
+ options.unlinkSync(p)
+ } catch (er) {
+ if (er.code === "ENOENT")
+ return
+ if (er.code === "EPERM")
+ return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
+ if (er.code !== "EISDIR")
+ throw er
+ rmdirSync(p, options, er)
+ }
+}
+
+function rmdirSync (p, options, originalEr) {
+ assert(p)
+ assert(options)
+ if (originalEr)
+ assert(originalEr instanceof Error)
+
+ try {
+ options.rmdirSync(p)
+ } catch (er) {
+ if (er.code === "ENOENT")
+ return
+ if (er.code === "ENOTDIR")
+ throw originalEr
+ if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")
+ rmkidsSync(p, options)
+ }
+}
+
+function rmkidsSync (p, options) {
+ assert(p)
+ assert(options)
+ options.readdirSync(p).forEach(function (f) {
+ rimrafSync(path.join(p, f), options)
+ })
+ options.rmdirSync(p, options)
+}
diff --git a/node_modules/grunt-contrib-clean/node_modules/rimraf/test/run.sh b/node_modules/grunt-contrib-clean/node_modules/rimraf/test/run.sh
new file mode 100644
index 0000000..653ff9b
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/rimraf/test/run.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+set -e
+code=0
+for i in test-*.js; do
+ echo -n $i ...
+ bash setup.sh
+ node $i
+ if [ -d target ]; then
+ echo "fail"
+ code=1
+ else
+ echo "pass"
+ fi
+done
+rm -rf target
+exit $code
diff --git a/node_modules/grunt-contrib-clean/node_modules/rimraf/test/setup.sh b/node_modules/grunt-contrib-clean/node_modules/rimraf/test/setup.sh
new file mode 100644
index 0000000..2602e63
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/rimraf/test/setup.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+set -e
+
+files=10
+folders=2
+depth=4
+target="$PWD/target"
+
+rm -rf target
+
+fill () {
+ local depth=$1
+ local files=$2
+ local folders=$3
+ local target=$4
+
+ if ! [ -d $target ]; then
+ mkdir -p $target
+ fi
+
+ local f
+
+ f=$files
+ while [ $f -gt 0 ]; do
+ touch "$target/f-$depth-$f"
+ let f--
+ done
+
+ let depth--
+
+ if [ $depth -le 0 ]; then
+ return 0
+ fi
+
+ f=$folders
+ while [ $f -gt 0 ]; do
+ mkdir "$target/folder-$depth-$f"
+ fill $depth $files $folders "$target/d-$depth-$f"
+ let f--
+ done
+}
+
+fill $depth $files $folders $target
+
+# sanity assert
+[ -d $target ]
diff --git a/node_modules/grunt-contrib-clean/node_modules/rimraf/test/test-async.js b/node_modules/grunt-contrib-clean/node_modules/rimraf/test/test-async.js
new file mode 100644
index 0000000..9c2e0b7
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/rimraf/test/test-async.js
@@ -0,0 +1,5 @@
+var rimraf = require("../rimraf")
+ , path = require("path")
+rimraf(path.join(__dirname, "target"), function (er) {
+ if (er) throw er
+})
diff --git a/node_modules/grunt-contrib-clean/node_modules/rimraf/test/test-sync.js b/node_modules/grunt-contrib-clean/node_modules/rimraf/test/test-sync.js
new file mode 100644
index 0000000..eb71f10
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/node_modules/rimraf/test/test-sync.js
@@ -0,0 +1,3 @@
+var rimraf = require("../rimraf")
+ , path = require("path")
+rimraf.sync(path.join(__dirname, "target"))
diff --git a/node_modules/grunt-contrib-clean/package.json b/node_modules/grunt-contrib-clean/package.json
new file mode 100644
index 0000000..3e32ae2
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/package.json
@@ -0,0 +1,88 @@
+{
+ "name": "grunt-contrib-clean",
+ "description": "Clean files and folders.",
+ "version": "0.6.0",
+ "homepage": "https://github.com/gruntjs/grunt-contrib-clean",
+ "author": {
+ "name": "Grunt Team",
+ "url": "http://gruntjs.com/"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/gruntjs/grunt-contrib-clean.git"
+ },
+ "bugs": {
+ "url": "https://github.com/gruntjs/grunt-contrib-clean/issues"
+ },
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://github.com/gruntjs/grunt-contrib-clean/blob/master/LICENSE-MIT"
+ }
+ ],
+ "engines": {
+ "node": ">= 0.8.0"
+ },
+ "scripts": {
+ "test": "grunt test"
+ },
+ "dependencies": {
+ "rimraf": "~2.2.1"
+ },
+ "devDependencies": {
+ "grunt-contrib-jshint": "~0.10.0",
+ "grunt-contrib-nodeunit": "~0.3.3",
+ "grunt-contrib-internal": "~0.4.10",
+ "grunt": "~0.4.0"
+ },
+ "peerDependencies": {
+ "grunt": "~0.4.0"
+ },
+ "keywords": [
+ "gruntplugin"
+ ],
+ "files": [
+ "tasks",
+ "LICENSE-MIT"
+ ],
+ "_id": "grunt-contrib-clean@0.6.0",
+ "dist": {
+ "shasum": "f532dba4b8212674c7c013e146bda6638b9048f6",
+ "tarball": "http://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz"
+ },
+ "_from": "grunt-contrib-clean@*",
+ "_npmVersion": "1.3.26",
+ "_npmUser": {
+ "name": "vladikoff",
+ "email": "vlad@vladikoff.com"
+ },
+ "maintainers": [
+ {
+ "name": "tkellen",
+ "email": "tyler@sleekcode.net"
+ },
+ {
+ "name": "cowboy",
+ "email": "cowboy@rj3.net"
+ },
+ {
+ "name": "shama",
+ "email": "kyle@dontkry.com"
+ },
+ {
+ "name": "vladikoff",
+ "email": "vlad@vladikoff.com"
+ },
+ {
+ "name": "sindresorhus",
+ "email": "sindresorhus@gmail.com"
+ },
+ {
+ "name": "jmeas",
+ "email": "jellyes2@gmail.com"
+ }
+ ],
+ "directories": {},
+ "_shasum": "f532dba4b8212674c7c013e146bda6638b9048f6",
+ "_resolved": "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-0.6.0.tgz"
+}
diff --git a/node_modules/grunt-contrib-clean/tasks/clean.js b/node_modules/grunt-contrib-clean/tasks/clean.js
new file mode 100644
index 0000000..db3d99d
--- /dev/null
+++ b/node_modules/grunt-contrib-clean/tasks/clean.js
@@ -0,0 +1,59 @@
+/*
+ * grunt-contrib-clean
+ * http://gruntjs.com/
+ *
+ * Copyright (c) 2014 Tim Branyen, contributors
+ * Licensed under the MIT license.
+ */
+
+'use strict';
+
+var rimraf = require('rimraf');
+
+module.exports = function(grunt) {
+
+ function clean(filepath, options) {
+ if (!grunt.file.exists(filepath)) {
+ return false;
+ }
+
+ // Only delete cwd or outside cwd if --force enabled. Be careful, people!
+ if (!options.force) {
+ if (grunt.file.isPathCwd(filepath)) {
+ grunt.verbose.error();
+ grunt.fail.warn('Cannot delete the current working directory.');
+ return false;
+ } else if (!grunt.file.isPathInCwd(filepath)) {
+ grunt.verbose.error();
+ grunt.fail.warn('Cannot delete files outside the current working directory.');
+ return false;
+ }
+ }
+
+ try {
+ // Actually delete. Or not.
+ if (!options['no-write']) {
+ rimraf.sync(filepath);
+ }
+ grunt.verbose.writeln((options['no-write'] ? 'Not actually cleaning ' : 'Cleaning ') + filepath + '...');
+ } catch (e) {
+ grunt.log.error();
+ grunt.fail.warn('Unable to delete "' + filepath + '" file (' + e.message + ').', e);
+ }
+ }
+
+ grunt.registerMultiTask('clean', 'Clean files and folders.', function() {
+ // Merge task-specific and/or target-specific options with these defaults.
+ var options = this.options({
+ force: grunt.option('force') === true,
+ 'no-write': grunt.option('no-write') === true,
+ });
+
+ // Clean specified files / dirs.
+ this.filesSrc.forEach(function(filepath) {
+ clean(filepath, options);
+ });
+ grunt.log.ok(this.filesSrc.length + ' ' + grunt.util.pluralize(this.filesSrc.length, 'path/paths') + ' cleaned.');
+ });
+
+};
diff --git a/node_modules/grunt-istanbul/.npmignore b/node_modules/grunt-istanbul/.npmignore
new file mode 100644
index 0000000..5174a94
--- /dev/null
+++ b/node_modules/grunt-istanbul/.npmignore
@@ -0,0 +1,5 @@
+/.settings/
+/node_modules/
+/build/
+/test/
+.project
diff --git a/node_modules/grunt-istanbul/.travis.yml b/node_modules/grunt-istanbul/.travis.yml
new file mode 100644
index 0000000..8473367
--- /dev/null
+++ b/node_modules/grunt-istanbul/.travis.yml
@@ -0,0 +1,12 @@
+language: node_js
+
+node_js:
+ - 0.11
+ - 0.10
+
+branches:
+ except:
+ - gh-pages
+
+before_script:
+ - npm install -g grunt-cli
diff --git a/node_modules/grunt-istanbul/Gruntfile.js b/node_modules/grunt-istanbul/Gruntfile.js
new file mode 100644
index 0000000..0dd894a
--- /dev/null
+++ b/node_modules/grunt-istanbul/Gruntfile.js
@@ -0,0 +1,72 @@
+module.exports = function(grunt) {
+ 'use strict';
+
+ var dateFormat = require('dateformat');
+
+ var tests = 'test/**/*_test.js';
+ var tasks = 'tasks/**/*.js';
+ var reportDir = 'build/reports/' + dateFormat(new Date(), 'yyyymmdd-HHMMss');
+
+ grunt.initConfig({
+ clean : [ 'build' ],
+ nodeunit : {
+ files : [ tests ]
+ },
+ watch : {
+ files : [ tasks, tests ],
+ tasks : 'default'
+ },
+ jshint : {
+ files : [ 'Gruntfile.js', tasks, tests ],
+ options : {
+ curly : true,
+ eqeqeq : true,
+ immed : true,
+ latedef : true,
+ newcap : true,
+ noarg : true,
+ sub : true,
+ undef : true,
+ boss : true,
+ eqnull : true,
+ node : true
+ },
+ globals : {}
+ },
+ instrument : {
+ files : tasks,
+ options : {
+ lazy : true,
+ basePath : 'build/instrument/'
+ }
+ },
+ reloadTasks : {
+ rootPath : 'build/instrument/tasks'
+ },
+ storeCoverage : {
+ options : {
+ dir : reportDir
+ }
+ },
+ makeReport : {
+ src : 'build/reports/**/*.json',
+ options : {
+ type : ['lcov', 'html'],
+ dir : reportDir,
+ print : 'detail'
+ }
+ }
+ });
+
+ // Load local tasks.
+ grunt.loadTasks('tasks');
+ grunt.loadNpmTasks('grunt-contrib-clean');
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-nodeunit');
+
+ grunt.registerTask('test', 'nodeunit');
+ grunt.registerTask('default', [ 'jshint', 'test' ]);
+ grunt.registerTask('cover', [ 'clean', 'instrument', 'reloadTasks', 'test',
+ 'storeCoverage', 'makeReport' ]);
+
+};
diff --git a/node_modules/grunt-istanbul/LICENSE-MIT b/node_modules/grunt-istanbul/LICENSE-MIT
new file mode 100644
index 0000000..d21775c
--- /dev/null
+++ b/node_modules/grunt-istanbul/LICENSE-MIT
@@ -0,0 +1,22 @@
+Copyright (c) 2012 taichi
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/node_modules/grunt-istanbul/README.md b/node_modules/grunt-istanbul/README.md
new file mode 100644
index 0000000..5111b3b
--- /dev/null
+++ b/node_modules/grunt-istanbul/README.md
@@ -0,0 +1,104 @@
+# grunt-istanbul [![Build Status](https://travis-ci.org/taichi/grunt-istanbul.png)](https://travis-ci.org/taichi/grunt-istanbul)
+
+JavaScript codecoverage tool for Grunt
+
+## Getting Started
+This plugin requires Grunt ~0.4.1
+
+Install this grunt plugin next to your project's [Gruntfile.js][getting_started] with: `npm install grunt-istanbul`
+
+Then add this line to your project's `Gruntfile.js` gruntfile:
+
+```javascript
+grunt.loadNpmTasks('grunt-istanbul');
+```
+
+[grunt]: https://github.com/cowboy/grunt
+[getting_started]: https://github.com/cowboy/grunt/blob/master/docs/getting_started.md
+
+## Documentation
+To use this grunt-istanbul plugin, register a grunt task to run the following:
+
+1. Instrument your source code
+2. Run your test suite against your instrumented source code
+3. Store your coverage results
+4. Make the report
+
+For step 2, an environment variable can be used to determine which path to use for loading
+the source code to run the tests against. For example, when you normally run your tests you
+want them to point directly at your source code. But when you run your instanbul code coverage
+task you want your tests to point at your instrumented source code. The `grunt-env` plugin
+can be used for setting an environment variable in a grunt task. Here's an example solution
+that solves this problem using `grunt-env` and `grunt-mocha-test`:
+
+```javascript
+// in Gruntfile.js
+module.exports = function (grunt) {
+
+ grunt.initConfig({
+ env: {
+ coverage: {
+ APP_DIR_FOR_CODE_COVERAGE: '../test/coverage/instrument/app/'
+ }
+ },
+ instrument: {
+ files: 'app/*.js',
+ options: {
+ lazy: true,
+ basePath: 'test/coverage/instrument/'
+ }
+ },
+ mochaTest: {
+ options: {
+ reporter: 'spec'
+ },
+ src: ['test/*.js']
+ },
+ storeCoverage: {
+ options: {
+ dir: 'test/coverage/reports'
+ }
+ },
+ makeReport: {
+ src: 'test/coverage/reports/**/*.json',
+ options: {
+ type: 'lcov',
+ dir: 'test/coverage/reports',
+ print: 'detail'
+ }
+ }
+ });
+
+ grunt.registerTask('coverage', ['env:coverage', 'instrument', 'mochaTest',
+ 'storeCoverage', 'makeReport']);
+};
+
+
+// require_helper.js
+module.exports = function (path) {
+ return require((process.env.APP_DIR_FOR_CODE_COVERAGE || '../app/') + path);
+};
+
+// using requireHelper in a test
+var requireHelper = require('../require_helper');
+var formValidator = requireHelper('form_validator');
+```
+
+Also, checkout the example Gruntfile.js in this repo (note that you do not need to implement the
+`reloadTasks` task in this example):
+[Gruntfile.js](https://github.com/taichi/grunt-istanbul/blob/master/Gruntfile.js#69)
+
+### more examples
+
+* [Testing and Code Coverage With Node.js Apps](http://www.gregjopa.com/2014/02/testing-and-code-coverage-with-node-js-apps/)
+ * [gregjopa/express-app-testing-demo](https://github.com/gregjopa/express-app-testing-demo)
+
+## Contributing
+In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [grunt][grunt].
+
+## Release History
+_(Nothing yet)_
+
+## License
+Copyright (c) 2014 taichi
+Licensed under the MIT license.
diff --git a/node_modules/grunt-istanbul/bin/grunt-istanbul b/node_modules/grunt-istanbul/bin/grunt-istanbul
new file mode 100755
index 0000000..3569462
--- /dev/null
+++ b/node_modules/grunt-istanbul/bin/grunt-istanbul
@@ -0,0 +1,2 @@
+#!/usr/bin/env node
+require('grunt').npmTasks('grunt-istanbul').cli();
diff --git a/node_modules/grunt-istanbul/node_modules/.bin/istanbul b/node_modules/grunt-istanbul/node_modules/.bin/istanbul
new file mode 120000
index 0000000..c129fe5
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/.bin/istanbul
@@ -0,0 +1 @@
+../istanbul/lib/cli.js
\ No newline at end of file
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/CHANGELOG.md b/node_modules/grunt-istanbul/node_modules/istanbul/CHANGELOG.md
new file mode 100644
index 0000000..6529966
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/CHANGELOG.md
@@ -0,0 +1,139 @@
+Changelog
+---------
+
+
+
+
v0.2.16
Make YUI links https-always since relative links break local
+filesystem use-case
+
+
+
+
v0.2.15
make link protocols relative so they don't break on https connections
+(thanks to @yasyf)
+
+
+
+
v0.2.14
Fix hook to deal with non-string/ missing filenames
+(thanks to @jason0x43), update dependencies
+
+
+
+
v0.2.13
Add `--preload-sources` option to `cover` command to make
+code not required by tests to appear in the coverage report.
+
+
+
+
v0.2.12
Text summary as valid markdown, thanks to @smikes
+
+
+
v0.2.11
Allow source map generation, thanks to @jason0x43
+
+
+
v0.2.10
Add flag to handle sigints and dump coverage, thanks to @samccone
+
+
+
v0.2.9
Fix #202
+
+
+
+
v0.2.8
Upgrade esprima
+
+
+
v0.2.7
+
Upgrade esprima
+
Misc jshint fixes
+
+
+
+
v0.2.6
+
Revert bad commit for tree summarizer
+
+
+
+
v0.2.5
+
Add clover report, thanks to @bixdeng, @mpderbec
+
Fix cobertura report bug for relative paths, thanks to @jxiaodev
+
Run self-coverage on tests always
+
Fix tree summarizer when relative paths are involved, thanks to @Swatinem
+
+
+
+
v0.2.4
+
Fix line-split algo to handle Mac lin separators, thanks to @asifrc
+
Update README for quick intro to ignoring code for coverage, thanks to @gergelyke
+
+
+
+
v0.2.3
+
Add YAML config file. `istanbul help config` has more details
+
Support custom reporting thresholds using the `watermarks` section of the config file
+
+
+
v0.2.2
update escodegen, handlebars and resolve dependency versions
+
+
v0.2.1
+
Add ability to skip branches and other hard-to-test code using comments.
+ See the doc for more details
+
Turn `util.error` into `console.error` for node 0.11 compatibility, thanks to @pornel
+
+
+
v0.2.0
+
Add --preserve-comments to instrumenter options, thanks to @arikon
+
Support 'use strict;' in file scope, thanks to @pornel
+
+ Up minor version due to the new way in which the global object is accessed.
+ This _should_ be backwards-compatible but has not been tested in the wild.
+
+
v0.1.46
Fix #114
+
v0.1.45
Add teamcity reporter, thanks to @chrisgladd
+
v0.1.44
Fix inconsistency in processing empty switch with latest esprima, up deps
+
v0.1.43
Add colors to text report thanks to @runk
+
v0.1.42
fix #78: embed source regression introduced in v0.1.38. Fix broken test for this
+
v0.1.41
add json report to dump coverage object for certain use cases
+
v0.1.40
forward sourceStore from lcov to html report, pull request by @vojtajina
+
v0.1.39
add
+
v0.1.38
+
factor out AST instrumentation into own instrumentASTSync method
+
always set function declaration coverage stats to 1 since every such declaration is "executed" exactly one time by the compiler
+
+
v0.1.37
--complete-copy flag contrib from @kami, correct strict mode semantics for instrumented functions
+
v0.1.36
real quiet when --print=none specified, add repo URL to package.json, add contributors
+
v0.1.35
accept cobertura contrib from @nbrownus, fix #52
+
v0.1.34
fix async reporting, update dependencies, accept html cleanup contrib from @mathiasbynens
+
v0.1.33
initialize global coverage object before running tests to workaround mocha leak detection
+
v0.1.32
Fix for null nodes in array expressions, add @unindented as contributor
+
v0.1.31
Misc internal fixes and test changes
+
v0.1.30
Write standard blurbs ("writing coverage object..." etc.) to stderr rather than stdout
+
v0.1.29
Allow --post-require-hook to be a module that can be `require`-d
+
v0.1.28
Add --post-require-hook switch to support use-cases similar to the YUI loader
+
v0.1.27
Add --hook-run-in-context switch to support RequireJS modules. Thanks to @millermedeiros for the pull request
+
v0.1.26
Add support for minimum uncovered unit for check-coverage. Fixes #25
+
v0.1.25
Allow for relative paths in the YUI loader hook
+
v0.1.24
Add lcov summaries. Fixes issue #20
+
v0.1.23
Add ability to save a baseline coverage file for the instrument command. Fixes issue #19
+
v0.1.22
Add signature attribute to cobertura method tags to fix NPE by the Hudson publisher
+
v0.1.21
Add cobertura XML report format; exprimental for now
+
v0.1.20
Fix HTML/ lcov report interface to be more customizable for middleware needs
+
v0.1.19
make all hooking non-destructive in that already loaded modules are never reloaded. Add self-test mode so that already loaded istanbul modules can be unloaded prior to hooking.
+
v0.1.18
Add option to hook in non-destructive mode; i.e. the require cache is not unloaded when hooking
+
v0.1.17
Export some more objects; undocumented for now
+
v0.1.16
Fix npm keywords for istanbul which expects an array of strings but was being fed a single string with keywords instead
+
v0.1.15
Add the 'check-coverage' command so that Istanbul can be used as a posttest script to enforce minimum coverage
+
v0.1.14
Expose the experimental YUI load hook in the interface
+
v0.1.13
Internal jshint cleanup, no features or fixes
+
v0.1.12
Give npm the README that was getting inadvertently excluded
+
v0.1.11
Merge pull request #14 for HTML tweaks. Thanks @davglass. Add @davglass and @nowamasa as contributors in `package.json`
+
v0.1.10
Fix to issue #12. Do not install `uncaughtException` handler and pass input error back to CLI using a callback as opposed to throwing.
+
v0.1.9
Attempt to create reporting directory again just before writing coverage in addition to initial creation
+
v0.1.8
Fix issue #11.
+
v0.1.7
Add text summary and detailed reporting available as --print [summary|detail|both|none]. summary is the default if nothing specified.
+
v0.1.6
Handle backslashes in the file path correctly in emitted code. Fixes #9. Thanks to @nowamasa for bug report and fix
+
v0.1.5
make object-utils.js work on a browser as-is
+
v0.1.4
partial fix for issue #4; add titles to missing coverage spans, remove negative margin for missing if/else indicators
+
v0.1.3
Set the environment variable running_under_istanbul to 1 when that is the case. This allows test runners that use istanbul as a library to back off on using it when set.
+
v0.1.2
HTML reporting cosmetics. Reports now show syntax-colored JS using `prettify`. Summary tables no longer wrap in awkward places.
+
v0.1.1
Fixes issue #1. HTML reports use sources embedded inside the file coverage objects if found rather than reading from the filesystem
+
v0.1.0
Initial version
+
+
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/LICENSE b/node_modules/grunt-istanbul/node_modules/istanbul/LICENSE
new file mode 100644
index 0000000..45a650b
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/LICENSE
@@ -0,0 +1,24 @@
+Copyright 2012 Yahoo! Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Yahoo! Inc. nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/README.md b/node_modules/grunt-istanbul/node_modules/istanbul/README.md
new file mode 100644
index 0000000..e97b6a3
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/README.md
@@ -0,0 +1,224 @@
+Istanbul - a JS code coverage tool written in JS
+================================================
+
+[![Build Status](https://secure.travis-ci.org/gotwarlost/istanbul.png)](http://travis-ci.org/gotwarlost/istanbul)
+[![Dependency Status](https://gemnasium.com/gotwarlost/istanbul.png)](https://gemnasium.com/gotwarlost/istanbul)
+[![Coverage Status](https://img.shields.io/coveralls/gotwarlost/istanbul.svg)](https://coveralls.io/r/gotwarlost/istanbul?branch=master)
+
+[![NPM](https://nodei.co/npm/istanbul.png?downloads=true)](https://nodei.co/npm/istanbul/)
+
+Features
+--------
+
+* All-javascript instrumentation library that tracks **statement, branch,
+and function coverage** and reverse-engineers **line coverage** with 100% fidelity.
+* **Module loader hooks** to instrument code on the fly
+* **Command line tools** to run node unit tests "with coverage turned on" and no cooperation
+whatsoever from the test runner
+* **HTML**, **LCOV**, **Cobertura**, **TeamCity**, and **Clover** reporting.
+* Ability to use as **middleware** when serving JS files that need to be tested on the browser.
+* Can be used on the **command line** as well as a **library**
+* Based on the awesome `esprima` parser and the equally awesome `escodegen` code generator
+* Well-tested on node 0.4.x, 0.6.x, 0.8.x and the browser (instrumentation library only)
+
+Installing
+----------
+
+ $ npm install -g istanbul
+
+Getting started
+---------------
+
+The best way to see it in action is to run node unit tests. Say you have a test
+script `test.js` that runs all tests for your node project without coverage.
+
+Simply:
+
+ $ cd /path/to/your/source/root
+ $ istanbul cover test.js
+
+and this should produce a `coverage.json`, `lcov.info` and `lcov-report/*html` under `./coverage`
+
+Sample of code coverage reports produced by this tool (for this tool!):
+
+* [HTML reports](http://gotwarlost.github.com/istanbul/public/coverage/lcov-report/index.html)
+* [Standard LCOV reports](http://gotwarlost.github.com/istanbul/public/coverage/std-lcov/index.html) (using `genhtml` on the lcov trace file)
+
+Use cases
+---------
+
+Supports the following use cases and more
+
+* transparent coverage of nodejs unit tests
+* ability to use in an npm test script for conditional coverage
+* instrumentation of files in batch mode for browser tests (using yeti for example)
+* Server side code coverage for nodejs by embedding it as custom middleware
+
+
+Ignoring code for coverage
+--------------------------
+
+* Skip an `if` or `else` path with `/* istanbul ignore if */` or `/* istanbul ignore else */` respectively.
+* For all other cases, skip the next 'thing' in the source with: `/* istanbul ignore next */`
+
+See [ignoring-code-for-coverage.md](ignoring-code-for-coverage.md) for the spec.
+
+The command line
+----------------
+
+ $ istanbul help
+
+gives you detailed help on all commands.
+
+Usage: istanbul help
+
+Available commands are:
+
+ check-coverage
+ checks overall coverage against thresholds from coverage JSON
+ files. Exits 1 if thresholds are not met, 0 otherwise
+
+
+ cover transparently adds coverage information to a node command. Saves
+ coverage.json and reports at the end of execution
+
+
+ help shows help
+
+
+ instrument
+ instruments a file or a directory tree and writes the
+ instrumented code to the desired output location
+
+
+ report writes reports for coverage JSON objects produced in a previous
+ run
+
+
+ test cover a node command only when npm_config_coverage is set. Use in
+ an `npm test` script for conditional coverage
+
+
+Command names can be abbreviated as long as the abbreviation is unambiguous
+
+The `cover` command
+-------------------
+
+ $ istanbul cover my-test-script.js -- my test args
+ # note the -- between the command name and the arguments to be passed
+
+The `cover` command can be used to get a coverage object and reports for any arbitrary
+node script. By default, coverage information is written under `./coverage` - this
+can be changed using command-line options.
+
+The `cover` command can also be passed an optional `--handle-sigint` flag to enable writing reports when a user triggers a manual SIGINT of the process that is being covered. This can be useful when you are generating coverage for a long lived process.
+
+The `test` command
+-------------------
+
+The `test` command has almost the same behavior as the `cover` command, except that
+it skips coverage unless the `npm_config_coverage` environment variable is set.
+
+This helps you set up conditional coverage for tests. In this case you would
+have a `package.json` that looks as follows.
+
+ {
+ "name": "my-awesome-lib",
+ "version": "1.0",
+ "script": {
+ "test": "istanbul test my-test-file.js"
+ }
+ }
+
+Then:
+
+ $ npm test # will run tests without coverage
+
+And:
+
+ $ npm test --coverage # will run tests with coverage
+
+**Note**: This needs `node 0.6` or better to work. `npm` for `node 0.4.x` does
+not support the `--coverage` flag.
+
+The `instrument` command
+------------------------
+
+Instruments a single JS file or an entire directory tree and produces an output directory tree with instrumented code. This should not be required for running node unit tests but is useful for tests to be run on the browser (using `yeti` for example).
+
+The `report` command
+-------------------
+
+Writes reports using `coverage*.json` files as the source of coverage information. Reports are available in the following formats:
+
+* html - produces a bunch of HTML files with annotated source code
+* lcovonly - produces an lcov.info file
+* lcov - produces html + lcov files. This is the default format
+* cobertura - produces a cobertura-coverage.xml file for easy Hudson integration
+* text-summary - produces a compact text summary of coverage, typically to console
+* text - produces a detailed text table with coverage for all files
+* teamcity - produces service messages to report code coverage to TeamCity
+* clover - produces a clover.xml file to integrate with Atlassian Clover
+
+Additional report formats may be plugged in at the library level.
+
+The `check-coverage` command
+----------------------------
+
+Checks the coverage of statements, functions, branches, and lines against the
+provided thresholds. Postive thresholds are taken to be the minimum percentage
+required and negative numbers are taken to be the number of uncovered entities
+allowed.
+
+Library usage
+-------------
+
+All the features of istanbul can be accessed as a library using its [public API](http://gotwarlost.github.com/istanbul/public/apidocs/index.html)
+
+Changelog
+---------
+
+Changelog has been moved [here](https://github.com/gotwarlost/istanbul/blob/master/CHANGELOG.md).
+
+License
+-------
+
+istanbul is licensed under the [BSD License](http://github.com/gotwarlost/istanbul/raw/master/LICENSE).
+
+Third-party libraries
+---------------------
+
+The following third-party libraries are used by this module:
+
+* abbrev: https://github.com/isaacs/abbrev-js - to handle command abbreviations
+* async: https://github.com/caolan/async - for parallel instrumentation of files
+* escodegen: https://github.com/Constellation/escodegen - for JS code generation
+* esprima: https://github.com/ariya/esprima - for JS parsing
+* fileset: https://github.com/mklabs/node-fileset - for loading and matching path expressions
+* handlebars: https://github.com/wycats/handlebars.js/ - for report template expansion
+* js-yaml: https://github.com/nodeca/js-yaml - for YAML config file load
+* mkdirp: https://github.com/substack/node-mkdirp - to create output directories
+* nodeunit: https://github.com/caolan/nodeunit - dev dependency for unit tests
+* nopt: https://github.com/isaacs/nopt - for option parsing
+* resolve: https://github.com/substack/node-resolve - for resolving a post-require hook module name into its main file.
+* rimraf - https://github.com/isaacs/rimraf - dev dependency for unit tests
+* which: https://github.com/isaacs/node-which - to resolve a node command to a file for the `cover` command
+* wordwrap: https://github.com/substack/node-wordwrap - for prettier help
+* prettify: http://code.google.com/p/google-code-prettify/ - for syntax colored HTML reports. Files checked in under `lib/vendor/`
+
+Inspired by
+-----------
+
+* YUI test coverage - https://github.com/yui/yuitest - the grand-daddy of JS coverage tools. Istanbul has been specifically designed to offer an alternative to this library with an easy migration path.
+* cover: https://github.com/itay/node-cover - the inspiration for the `cover` command, modeled after the `run` command in that tool. The coverage methodology used by istanbul is quite different, however
+
+Shout out to
+------------
+
+ * [mfncooper](https://github.com/mfncooper) - for great brainstorming discussions
+ * [reid](https://github.com/reid), [davglass](https://github.com/davglass), the YUI dudes, for interesting conversations, encouragement, support and gentle pressure to get it done :)
+
+Why the funky name?
+-------------------
+
+Since all the good ones are taken. Comes from the loose association of ideas across coverage, carpet-area coverage, the country that makes good carpets and so on...
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/index.js b/node_modules/grunt-istanbul/node_modules/istanbul/index.js
new file mode 100644
index 0000000..e3432e4
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/index.js
@@ -0,0 +1,114 @@
+/*
+Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+*/
+
+/**
+ * provides access to the key libraries in istanbul so you can write
+ * your own tools using `istanbul` as a library.
+ *
+ * @module istanbul
+ */
+
+/*jslint nomen: true */
+var path = require('path'),
+ Store = require('./lib/store'),
+ Report = require('./lib/report'),
+ meta = require('./lib/util/meta');
+
+//register our standard plaugins
+require('./lib/register-plugins');
+
+/**
+ * the top-level API for `istanbul`.
+ *
+ * Usage
+ * -----
+ *
+ * var istanbul = require('istanbul');
+ *
+ *
+ * @class API
+ */
+
+module.exports = {
+ /**
+ * the Instrumenter class.
+ * @property {Instrumenter} Instrumenter
+ * @static
+ */
+ Instrumenter: require('./lib/instrumenter'),
+ /**
+ * the Store class.
+ * @property {Store} Store
+ * @static
+ */
+ Store: Store,
+ /**
+ * the Collector class
+ * @property {Collector} Collector
+ * @static
+ */
+ Collector: require('./lib/collector'),
+ /**
+ * the hook module
+ * @property {Hook} hook
+ * @static
+ */
+ hook: require('./lib/hook'),
+ /**
+ * the Report class
+ * @property {Report} Report
+ * @static
+ */
+ Report: Report,
+ /**
+ * utility for processing coverage objects
+ * @property {ObjectUtils} utils
+ * @static
+ */
+ utils: require('./lib/object-utils'),
+ /**
+ * asynchronously returns a function that can match filesystem paths.
+ * The function returned in the callback may be passed directly as a `matcher`
+ * to the functions in the `hook` module.
+ *
+ * When no options are passed, the match function is one that matches all JS
+ * files under the current working directory except ones under `node_modules`
+ *
+ * Match patterns are `ant`-style patterns processed using the `fileset` library.
+ * Examples not provided due to limitations in putting asterisks inside
+ * jsdoc comments. Please refer to tests under `test/other/test-matcher.js`
+ * for examples.
+ *
+ * @method matcherFor
+ * @static
+ * @param {Object} options Optional. Lookup options.
+ * @param {String} [options.root] the root of the filesystem tree under
+ * which to match files. Defaults to `process.cwd()`
+ * @param {Array} [options.includes] an array of include patterns to match.
+ * Defaults to all JS files under the root.
+ * @param {Array} [options.excludes] and array of exclude patterns. File paths
+ * matching these patterns will be excluded by the returned matcher.
+ * Defaults to files under `node_modules` found anywhere under root.
+ * @param {Function(err, matchFunction)} callback The callback that is
+ * called with two arguments. The first is an `Error` object in case
+ * of errors or a falsy value if there were no errors. The second
+ * is a function that may be use as a matcher.
+ */
+ matcherFor: require('./lib/util/file-matcher').matcherFor,
+ /**
+ * the version of the library
+ * @property {String} VERSION
+ * @static
+ */
+ VERSION: meta.VERSION,
+ //undocumented
+ _yuiLoadHook: require('./lib/util/yui-load-hook'),
+ //undocumented
+ TreeSummarizer: require('./lib/util/tree-summarizer'),
+ //undocumented
+ assetsDir: path.resolve(__dirname, 'lib', 'vendor')
+};
+
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/cli.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/cli.js
new file mode 100755
index 0000000..ec4245b
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/cli.js
@@ -0,0 +1,73 @@
+#!/usr/bin/env node
+
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+
+var Command = require('./command'),
+ inputError = require('./util/input-error'),
+ exit = process.exit; //hold a reference to original process.exit so that we are not affected even when a test changes it
+
+require('./register-plugins');
+
+function findCommandPosition(args) {
+ var i;
+
+ for (i = 0; i < args.length; i += 1) {
+ if (args[i].charAt(0) !== '-') {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+function errHandler (ex) {
+ if (!ex) { return; }
+ if (!ex.inputError) {
+ throw ex; // turn it into an uncaught exception
+ } else {
+ //don't print nasty traces but still exit(1)
+ console.error(ex.message);
+ console.error('Try "istanbul help" for usage');
+ exit(1);
+ }
+}
+
+function runCommand(args, callback) {
+ var pos = findCommandPosition(args),
+ command,
+ commandArgs,
+ commandObject;
+
+ if (pos < 0) {
+ return callback(inputError.create('Need a command to run'));
+ }
+
+ commandArgs = args.slice(0, pos);
+ command = args[pos];
+ commandArgs.push.apply(commandArgs, args.slice(pos + 1));
+
+ try {
+ commandObject = Command.create(command);
+ } catch (ex) {
+ errHandler(inputError.create(ex.message));
+ }
+ commandObject.run(commandArgs, errHandler);
+}
+
+function runToCompletion(args) {
+ runCommand(args, errHandler);
+}
+
+if (require.main === module) {
+ var args = Array.prototype.slice.call(process.argv, 2);
+ runToCompletion(args);
+}
+
+module.exports = {
+ runToCompletion: runToCompletion
+};
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/collector.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/collector.js
new file mode 100644
index 0000000..f22b23f
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/collector.js
@@ -0,0 +1,121 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+"use strict";
+var MemoryStore = require('./store/memory'),
+ utils = require('./object-utils');
+
+/**
+ * a mechanism to merge multiple coverage objects into one. Handles the use case
+ * of overlapping coverage information for the same files in multiple coverage
+ * objects and does not double-count in this situation. For example, if
+ * you pass the same coverage object multiple times, the final merged object will be
+ * no different that any of the objects passed in (except for execution counts).
+ *
+ * The `Collector` is built for scale to handle thousands of coverage objects.
+ * By default, all processing is done in memory since the common use-case is of
+ * one or a few coverage objects. You can work around memory
+ * issues by passing in a `Store` implementation that stores temporary computations
+ * on disk (the `tmp` store, for example).
+ *
+ * The `getFinalCoverage` method returns an object with merged coverage information
+ * and is provided as a convenience for implementors working with coverage information
+ * that can fit into memory. Reporters, in the interest of generality, should *not* use this method for
+ * creating reports.
+ *
+ * Usage
+ * -----
+ *
+ * var collector = new require('istanbul').Collector();
+ *
+ * files.forEach(function (f) {
+ * //each coverage object can have overlapping information about multiple files
+ * collector.add(JSON.parse(fs.readFileSync(f, 'utf8')));
+ * });
+ *
+ * collector.files().forEach(function(file) {
+ * var fileCoverage = collector.fileCoverageFor(file);
+ * console.log('Coverage for ' + file + ' is:' + JSON.stringify(fileCoverage));
+ * });
+ *
+ * // convenience method: do not use this when dealing with a large number of files
+ * var finalCoverage = collector.getFinalCoverage();
+ *
+ * @class Collector
+ * @constructor
+ * @param {Object} options Optional. Configuration options.
+ * @param {Store} options.store - an implementation of `Store` to use for temporary
+ * calculations.
+ */
+function Collector(options) {
+ options = options || {};
+ this.store = options.store || new MemoryStore();
+}
+
+Collector.prototype = {
+ /**
+ * adds a coverage object to the collector.
+ *
+ * @method add
+ * @param {Object} coverage the coverage object.
+ * @param {String} testName Optional. The name of the test used to produce the object.
+ * This is currently not used.
+ */
+ add: function (coverage /*, testName */) {
+ var store = this.store;
+ Object.keys(coverage).forEach(function (key) {
+ var fileCoverage = coverage[key];
+ if (store.hasKey(key)) {
+ store.setObject(key, utils.mergeFileCoverage(fileCoverage, store.getObject(key)));
+ } else {
+ store.setObject(key, fileCoverage);
+ }
+ });
+ },
+ /**
+ * returns a list of unique file paths for which coverage information has been added.
+ * @method files
+ * @return {Array} an array of file paths for which coverage information is present.
+ */
+ files: function () {
+ return this.store.keys();
+ },
+ /**
+ * return file coverage information for a single file
+ * @method fileCoverageFor
+ * @param {String} fileName the path for the file for which coverage information is
+ * required. Must be one of the values returned in the `files()` method.
+ * @return {Object} the coverage information for the specified file.
+ */
+ fileCoverageFor: function (fileName) {
+ var ret = this.store.getObject(fileName);
+ utils.addDerivedInfoForFile(ret);
+ return ret;
+ },
+ /**
+ * returns file coverage information for all files. This has the same format as
+ * any of the objects passed in to the `add` method. The number of keys in this
+ * object will be a superset of all keys found in the objects passed to `add()`
+ * @method getFinalCoverage
+ * @return {Object} the merged coverage information
+ */
+ getFinalCoverage: function () {
+ var ret = {},
+ that = this;
+ this.files().forEach(function (file) {
+ ret[file] = that.fileCoverageFor(file);
+ });
+ return ret;
+ },
+ /**
+ * disposes this collector and reclaims temporary resources used in the
+ * computation. Calls `dispose()` on the underlying store.
+ * @method dispose
+ */
+ dispose: function () {
+ this.store.dispose();
+ }
+};
+
+module.exports = Collector;
\ No newline at end of file
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/check-coverage.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/check-coverage.js
new file mode 100644
index 0000000..c2afeae
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/check-coverage.js
@@ -0,0 +1,115 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var nopt = require('nopt'),
+ path = require('path'),
+ fs = require('fs'),
+ Collector = require('../collector'),
+ formatOption = require('../util/help-formatter').formatOption,
+ util = require('util'),
+ utils = require('../object-utils'),
+ filesFor = require('../util/file-matcher').filesFor,
+ Command = require('./index');
+
+function CheckCoverageCommand() {
+ Command.call(this);
+}
+
+CheckCoverageCommand.TYPE = 'check-coverage';
+util.inherits(CheckCoverageCommand, Command);
+
+Command.mix(CheckCoverageCommand, {
+ synopsis: function () {
+ return "checks overall coverage against thresholds from coverage JSON files. Exits 1 if thresholds are not met, 0 otherwise";
+ },
+
+ usage: function () {
+ console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' []\n\nOptions are:\n\n' +
+ [
+ formatOption('--statements ', 'statement coverage threshold'),
+ formatOption('--functions ', 'function coverage threshold'),
+ formatOption('--branches ', 'branch coverage threshold'),
+ formatOption('--lines ', 'line coverage threshold')
+ ].join('\n\n') + '\n');
+
+ console.error('\n\n');
+
+ console.error('Thresholds, when specified as a positive number are taken to be the minimum percentage required.');
+ console.error('When a threshold is specified as a negative number it represents the maximum number of uncovered entities allowed.\n');
+ console.error('For example, --statements 90 implies minimum statement coverage is 90%.');
+ console.error(' --statements -10 implies that no more than 10 uncovered statements are allowed\n');
+ console.error(' is a fileset pattern that can be used to select one or more coverage files ' +
+ 'for merge. This defaults to "**/coverage*.json"');
+
+ console.error('\n');
+ },
+
+ run: function (args, callback) {
+
+ var config = {
+ root: path,
+ dir: path,
+ statements: Number,
+ lines: Number,
+ branches: Number,
+ functions: Number,
+ verbose: Boolean
+ },
+ opts = nopt(config, { v : '--verbose' }, args, 0),
+ includePattern = '**/coverage*.json',
+ root,
+ collector = new Collector(),
+ errors = [];
+
+ if (opts.argv.remain.length > 0) {
+ includePattern = opts.argv.remain[0];
+ }
+
+ root = opts.root || process.cwd();
+ filesFor({
+ root: root,
+ includes: [ includePattern ]
+ }, function (err, files) {
+ if (err) { throw err; }
+ files.forEach(function (file) {
+ var coverageObject = JSON.parse(fs.readFileSync(file, 'utf8'));
+ collector.add(coverageObject);
+ });
+ var thresholds = {
+ statements: opts.statements || 0,
+ branches: opts.branches || 0,
+ lines: opts.lines || 0,
+ functions: opts.functions || 0
+ },
+ actuals = utils.summarizeCoverage(collector.getFinalCoverage());
+
+ if (opts.verbose) {
+ console.log('Compare actuals against thresholds');
+ console.log(JSON.stringify({ actuals: actuals, thresholds: thresholds }, undefined, 4));
+ }
+
+ Object.keys(thresholds).forEach(function (key) {
+ var actual = actuals[key].pct,
+ actualUncovered = actuals[key].total - actuals[key].covered,
+ threshold = thresholds[key];
+
+ if (threshold < 0) {
+ if (threshold * -1 < actualUncovered) {
+ errors.push('ERROR: Uncovered count for ' + key + ' (' + actualUncovered + ') exceeds threshold (' + -1 * threshold + ')');
+ }
+ } else {
+ if (actual < threshold) {
+ errors.push('ERROR: Coverage for ' + key + ' (' + actual + '%) does not meet threshold (' + threshold + '%)');
+ }
+ }
+ });
+ return callback(errors.length === 0 ? null : errors.join("\n"));
+ });
+ }
+});
+
+module.exports = CheckCoverageCommand;
+
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/common/run-with-cover.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/common/run-with-cover.js
new file mode 100644
index 0000000..171d449
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/common/run-with-cover.js
@@ -0,0 +1,250 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+var Module = require('module'),
+ path = require('path'),
+ fs = require('fs'),
+ nopt = require('nopt'),
+ which = require('which'),
+ mkdirp = require('mkdirp'),
+ existsSync = fs.existsSync || path.existsSync,
+ inputError = require('../../util/input-error'),
+ matcherFor = require('../../util/file-matcher').matcherFor,
+ Instrumenter = require('../../instrumenter'),
+ Collector = require('../../collector'),
+ formatOption = require('../../util/help-formatter').formatOption,
+ hook = require('../../hook'),
+ Report = require('../../report'),
+ resolve = require('resolve'),
+ configuration = require('../../configuration');
+
+function usage(arg0, command) {
+
+ console.error('\nUsage: ' + arg0 + ' ' + command + ' [] [-- ]\n\nOptions are:\n\n'
+ + [
+ formatOption('--config ', 'the configuration file to use, defaults to .istanbul.yml'),
+ formatOption('--root ', 'the root path to look for files to instrument, defaults to .'),
+ formatOption('-x [-x ]', 'one or more fileset patterns e.g. "**/vendor/**"'),
+ formatOption('--[no-]default-excludes', 'apply default excludes [ **/node_modules/**, **/test/**, **/tests/** ], defaults to true'),
+ formatOption('--hook-run-in-context', 'hook vm.runInThisContext in addition to require (supports RequireJS), defaults to false'),
+ formatOption('--post-require-hook | ', 'JS module that exports a function for post-require processing'),
+ formatOption('--report [--report ] ', 'report format, defaults to lcov (= lcov.info + HTML)'),
+ formatOption('--dir ', 'report directory, defaults to ./coverage'),
+ formatOption('--print ', 'type of report to print to console, one of summary (default), detail, both or none'),
+ formatOption('--verbose, -v', 'verbose mode'),
+ formatOption('--[no-]preserve-comments', 'remove / preserve comments in the output, defaults to false'),
+ formatOption('--[no-]preload-sources', '`require` all sources before running tests, defaults to false')
+ ].join('\n\n') + '\n');
+ console.error('\n');
+}
+
+function run(args, commandName, enableHooks, callback) {
+
+ var template = {
+ config: path,
+ root: path,
+ x: [ Array, String ],
+ report: [Array, String ],
+ dir: path,
+ verbose: Boolean,
+ yui: Boolean,
+ 'default-excludes': Boolean,
+ print: String,
+ 'self-test': Boolean,
+ 'hook-run-in-context': Boolean,
+ 'post-require-hook': String,
+ 'preserve-comments': Boolean,
+ 'preload-sources': Boolean
+ },
+ opts = nopt(template, { v : '--verbose' }, args, 0),
+ overrides = {
+ verbose: opts.verbose,
+ instrumentation: {
+ root: opts.root,
+ 'default-excludes': opts['default-excludes'],
+ excludes: opts.x,
+ 'preload-sources': opts['preload-sources']
+ },
+ reporting: {
+ reports: opts.report,
+ print: opts.print,
+ dir: opts.dir
+ },
+ hooks: {
+ 'hook-run-in-context': opts['hook-run-in-context'],
+ 'post-require-hook': opts['post-require-hook'],
+ 'handle-sigint': opts['handle-sigint']
+ }
+ },
+ config = configuration.loadFile(opts.config, overrides),
+ watermarks = config.reporting.watermarks(),
+ reportOpts,
+ verbose = config.verbose,
+ cmdAndArgs = opts.argv.remain,
+ preserveComments = opts['preserve-comments'],
+ cmd,
+ cmdArgs,
+ reportingDir,
+ reports = [],
+ runFn,
+ excludes;
+
+ if (cmdAndArgs.length === 0) {
+ return callback(inputError.create('Need a filename argument for the ' + commandName + ' command!'));
+ }
+
+ cmd = cmdAndArgs.shift();
+ cmdArgs = cmdAndArgs;
+
+ if (!existsSync(cmd)) {
+ try {
+ cmd = which.sync(cmd);
+ } catch (ex) {
+ return callback(inputError.create('Unable to resolve file [' + cmd + ']'));
+ }
+ } else {
+ cmd = path.resolve(cmd);
+ }
+
+ runFn = function () {
+ process.argv = ["node", cmd].concat(cmdArgs);
+ if (verbose) {
+ console.log('Running: ' + process.argv.join(' '));
+ }
+ process.env.running_under_istanbul=1;
+ Module.runMain(cmd, null, true);
+ };
+
+ excludes = config.instrumentation.excludes(true);
+
+ if (enableHooks) {
+ reportingDir = path.resolve(config.reporting.dir());
+ reportOpts = { dir: reportingDir, watermarks: watermarks };
+ mkdirp.sync(reportingDir); //ensure we fail early if we cannot do this
+ reports.push.apply(reports, config.reporting.reports().map(function (r) {
+ return Report.create(r, reportOpts);
+ }));
+ if (config.reporting.print() !== 'none') {
+ switch (config.reporting.print()) {
+ case 'detail':
+ reports.push(Report.create('text', reportOpts));
+ break;
+ case 'both':
+ reports.push(Report.create('text', reportOpts));
+ reports.push(Report.create('text-summary', reportOpts));
+ break;
+ default:
+ reports.push(Report.create('text-summary', reportOpts));
+ break;
+ }
+ }
+
+ excludes.push(path.relative(process.cwd(), path.join(reportingDir, '**', '*')));
+ matcherFor({
+ root: config.instrumentation.root() || process.cwd(),
+ includes: [ '**/*.js' ],
+ excludes: excludes
+ },
+ function (err, matchFn) {
+ if (err) { return callback(err); }
+
+ var coverageVar = '$$cov_' + new Date().getTime() + '$$',
+ instrumenter = new Instrumenter({ coverageVariable: coverageVar , preserveComments: preserveComments}),
+ transformer = instrumenter.instrumentSync.bind(instrumenter),
+ hookOpts = { verbose: verbose },
+ postRequireHook = config.hooks.postRequireHook(),
+ postLoadHookFile;
+
+ if (postRequireHook) {
+ postLoadHookFile = path.resolve(postRequireHook);
+ } else if (opts.yui) { //EXPERIMENTAL code: do not rely on this in anyway until the docs say it is allowed
+ postLoadHookFile = path.resolve(__dirname, '../../util/yui-load-hook');
+ }
+
+ if (postRequireHook) {
+ if (!existsSync(postLoadHookFile)) { //assume it is a module name and resolve it
+ try {
+ postLoadHookFile = resolve.sync(postRequireHook, { basedir: process.cwd() });
+ } catch (ex) {
+ if (verbose) { console.error('Unable to resolve [' + postRequireHook + '] as a node module'); }
+ }
+ }
+ }
+ if (postLoadHookFile) {
+ if (verbose) { console.error('Use post-load-hook: ' + postLoadHookFile); }
+ hookOpts.postLoadHook = require(postLoadHookFile)(matchFn, transformer, verbose);
+ }
+
+ if (opts['self-test']) {
+ hook.unloadRequireCache(matchFn);
+ }
+ // runInThisContext is used by RequireJS [issue #23]
+ if (config.hooks.hookRunInContext()) {
+ hook.hookRunInThisContext(matchFn, transformer, hookOpts);
+ }
+ hook.hookRequire(matchFn, transformer, hookOpts);
+
+ //initialize the global variable to stop mocha from complaining about leaks
+ global[coverageVar] = {};
+
+ // enable passing --handle-sigint to write reports on SIGINT.
+ // This allows a user to manually kill a process while
+ // still getting the istanbul report.
+ if (config.hooks.handleSigint()) {
+ process.once('SIGINT', process.exit);
+ }
+
+ process.once('exit', function () {
+ var file = path.resolve(reportingDir, 'coverage.json'),
+ collector,
+ cov;
+ if (typeof global[coverageVar] === 'undefined' || Object.keys(global[coverageVar]).length === 0) {
+ console.error('No coverage information was collected, exit without writing coverage information');
+ return;
+ } else {
+ cov = global[coverageVar];
+ }
+ //important: there is no event loop at this point
+ //everything that happens in this exit handler MUST be synchronous
+ mkdirp.sync(reportingDir); //yes, do this again since some test runners could clean the dir initially created
+ if (config.reporting.print() !== 'none') {
+ console.error('=============================================================================');
+ console.error('Writing coverage object [' + file + ']');
+ }
+ fs.writeFileSync(file, JSON.stringify(cov), 'utf8');
+ collector = new Collector();
+ collector.add(cov);
+ if (config.reporting.print() !== 'none') {
+ console.error('Writing coverage reports at [' + reportingDir + ']');
+ console.error('=============================================================================');
+ }
+ reports.forEach(function (report) {
+ report.writeReport(collector, true);
+ });
+ return callback();
+ });
+ if (config.instrumentation.preloadSources()) {
+ matchFn.files.forEach(function (file) {
+ if (verbose) { console.error('Preload ' + file); }
+ try {
+ require(file);
+ } catch (ex) {
+ if (verbose) {
+ console.error('Unable to preload ' + file);
+ }
+ // swallow
+ }
+ });
+ }
+ runFn();
+ });
+ } else {
+ runFn();
+ }
+}
+
+module.exports = {
+ run: run,
+ usage: usage
+};
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/cover.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/cover.js
new file mode 100644
index 0000000..ee82429
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/cover.js
@@ -0,0 +1,33 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var runWithCover = require('./common/run-with-cover'),
+ util = require('util'),
+ Command = require('./index');
+
+function CoverCommand() {
+ Command.call(this);
+}
+
+CoverCommand.TYPE = 'cover';
+util.inherits(CoverCommand, Command);
+
+Command.mix(CoverCommand, {
+ synopsis: function () {
+ return "transparently adds coverage information to a node command. Saves coverage.json and reports at the end of execution";
+ },
+
+ usage: function () {
+ runWithCover.usage(this.toolName(), this.type());
+ },
+
+ run: function (args, callback) {
+ runWithCover.run(args, this.type(), true, callback);
+ }
+});
+
+
+module.exports = CoverCommand;
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/help.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/help.js
new file mode 100644
index 0000000..e150621
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/help.js
@@ -0,0 +1,90 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var Command = require('./index.js'),
+ util = require('util'),
+ formatOption = require('../util/help-formatter').formatOption,
+ VERSION = require('../../index').VERSION,
+ configuration = require('../configuration'),
+ yaml = require('js-yaml'),
+ formatPara = require('../util/help-formatter').formatPara;
+
+function showConfigHelp(toolName) {
+
+ console.error('\nConfiguring ' + toolName);
+ console.error('====================');
+ console.error('\n' +
+ formatPara(toolName + ' can be configured globally using a .istanbul.yml YAML file ' +
+ 'at the root of your source tree. Every command also accepts a --config= argument to ' +
+ 'customize its location per command. The alternate config file can be in YAML, JSON or node.js ' +
+ '(exporting the config object).'));
+ console.error('\n' +
+ formatPara('The config file currently has three sections for instrumentation, reporting and hooks. ' +
+ 'Note that certain commands (like `cover`) use information from multiple sections.'));
+ console.error('\n' +
+ formatPara('Keys in the config file usually correspond to command line parameters with the same name. ' +
+ 'The verbose option for every command shows you the exact configuration used'));
+
+ console.error('\nThe default configuration is as follows:\n');
+ console.error(yaml.safeDump(configuration.defaultConfig(), { indent: 4, flowLevel: 3 }));
+ console.error('\n' +
+ formatPara('The `watermarks` section does not have a command line equivalent. It allows you to set up ' +
+ 'low and high watermark percentages for reporting. These are honored by all reporters that colorize ' +
+ 'their output based on low/ medium/ high coverage'));
+}
+
+function HelpCommand() {
+ Command.call(this);
+}
+
+HelpCommand.TYPE = 'help';
+util.inherits(HelpCommand, Command);
+
+Command.mix(HelpCommand, {
+ synopsis: function () {
+ return "shows help";
+ },
+
+ usage: function () {
+
+ console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' config | \n');
+ console.error('`config` provides help with istanbul configuration\n');
+ console.error('Available commands are:\n');
+
+ var commandObj;
+ Command.getCommandList().forEach(function (cmd) {
+ commandObj = Command.create(cmd);
+ console.error(formatOption(cmd, commandObj.synopsis()));
+ console.error("\n");
+ });
+ console.error("Command names can be abbreviated as long as the abbreviation is unambiguous");
+ console.error(this.toolName() + ' version:' + VERSION);
+ console.error("\n");
+ },
+ run: function (args, callback) {
+ var command;
+ if (args.length === 0) {
+ this.usage();
+ } else {
+ if (args[0] === 'config') {
+ showConfigHelp(this.toolName());
+ } else {
+ try {
+ command = Command.create(args[0]);
+ command.usage('istanbul', Command.resolveCommandName(args[0]));
+ } catch (ex) {
+ console.error('Invalid command: ' + args[0]);
+ this.usage();
+ }
+ }
+ }
+ return callback();
+ }
+});
+
+
+module.exports = HelpCommand;
+
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/index.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/index.js
new file mode 100644
index 0000000..754cf1d
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/index.js
@@ -0,0 +1,33 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var Factory = require('../util/factory'),
+ factory = new Factory('command', __dirname, true);
+
+function Command() {}
+// add register, create, mix, loadAll, getCommandList, resolveCommandName to the Command object
+factory.bindClassMethods(Command);
+
+Command.prototype = {
+ toolName: function () {
+ return require('../util/meta').NAME;
+ },
+
+ type: function () {
+ return this.constructor.TYPE;
+ },
+ synopsis: /* istanbul ignore next: base method */ function () {
+ return "the developer has not written a one-line summary of the " + this.type() + " command";
+ },
+ usage: /* istanbul ignore next: base method */ function () {
+ console.error("the developer has not provided a usage for the " + this.type() + " command");
+ },
+ run: /* istanbul ignore next: abstract method */ function (args, callback) {
+ return callback(new Error("run: must be overridden for the " + this.type() + " command"));
+ }
+};
+
+module.exports = Command;
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/instrument.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/instrument.js
new file mode 100644
index 0000000..e72ab2c
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/instrument.js
@@ -0,0 +1,259 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var path = require('path'),
+ mkdirp = require('mkdirp'),
+ async = require('async'),
+ fs = require('fs'),
+ filesFor = require('../util/file-matcher').filesFor,
+ nopt = require('nopt'),
+ Instrumenter = require('../instrumenter'),
+ inputError = require('../util/input-error'),
+ formatOption = require('../util/help-formatter').formatOption,
+ util = require('util'),
+ Command = require('./index'),
+ Collector = require('../collector'),
+ flowControl = require('../util/flow-control'),
+ configuration = require('../configuration'),
+ verbose;
+
+
+/**
+ * Chunk file size to use when reading non JavaScript files in memory
+ * and copying them over when using complete-copy flag.
+ */
+var READ_FILE_CHUNK_SIZE = 64 * 1024;
+
+function BaselineCollector(instrumenter) {
+ this.instrumenter = instrumenter;
+ this.collector = new Collector();
+ this.instrument = instrumenter.instrument.bind(this.instrumenter);
+
+ var origInstrumentSync = instrumenter.instrumentSync;
+ this.instrumentSync = function () {
+ var args = Array.prototype.slice.call(arguments),
+ ret = origInstrumentSync.apply(this.instrumenter, args),
+ baseline = this.instrumenter.lastFileCoverage(),
+ coverage = {};
+ coverage[baseline.path] = baseline;
+ this.collector.add(coverage);
+ return ret;
+ };
+ //monkey patch the instrumenter to call our version instead
+ instrumenter.instrumentSync = this.instrumentSync.bind(this);
+}
+
+BaselineCollector.prototype = {
+ getCoverage: function () {
+ return this.collector.getFinalCoverage();
+ }
+};
+
+
+function processFiles(instrumenter, inputDir, outputDir, relativeNames) {
+ var processor = function (name, callback) {
+ var inputFile = path.resolve(inputDir, name),
+ outputFile = path.resolve(outputDir, name),
+ inputFileExtenstion = path.extname(inputFile),
+ isJavaScriptFile = (inputFileExtenstion === '.js'),
+ oDir = path.dirname(outputFile),
+ readStream, writeStream;
+
+ callback = flowControl.callOnce(callback);
+ mkdirp.sync(oDir);
+
+ if (fs.statSync(inputFile).isDirectory()) {
+ return callback(null, name);
+ }
+
+ if (isJavaScriptFile) {
+ fs.readFile(inputFile, 'utf8', function (err, data) {
+ if (err) { return callback(err, name); }
+ instrumenter.instrument(data, inputFile, function (iErr, instrumented) {
+ if (iErr) { return callback(iErr, name); }
+ fs.writeFile(outputFile, instrumented, 'utf8', function (err) {
+ return callback(err, name);
+ });
+ });
+ });
+ }
+ else {
+ // non JavaScript file, copy it as is
+ readStream = fs.createReadStream(inputFile, {'bufferSize': READ_FILE_CHUNK_SIZE});
+ writeStream = fs.createWriteStream(outputFile);
+
+ readStream.on('error', callback);
+ writeStream.on('error', callback);
+
+ readStream.pipe(writeStream);
+ readStream.on('end', function() {
+ callback(null, name);
+ });
+ }
+ },
+ q = async.queue(processor, 10),
+ errors = [],
+ count = 0,
+ startTime = new Date().getTime();
+
+ q.push(relativeNames, function (err, name) {
+ var inputFile, outputFile;
+ if (err) {
+ errors.push({ file: name, error: err.message || err.toString() });
+ inputFile = path.resolve(inputDir, name);
+ outputFile = path.resolve(outputDir, name);
+ fs.writeFileSync(outputFile, fs.readFileSync(inputFile));
+ }
+ if (verbose) {
+ console.log('Processed: ' + name);
+ } else {
+ if (count % 100 === 0) { process.stdout.write('.'); }
+ }
+ count += 1;
+ });
+
+ q.drain = function () {
+ var endTime = new Date().getTime();
+ console.log('\nProcessed [' + count + '] files in ' + Math.floor((endTime - startTime) / 1000) + ' secs');
+ if (errors.length > 0) {
+ console.log('The following ' + errors.length + ' file(s) had errors and were copied as-is');
+ console.log(errors);
+ }
+ };
+}
+
+
+function InstrumentCommand() {
+ Command.call(this);
+}
+
+InstrumentCommand.TYPE = 'instrument';
+util.inherits(InstrumentCommand, Command);
+
+Command.mix(InstrumentCommand, {
+ synopsis: function synopsis() {
+ return "instruments a file or a directory tree and writes the instrumented code to the desired output location";
+ },
+
+ usage: function () {
+ console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' \n\nOptions are:\n\n' +
+ [
+ formatOption('--config ', 'the configuration file to use, defaults to .istanbul.yml'),
+ formatOption('--output ', 'The output file or directory. This is required when the input is a directory, ' +
+ 'defaults to standard output when input is a file'),
+ formatOption('-x [-x ]', 'one or more fileset patterns (e.g. "**/vendor/**" to ignore all files ' +
+ 'under a vendor directory). Also see the --default-excludes option'),
+ formatOption('--variable ', 'change the variable name of the global coverage variable from the ' +
+ 'default value of `__coverage__` to something else'),
+ formatOption('--embed-source', 'embed source code into the coverage object, defaults to false'),
+ formatOption('--[no-]compact', 'produce [non]compact output, defaults to compact'),
+ formatOption('--[no-]preserve-comments', 'remove / preserve comments in the output, defaults to false'),
+ formatOption('--[no-]complete-copy', 'also copy non-javascript files to the ouput directory as is, defaults to false'),
+ formatOption('--save-baseline', 'produce a baseline coverage.json file out of all files instrumented'),
+ formatOption('--baseline-file ', 'filename of baseline file, defaults to coverage/coverage-baseline.json')
+ ].join('\n\n') + '\n');
+ console.error('\n');
+ },
+
+ run: function (args, callback) {
+
+ var template = {
+ config: path,
+ output: path,
+ x: [Array, String],
+ variable: String,
+ compact: Boolean,
+ 'complete-copy': Boolean,
+ verbose: Boolean,
+ 'save-baseline': Boolean,
+ 'baseline-file': path,
+ 'embed-source': Boolean,
+ 'preserve-comments': Boolean
+ },
+ opts = nopt(template, { v : '--verbose' }, args, 0),
+ overrides = {
+ verbose: opts.verbose,
+ instrumentation: {
+ variable: opts.variable,
+ compact: opts.compact,
+ 'embed-source': opts['embed-source'],
+ 'preserve-comments': opts['preserve-comments'],
+ excludes: opts.x,
+ 'complete-copy': opts['complete-copy'],
+ 'save-baseline': opts['save-baseline'],
+ 'baseline-file': opts['baseline-file']
+ }
+ },
+ config = configuration.loadFile(opts.config, overrides),
+ iOpts = config.instrumentation,
+ cmdArgs = opts.argv.remain,
+ file,
+ stats,
+ stream,
+ includes,
+ instrumenter,
+ needBaseline = iOpts.saveBaseline(),
+ baselineFile = path.resolve(iOpts.baselineFile()),
+ output = opts.output;
+
+ verbose = config.verbose;
+ if (cmdArgs.length !== 1) {
+ return callback(inputError.create('Need exactly one filename/ dirname argument for the instrument command!'));
+ }
+
+ if (iOpts.completeCopy()) {
+ includes = ['**/*'];
+ }
+ else {
+ includes = ['**/*.js'];
+ }
+
+ instrumenter = new Instrumenter({
+ coverageVariable: iOpts.variable(),
+ embedSource: iOpts.embedSource(),
+ noCompact: !iOpts.compact(),
+ preserveComments: iOpts.preserveComments()
+ });
+
+ if (needBaseline) {
+ mkdirp.sync(path.dirname(baselineFile));
+ instrumenter = new BaselineCollector(instrumenter);
+ process.on('exit', function () {
+ util.puts('Saving baseline coverage at: ' + baselineFile);
+ fs.writeFileSync(baselineFile, JSON.stringify(instrumenter.getCoverage()), 'utf8');
+ });
+ }
+
+ file = path.resolve(cmdArgs[0]);
+ stats = fs.statSync(file);
+ if (stats.isDirectory()) {
+ if (!output) { return callback(inputError.create('Need an output directory [-o ] when input is a directory!')); }
+ if (output === file) { return callback(inputError.create('Cannot instrument into the same directory/ file as input!')); }
+ mkdirp.sync(output);
+ filesFor({
+ root: file,
+ includes: includes,
+ excludes: opts.x || iOpts.excludes(false), // backwards-compat, *sigh*
+ relative: true
+ }, function (err, files) {
+ if (err) { return callback(err); }
+ processFiles(instrumenter, file, output, files);
+ });
+ } else {
+ if (output) {
+ stream = fs.createWriteStream(output);
+ } else {
+ stream = process.stdout;
+ }
+ stream.write(instrumenter.instrumentSync(fs.readFileSync(file, 'utf8'), file));
+ if (stream !== process.stdout) {
+ stream.end();
+ }
+ }
+ }
+});
+
+module.exports = InstrumentCommand;
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/report.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/report.js
new file mode 100644
index 0000000..c2301f4
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/report.js
@@ -0,0 +1,110 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var nopt = require('nopt'),
+ Report = require('../report'),
+ path = require('path'),
+ fs = require('fs'),
+ Collector = require('../collector'),
+ inputError = require('../util/input-error'),
+ formatOption = require('../util/help-formatter').formatOption,
+ filesFor = require('../util/file-matcher').filesFor,
+ util = require('util'),
+ Command = require('./index'),
+ configuration = require('../configuration');
+
+function ReportCommand() {
+ Command.call(this);
+}
+
+ReportCommand.TYPE = 'report';
+util.inherits(ReportCommand, Command);
+
+Command.mix(ReportCommand, {
+ synopsis: function () {
+ return "writes reports for coverage JSON objects produced in a previous run";
+ },
+
+ usage: function () {
+ console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' [ [] ]\n\nOptions are:\n\n' +
+ [
+ formatOption('--config ', 'the configuration file to use, defaults to .istanbul.yml'),
+ formatOption('--root ', 'The input root directory for finding coverage files'),
+ formatOption('--dir ', 'The output directory where files will be written. This defaults to ./coverage/'),
+ formatOption('--verbose, -v', 'verbose mode')
+ ].join('\n\n') + '\n');
+
+ console.error('\n');
+
+ console.error(' is one of html, lcovonly, lcov (html + lcovonly), cobertura, text-summary, text, ' +
+ 'teamcity, or clover. Default is lcov');
+ console.error(' is a fileset pattern that can be used to select one or more coverage files ' +
+ 'for merged reporting. This defaults to "**/coverage*.json"');
+
+ console.error('\n');
+ },
+
+ run: function (args, callback) {
+
+ var template = {
+ config: path,
+ root: path,
+ dir: path,
+ verbose: Boolean
+ },
+ opts = nopt(template, { v : '--verbose' }, args, 0),
+ fmtAndArgs = opts.argv.remain,
+ fmt = 'lcov',
+ includePattern = '**/coverage*.json',
+ reporter,
+ root,
+ collector = new Collector(),
+ config = configuration.loadFile(opts.config, {
+ verbose: opts.verbose,
+ reporting: {
+ dir: opts.dir
+ }
+ }),
+ reportOpts = {
+ verbose: config.verbose,
+ dir: config.reporting.dir(),
+ watermarks: config.reporting.watermarks()
+ };
+
+ if (fmtAndArgs.length > 0) {
+ fmt = fmtAndArgs[0];
+ }
+
+ if (fmtAndArgs.length > 1) {
+ includePattern = fmtAndArgs[1];
+ }
+
+ try {
+ reporter = Report.create(fmt, reportOpts);
+ } catch (ex) {
+ return callback(inputError.create('Invalid report format [' + fmt + ']'));
+ }
+
+ root = opts.root || process.cwd();
+ filesFor({
+ root: root,
+ includes: [ includePattern ]
+ }, function (err, files) {
+ if (err) { throw err; }
+ files.forEach(function (file) {
+ var coverageObject = JSON.parse(fs.readFileSync(file, 'utf8'));
+ collector.add(coverageObject);
+ });
+ console.log('Using reporter [' + fmt + ']');
+ reporter.writeReport(collector);
+ console.log('Done');
+ return callback();
+ });
+ }
+});
+
+module.exports = ReportCommand;
+
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/test.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/test.js
new file mode 100644
index 0000000..5930507
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/command/test.js
@@ -0,0 +1,31 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var runWithCover = require('./common/run-with-cover'),
+ util = require('util'),
+ Command = require('./index');
+
+function TestCommand() {
+ Command.call(this);
+}
+
+TestCommand.TYPE = 'test';
+util.inherits(TestCommand, Command);
+
+Command.mix(TestCommand, {
+ synopsis: function () {
+ return "cover a node command only when npm_config_coverage is set. Use in an `npm test` script for conditional coverage";
+ },
+
+ usage: function () {
+ runWithCover.usage(this.toolName(), this.type());
+ },
+
+ run: function (args, callback) {
+ runWithCover.run(args, this.type(), !!process.env.npm_config_coverage, callback);
+ }
+});
+
+module.exports = TestCommand;
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/configuration.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/configuration.js
new file mode 100644
index 0000000..b7ee8d1
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/configuration.js
@@ -0,0 +1,223 @@
+/*
+ Copyright (c) 2013, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+var path = require('path'),
+ fs = require('fs'),
+ CAMEL_PATTERN = /([a-z])([A-Z])/g,
+ YML_PATTERN = /\.ya?ml$/,
+ yaml = require('js-yaml'),
+ defaults = require('./report/common/defaults');
+
+function defaultConfig() {
+ var ret = {
+ verbose: false,
+ instrumentation: {
+ root: '.',
+ 'default-excludes': true,
+ excludes: [],
+ 'embed-source': false,
+ variable: '__coverage__',
+ compact: true,
+ 'preserve-comments': false,
+ 'complete-copy': false,
+ 'save-baseline': false,
+ 'baseline-file': './coverage/coverage-baseline.json',
+ 'preload-sources': false
+ },
+ reporting: {
+ print: 'summary',
+ reports: [ 'lcov' ],
+ dir: './coverage'
+ },
+ hooks: {
+ 'hook-run-in-context': false,
+ 'post-require-hook': null,
+ 'handle-sigint': false
+ }
+ };
+ ret.reporting.watermarks = defaults.watermarks();
+ return ret;
+}
+
+function dasherize(word) {
+ return word.replace(CAMEL_PATTERN, function (match, lch, uch) {
+ return lch + '-' + uch.toLowerCase();
+ });
+}
+function isScalar(v) {
+ if (v === null) { return true; }
+ return v !== undefined && !Array.isArray(v) && typeof v !== 'object';
+}
+
+function isObject(v) {
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
+}
+
+function mergeObjects(explicit, template) {
+
+ var ret = {};
+
+ Object.keys(template).forEach(function (k) {
+ var v1 = template[k],
+ v2 = explicit[k];
+
+ if (Array.isArray(v1)) {
+ ret[k] = Array.isArray(v2) && v2.length > 0 ? v2 : v1;
+ } else if (isObject(v1)) {
+ v2 = isObject(v2) ? v2 : {};
+ ret[k] = mergeObjects(v2, v1);
+ } else {
+ ret[k] = isScalar(v2) ? v2 : v1;
+ }
+ });
+ return ret;
+}
+
+function mergeDefaults(explicit, implicit) {
+ return mergeObjects(explicit || {}, implicit);
+}
+
+function addMethods() {
+ var args = Array.prototype.slice.call(arguments),
+ cons = args.shift();
+
+ args.forEach(function (arg) {
+ var method = arg,
+ property = dasherize(arg);
+ cons.prototype[method] = function () {
+ return this.config[property];
+ };
+ });
+}
+
+function InstrumentOptions(config) {
+ this.config = config;
+}
+
+addMethods(InstrumentOptions,
+ 'defaultExcludes', 'completeCopy',
+ 'embedSource', 'variable', 'compact', 'preserveComments',
+ 'saveBaseline', 'baselineFile',
+ 'preloadSources');
+
+InstrumentOptions.prototype.root = function () { return path.resolve(this.config.root); };
+InstrumentOptions.prototype.excludes = function (excludeTests) {
+ var defs;
+ if (this.defaultExcludes()) {
+ defs = [ '**/node_modules/**' ];
+ if (excludeTests) {
+ defs = defs.concat(['**/test/**', '**/tests/**']);
+ }
+ return defs.concat(this.config.excludes);
+ }
+ return this.config.excludes;
+};
+
+function ReportingOptions(config) {
+ this.config = config;
+}
+
+addMethods(ReportingOptions, 'print', 'reports', 'dir');
+
+function isInvalidMark(v, key) {
+ var prefix = 'Watermark for [' + key + '] :';
+
+ if (v.length !== 2) {
+ return prefix + 'must be an array of length 2';
+ }
+ v[0] = Number(v[0]);
+ v[1] = Number(v[1]);
+
+ if (isNaN(v[0]) || isNaN(v[1])) {
+ return prefix + 'must have valid numbers';
+ }
+ if (v[0] < 0 || v[1] < 0) {
+ return prefix + 'must be positive numbers';
+ }
+ if (v[1] > 100) {
+ return prefix + 'cannot exceed 100';
+ }
+ if (v[1] <= v[0]) {
+ return prefix + 'low must be less than high';
+ }
+ return null;
+}
+
+ReportingOptions.prototype.watermarks = function () {
+ var v = this.config.watermarks,
+ defs = defaults.watermarks(),
+ ret = {};
+
+ Object.keys(defs).forEach(function (k) {
+ var mark = v[k], //it will already be a non-zero length array because of the way the merge works
+ message = isInvalidMark(mark, k);
+ if (message) {
+ console.error(message);
+ ret[k] = defs[k];
+ } else {
+ ret[k] = mark;
+ }
+ });
+ return ret;
+};
+
+function HookOptions(config) {
+ this.config = config;
+}
+
+addMethods(HookOptions, 'hookRunInContext', 'postRequireHook', 'handleSigint');
+
+function Configuration(obj, overrides) {
+
+ var config = mergeDefaults(obj, defaultConfig());
+ if (isObject(overrides)) {
+ config = mergeDefaults(overrides, config);
+ }
+ if (config.verbose) {
+ console.error('Using configuration');
+ console.error('-------------------');
+ console.error(yaml.safeDump(config, { indent: 4, flowLevel: 3 }));
+ console.error('-------------------\n');
+ }
+ this.verbose = config.verbose;
+ this.instrumentation = new InstrumentOptions(config.instrumentation);
+ this.reporting = new ReportingOptions(config.reporting);
+ this.hooks = new HookOptions(config.hooks);
+ //this.thresholds = new ThresholdOptions(config.thresholds);
+}
+
+function loadFile(file, overrides) {
+ var defaultConfigFile = path.resolve('.istanbul.yml'),
+ configObject;
+
+ if (file) {
+ if (!fs.existsSync(file)) {
+ throw new Error('Invalid configuration file specified:' + file);
+ }
+ } else {
+ if (fs.existsSync(defaultConfigFile)) {
+ file = defaultConfigFile;
+ }
+ }
+
+ if (file) {
+ console.error('Loading config: ' + file);
+ configObject = file.match(YML_PATTERN) ?
+ yaml.safeLoad(fs.readFileSync(file, 'utf8'), { filename: file }) :
+ require(path.resolve(file));
+ }
+
+ return new Configuration(configObject, overrides);
+}
+
+function loadObject(obj, overrides) {
+ return new Configuration(obj, overrides);
+}
+
+module.exports = {
+ loadFile: loadFile,
+ loadObject: loadObject,
+ defaultConfig: defaultConfig
+};
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/hook.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/hook.js
new file mode 100644
index 0000000..d60a0de
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/hook.js
@@ -0,0 +1,187 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+/**
+ * provides a mechanism to transform code in the scope of `require` or `vm.createScript`.
+ * This mechanism is general and relies on a user-supplied `matcher` function that determines when transformations should be
+ * performed and a user-supplied `transformer` function that performs the actual transform.
+ * Instrumenting code for coverage is one specific example of useful hooking.
+ *
+ * Note that both the `matcher` and `transformer` must execute synchronously.
+ *
+ * For the common case of matching filesystem paths based on inclusion/ exclusion patterns, use the `matcherFor`
+ * function in the istanbul API to get a matcher.
+ *
+ * It is up to the transformer to perform processing with side-effects, such as caching, storing the original
+ * source code to disk in case of dynamically generated scripts etc. The `Store` class can help you with this.
+ *
+ * Usage
+ * -----
+ *
+ * var hook = require('istanbul').hook,
+ * myMatcher = function (file) { return file.match(/foo/); },
+ * myTransformer = function (code, file) { return 'console.log("' + file + '");' + code; };
+ *
+ * hook.hookRequire(myMatcher, myTransformer);
+ *
+ * var foo = require('foo'); //will now print foo's module path to console
+ *
+ * @class Hook
+ */
+var path = require('path'),
+ fs = require('fs'),
+ Module = require('module'),
+ vm = require('vm'),
+ originalLoader = Module._extensions['.js'],
+ originalCreateScript = vm.createScript,
+ originalRunInThisContext = vm.runInThisContext;
+
+function transformFn(matcher, transformer, verbose) {
+
+ return function (code, filename) {
+ var shouldHook = typeof filename === 'string' && matcher(path.resolve(filename)),
+ transformed,
+ changed = false;
+
+ if (shouldHook) {
+ if (verbose) {
+ console.error('Module load hook: transform [' + filename + ']');
+ }
+ try {
+ transformed = transformer(code, filename);
+ changed = true;
+ } catch (ex) {
+ console.error('Transformation error; return original code');
+ console.error(ex);
+ transformed = code;
+ }
+ } else {
+ transformed = code;
+ }
+ return { code: transformed, changed: changed };
+ };
+}
+
+function unloadRequireCache(matcher) {
+ if (matcher && typeof require !== 'undefined' && require && require.cache) {
+ Object.keys(require.cache).forEach(function (filename) {
+ if (matcher(filename)) {
+ delete require.cache[filename];
+ }
+ });
+ }
+}
+/**
+ * hooks `require` to return transformed code to the node module loader.
+ * Exceptions in the transform result in the original code being used instead.
+ * @method hookRequire
+ * @static
+ * @param matcher {Function(filePath)} a function that is called with the absolute path to the file being
+ * `require`-d. Should return a truthy value when transformations need to be applied to the code, a falsy value otherwise
+ * @param transformer {Function(code, filePath)} a function called with the original code and the associated path of the file
+ * from where the code was loaded. Should return the transformed code.
+ * @param options {Object} options Optional.
+ * @param {Boolean} [options.verbose] write a line to standard error every time the transformer is called
+ * @param {Function} [options.postLoadHook] a function that is called with the name of the file being
+ * required. This is called after the require is processed irrespective of whether it was transformed.
+ */
+function hookRequire(matcher, transformer, options) {
+ options = options || {};
+ var fn = transformFn(matcher, transformer, options.verbose),
+ postLoadHook = options.postLoadHook &&
+ typeof options.postLoadHook === 'function' ? options.postLoadHook : null;
+
+ Module._extensions['.js'] = function (module, filename) {
+ var ret = fn(fs.readFileSync(filename, 'utf8'), filename);
+ if (ret.changed) {
+ module._compile(ret.code, filename);
+ } else {
+ originalLoader(module, filename);
+ }
+ if (postLoadHook) {
+ postLoadHook(filename);
+ }
+ };
+}
+/**
+ * unhook `require` to restore it to its original state.
+ * @method unhookRequire
+ * @static
+ */
+function unhookRequire() {
+ Module._extensions['.js'] = originalLoader;
+}
+/**
+ * hooks `vm.createScript` to return transformed code out of which a `Script` object will be created.
+ * Exceptions in the transform result in the original code being used instead.
+ * @method hookCreateScript
+ * @static
+ * @param matcher {Function(filePath)} a function that is called with the filename passed to `vm.createScript`
+ * Should return a truthy value when transformations need to be applied to the code, a falsy value otherwise
+ * @param transformer {Function(code, filePath)} a function called with the original code and the filename passed to
+ * `vm.createScript`. Should return the transformed code.
+ * @param options {Object} options Optional.
+ * @param {Boolean} [options.verbose] write a line to standard error every time the transformer is called
+ */
+function hookCreateScript(matcher, transformer, opts) {
+ opts = opts || {};
+ var fn = transformFn(matcher, transformer, opts.verbose);
+ vm.createScript = function (code, file) {
+ var ret = fn(code, file);
+ return originalCreateScript(ret.code, file);
+ };
+}
+
+/**
+ * unhooks vm.createScript, restoring it to its original state.
+ * @method unhookCreateScript
+ * @static
+ */
+function unhookCreateScript() {
+ vm.createScript = originalCreateScript;
+}
+
+
+/**
+ * hooks `vm.runInThisContext` to return transformed code.
+ * @method hookRunInThisContext
+ * @static
+ * @param matcher {Function(filePath)} a function that is called with the filename passed to `vm.createScript`
+ * Should return a truthy value when transformations need to be applied to the code, a falsy value otherwise
+ * @param transformer {Function(code, filePath)} a function called with the original code and the filename passed to
+ * `vm.createScript`. Should return the transformed code.
+ * @param options {Object} options Optional.
+ * @param {Boolean} [options.verbose] write a line to standard error every time the transformer is called
+ */
+function hookRunInThisContext(matcher, transformer, opts) {
+ opts = opts || {};
+ var fn = transformFn(matcher, transformer, opts.verbose);
+ vm.runInThisContext = function (code, file) {
+ var ret = fn(code, file);
+ return originalRunInThisContext(ret.code, file);
+ };
+}
+
+/**
+ * unhooks vm.runInThisContext, restoring it to its original state.
+ * @method unhookRunInThisContext
+ * @static
+ */
+function unhookRunInThisContext() {
+ vm.runInThisContext = originalRunInThisContext;
+}
+
+
+module.exports = {
+ hookRequire: hookRequire,
+ unhookRequire: unhookRequire,
+ hookCreateScript: hookCreateScript,
+ unhookCreateScript: unhookCreateScript,
+ hookRunInThisContext : hookRunInThisContext,
+ unhookRunInThisContext : unhookRunInThisContext,
+ unloadRequireCache: unloadRequireCache
+};
+
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/instrumenter.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/instrumenter.js
new file mode 100644
index 0000000..9259eed
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/instrumenter.js
@@ -0,0 +1,966 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+/*global esprima, escodegen, window */
+(function (isNode) {
+ "use strict";
+ var SYNTAX,
+ nodeType,
+ ESP = isNode ? require('esprima') : esprima,
+ ESPGEN = isNode ? require('escodegen') : escodegen, //TODO - package as dependency
+ crypto = isNode ? require('crypto') : null,
+ LEADER_WRAP = '(function () { ',
+ TRAILER_WRAP = '\n}());',
+ COMMENT_RE = /^\s*istanbul\s+ignore\s+(if|else|next)(?=\W|$)/,
+ astgen,
+ preconditions,
+ cond,
+ isArray = Array.isArray;
+
+ /* istanbul ignore if: untestable */
+ if (!isArray) {
+ isArray = function (thing) { return thing && Object.prototype.toString.call(thing) === '[object Array]'; };
+ }
+
+ if (!isNode) {
+ preconditions = {
+ 'Could not find esprima': ESP,
+ 'Could not find escodegen': ESPGEN,
+ 'JSON object not in scope': JSON,
+ 'Array does not implement push': [].push,
+ 'Array does not implement unshift': [].unshift
+ };
+ /* istanbul ignore next: untestable */
+ for (cond in preconditions) {
+ if (preconditions.hasOwnProperty(cond)) {
+ if (!preconditions[cond]) { throw new Error(cond); }
+ }
+ }
+ }
+
+ function generateTrackerVar(filename, omitSuffix) {
+ var hash, suffix;
+ if (crypto !== null) {
+ hash = crypto.createHash('md5');
+ hash.update(filename);
+ suffix = hash.digest('base64');
+ //trim trailing equal signs, turn identifier unsafe chars to safe ones + => _ and / => $
+ suffix = suffix.replace(new RegExp('=', 'g'), '')
+ .replace(new RegExp('\\+', 'g'), '_')
+ .replace(new RegExp('/', 'g'), '$');
+ } else {
+ window.__cov_seq = window.__cov_seq || 0;
+ window.__cov_seq += 1;
+ suffix = window.__cov_seq;
+ }
+ return '__cov_' + (omitSuffix ? '' : suffix);
+ }
+
+ function pushAll(ary, thing) {
+ if (!isArray(thing)) {
+ thing = [ thing ];
+ }
+ Array.prototype.push.apply(ary, thing);
+ }
+
+ SYNTAX = {
+ ArrayExpression: [ 'elements' ],
+ AssignmentExpression: ['left', 'right'],
+ BinaryExpression: ['left', 'right' ],
+ BlockStatement: [ 'body' ],
+ BreakStatement: [ 'label' ],
+ CallExpression: [ 'callee', 'arguments'],
+ CatchClause: ['param', 'body'],
+ ConditionalExpression: [ 'test', 'consequent', 'alternate' ],
+ ContinueStatement: [ 'label' ],
+ DebuggerStatement: [ ],
+ DoWhileStatement: [ 'test', 'body' ],
+ EmptyStatement: [],
+ ExpressionStatement: [ 'expression'],
+ ForInStatement: [ 'left', 'right', 'body' ],
+ ForStatement: ['init', 'test', 'update', 'body' ],
+ FunctionDeclaration: ['id', 'params', 'body'],
+ FunctionExpression: ['id', 'params', 'defaults', 'body'],
+ Identifier: [],
+ IfStatement: ['test', 'consequent', 'alternate'],
+ LabeledStatement: ['label', 'body'],
+ Literal: [],
+ LogicalExpression: [ 'left', 'right' ],
+ MemberExpression: ['object', 'property'],
+ NewExpression: ['callee', 'arguments'],
+ ObjectExpression: [ 'properties' ],
+ Program: [ 'body' ],
+ Property: [ 'key', 'value'],
+ ReturnStatement: ['argument'],
+ SequenceExpression: ['expressions'],
+ SwitchCase: [ 'test', 'consequent' ],
+ SwitchStatement: ['discriminant', 'cases' ],
+ ThisExpression: [],
+ ThrowStatement: ['argument'],
+ TryStatement: [ 'block', 'handlers', 'finalizer' ],
+ UnaryExpression: ['argument'],
+ UpdateExpression: [ 'argument' ],
+ VariableDeclaration: [ 'declarations' ],
+ VariableDeclarator: [ 'id', 'init' ],
+ WhileStatement: [ 'test', 'body' ],
+ WithStatement: [ 'object', 'body' ]
+
+ };
+
+ for (nodeType in SYNTAX) {
+ /* istanbul ignore else: has own property */
+ if (SYNTAX.hasOwnProperty(nodeType)) {
+ SYNTAX[nodeType] = { name: nodeType, children: SYNTAX[nodeType] };
+ }
+ }
+
+ astgen = {
+ variable: function (name) { return { type: SYNTAX.Identifier.name, name: name }; },
+ stringLiteral: function (str) { return { type: SYNTAX.Literal.name, value: String(str) }; },
+ numericLiteral: function (num) { return { type: SYNTAX.Literal.name, value: Number(num) }; },
+ statement: function (contents) { return { type: SYNTAX.ExpressionStatement.name, expression: contents }; },
+ dot: function (obj, field) { return { type: SYNTAX.MemberExpression.name, computed: false, object: obj, property: field }; },
+ subscript: function (obj, sub) { return { type: SYNTAX.MemberExpression.name, computed: true, object: obj, property: sub }; },
+ postIncrement: function (obj) { return { type: SYNTAX.UpdateExpression.name, operator: '++', prefix: false, argument: obj }; },
+ sequence: function (one, two) { return { type: SYNTAX.SequenceExpression.name, expressions: [one, two] }; }
+ };
+
+ function Walker(walkMap, preprocessor, scope, debug) {
+ this.walkMap = walkMap;
+ this.preprocessor = preprocessor;
+ this.scope = scope;
+ this.debug = debug;
+ if (this.debug) {
+ this.level = 0;
+ this.seq = true;
+ }
+ }
+
+ function defaultWalker(node, walker) {
+
+ var type = node.type,
+ preprocessor,
+ postprocessor,
+ children = SYNTAX[type].children,
+ // don't run generated nodes thru custom walks otherwise we will attempt to instrument the instrumentation code :)
+ applyCustomWalker = !!node.loc || node.type === SYNTAX.Program.name,
+ walkerFn = applyCustomWalker ? walker.walkMap[type] : null,
+ i,
+ j,
+ walkFnIndex,
+ childType,
+ childNode,
+ ret,
+ childArray,
+ childElement,
+ pathElement,
+ assignNode,
+ isLast;
+
+ /* istanbul ignore if: guard */
+ if (node.walking) { throw new Error('Infinite regress: Custom walkers may NOT call walker.apply(node)'); }
+ node.walking = true;
+
+ ret = walker.apply(node, walker.preprocessor);
+
+ preprocessor = ret.preprocessor;
+ if (preprocessor) {
+ delete ret.preprocessor;
+ ret = walker.apply(node, preprocessor);
+ }
+
+ if (isArray(walkerFn)) {
+ for (walkFnIndex = 0; walkFnIndex < walkerFn.length; walkFnIndex += 1) {
+ isLast = walkFnIndex === walkerFn.length - 1;
+ ret = walker.apply(ret, walkerFn[walkFnIndex]);
+ /*istanbul ignore next: paranoid check */
+ if (ret.type !== type && !isLast) {
+ throw new Error('Only the last walker is allowed to change the node type: [type was: ' + type + ' ]');
+ }
+ }
+ } else {
+ if (walkerFn) {
+ ret = walker.apply(node, walkerFn);
+ }
+ }
+
+ for (i = 0; i < children.length; i += 1) {
+ childType = children[i];
+ childNode = node[childType];
+ if (childNode && !childNode.skipWalk) {
+ pathElement = { node: node, property: childType };
+ if (isArray(childNode)) {
+ childArray = [];
+ for (j = 0; j < childNode.length; j += 1) {
+ childElement = childNode[j];
+ pathElement.index = j;
+ if (childElement) {
+ assignNode = walker.apply(childElement, null, pathElement);
+ if (isArray(assignNode.prepend)) {
+ pushAll(childArray, assignNode.prepend);
+ delete assignNode.prepend;
+ }
+ }
+ pushAll(childArray, assignNode);
+ }
+ node[childType] = childArray;
+ } else {
+ assignNode = walker.apply(childNode, null, pathElement);
+ /*istanbul ignore if: paranoid check */
+ if (isArray(assignNode.prepend)) {
+ throw new Error('Internal error: attempt to prepend statements in disallowed (non-array) context');
+ /* if this should be allowed, this is how to solve it
+ tmpNode = { type: 'BlockStatement', body: [] };
+ pushAll(tmpNode.body, assignNode.prepend);
+ pushAll(tmpNode.body, assignNode);
+ node[childType] = tmpNode;
+ delete assignNode.prepend;
+ */
+ } else {
+ node[childType] = assignNode;
+ }
+ }
+ }
+ }
+
+ postprocessor = ret.postprocessor;
+ if (postprocessor) {
+ delete ret.postprocessor;
+ ret = walker.apply(ret, postprocessor);
+ }
+
+ delete node.walking;
+
+ return ret;
+ }
+
+ Walker.prototype = {
+ startWalk: function (node) {
+ this.path = [];
+ this.apply(node);
+ },
+
+ apply: function (node, walkFn, pathElement) {
+ var ret, i, seq, prefix;
+
+ walkFn = walkFn || defaultWalker;
+ if (this.debug) {
+ this.seq += 1;
+ this.level += 1;
+ seq = this.seq;
+ prefix = '';
+ for (i = 0; i < this.level; i += 1) { prefix += ' '; }
+ console.log(prefix + 'Enter (' + seq + '):' + node.type);
+ }
+ if (pathElement) { this.path.push(pathElement); }
+ ret = walkFn.call(this.scope, node, this);
+ if (pathElement) { this.path.pop(); }
+ if (this.debug) {
+ this.level -= 1;
+ console.log(prefix + 'Return (' + seq + '):' + node.type);
+ }
+ return ret || node;
+ },
+
+ startLineForNode: function (node) {
+ return node && node.loc && node.loc.start ? node.loc.start.line : /* istanbul ignore next: guard */ null;
+ },
+
+ ancestor: function (n) {
+ return this.path.length > n - 1 ? this.path[this.path.length - n] : /* istanbul ignore next: guard */ null;
+ },
+
+ parent: function () {
+ return this.ancestor(1);
+ },
+
+ isLabeled: function () {
+ var el = this.parent();
+ return el && el.node.type === SYNTAX.LabeledStatement.name;
+ }
+ };
+
+ /**
+ * mechanism to instrument code for coverage. It uses the `esprima` and
+ * `escodegen` libraries for JS parsing and code generation respectively.
+ *
+ * Works on `node` as well as the browser.
+ *
+ * Usage on nodejs
+ * ---------------
+ *
+ * var instrumenter = new require('istanbul').Instrumenter(),
+ * changed = instrumenter.instrumentSync('function meaningOfLife() { return 42; }', 'filename.js');
+ *
+ * Usage in a browser
+ * ------------------
+ *
+ * Load `esprima.js`, `escodegen.js` and `instrumenter.js` (this file) using `script` tags or other means.
+ *
+ * Create an instrumenter object as:
+ *
+ * var instrumenter = new Instrumenter(),
+ * changed = instrumenter.instrumentSync('function meaningOfLife() { return 42; }', 'filename.js');
+ *
+ * Aside from demonstration purposes, it is unclear why you would want to instrument code in a browser.
+ *
+ * @class Instrumenter
+ * @constructor
+ * @param {Object} options Optional. Configuration options.
+ * @param {String} [options.coverageVariable] the global variable name to use for
+ * tracking coverage. Defaults to `__coverage__`
+ * @param {Boolean} [options.embedSource] whether to embed the source code of every
+ * file as an array in the file coverage object for that file. Defaults to `false`
+ * @param {Boolean} [options.preserveComments] whether comments should be preserved in the output. Defaults to `false`
+ * @param {Boolean} [options.noCompact] emit readable code when set. Defaults to `false`
+ * @param {Boolean} [options.noAutoWrap] do not automatically wrap the source in
+ * an anonymous function before covering it. By default, code is wrapped in
+ * an anonymous function before it is parsed. This is done because
+ * some nodejs libraries have `return` statements outside of
+ * a function which is technically invalid Javascript and causes the parser to fail.
+ * This construct, however, works correctly in node since module loading
+ * is done in the context of an anonymous function.
+ *
+ * Note that the semantics of the code *returned* by the instrumenter does not change in any way.
+ * The function wrapper is "unwrapped" before the instrumented code is generated.
+ * @param {Object} [options.codeGenerationOptions] an object that is directly passed to the `escodegen`
+ * library as configuration for code generation. The `noCompact` setting is not honored when this
+ * option is specified
+ * @param {Boolean} [options.debug] assist in debugging. Currently, the only effect of
+ * setting this option is a pretty-print of the coverage variable. Defaults to `false`
+ * @param {Boolean} [options.walkDebug] assist in debugging of the AST walker used by this class.
+ *
+ */
+ function Instrumenter(options) {
+ this.opts = options || {
+ debug: false,
+ walkDebug: false,
+ coverageVariable: '__coverage__',
+ codeGenerationOptions: undefined,
+ noAutoWrap: false,
+ noCompact: false,
+ embedSource: false,
+ preserveComments: false
+ };
+
+ this.walker = new Walker({
+ ExpressionStatement: this.coverStatement,
+ BreakStatement: this.coverStatement,
+ ContinueStatement: this.coverStatement,
+ DebuggerStatement: this.coverStatement,
+ ReturnStatement: this.coverStatement,
+ ThrowStatement: this.coverStatement,
+ TryStatement: this.coverStatement,
+ VariableDeclaration: this.coverStatement,
+ IfStatement: [ this.ifBlockConverter, this.coverStatement, this.ifBranchInjector ],
+ ForStatement: [ this.skipInit, this.loopBlockConverter, this.coverStatement ],
+ ForInStatement: [ this.skipLeft, this.loopBlockConverter, this.coverStatement ],
+ WhileStatement: [ this.loopBlockConverter, this.coverStatement ],
+ DoWhileStatement: [ this.loopBlockConverter, this.coverStatement ],
+ SwitchStatement: [ this.coverStatement, this.switchBranchInjector ],
+ SwitchCase: [ this.switchCaseInjector ],
+ WithStatement: [ this.withBlockConverter, this.coverStatement ],
+ FunctionDeclaration: [ this.coverFunction, this.coverStatement ],
+ FunctionExpression: this.coverFunction,
+ LabeledStatement: this.coverStatement,
+ ConditionalExpression: this.conditionalBranchInjector,
+ LogicalExpression: this.logicalExpressionBranchInjector,
+ ObjectExpression: this.maybeAddType
+ }, this.extractCurrentHint, this, this.opts.walkDebug);
+
+ //unit testing purposes only
+ if (this.opts.backdoor && this.opts.backdoor.omitTrackerSuffix) {
+ this.omitTrackerSuffix = true;
+ }
+ }
+
+ Instrumenter.prototype = {
+ /**
+ * synchronous instrumentation method. Throws when illegal code is passed to it
+ * @method instrumentSync
+ * @param {String} code the code to be instrumented as a String
+ * @param {String} filename Optional. The name of the file from which
+ * the code was read. A temporary filename is generated when not specified.
+ * Not specifying a filename is only useful for unit tests and demonstrations
+ * of this library.
+ */
+ instrumentSync: function (code, filename) {
+ var program;
+
+ //protect from users accidentally passing in a Buffer object instead
+ if (typeof code !== 'string') { throw new Error('Code must be string'); }
+ if (code.charAt(0) === '#') { //shebang, 'comment' it out, won't affect syntax tree locations for things we care about
+ code = '//' + code;
+ }
+ if (!this.opts.noAutoWrap) {
+ code = LEADER_WRAP + code + TRAILER_WRAP;
+ }
+ program = ESP.parse(code, {
+ loc: true,
+ range: true,
+ tokens: this.opts.preserveComments,
+ comment: true
+ });
+ if (this.opts.preserveComments) {
+ program = ESPGEN.attachComments(program, program.comments, program.tokens);
+ }
+ if (!this.opts.noAutoWrap) {
+ program = {
+ type: SYNTAX.Program.name,
+ body: program.body[0].expression.callee.body.body,
+ comments: program.comments
+ };
+ }
+ return this.instrumentASTSync(program, filename, code);
+ },
+ filterHints: function (comments) {
+ var ret = [],
+ i,
+ comment,
+ groups;
+ if (!(comments && isArray(comments))) {
+ return ret;
+ }
+ for (i = 0; i < comments.length; i += 1) {
+ comment = comments[i];
+ /* istanbul ignore else: paranoid check */
+ if (comment && comment.value && comment.range && isArray(comment.range)) {
+ groups = String(comment.value).match(COMMENT_RE);
+ if (groups) {
+ ret.push({ type: groups[1], start: comment.range[0], end: comment.range[1] });
+ }
+ }
+ }
+ return ret;
+ },
+ extractCurrentHint: function (node) {
+ if (!node.range) { return; }
+ var i = this.currentState.lastHintPosition + 1,
+ hints = this.currentState.hints,
+ nodeStart = node.range[0],
+ hint;
+ this.currentState.currentHint = null;
+ while (i < hints.length) {
+ hint = hints[i];
+ if (hint.end < nodeStart) {
+ this.currentState.currentHint = hint;
+ this.currentState.lastHintPosition = i;
+ i += 1;
+ } else {
+ break;
+ }
+ }
+ },
+ /**
+ * synchronous instrumentation method that instruments an AST instead.
+ * @method instrumentASTSync
+ * @param {String} program the AST to be instrumented
+ * @param {String} filename Optional. The name of the file from which
+ * the code was read. A temporary filename is generated when not specified.
+ * Not specifying a filename is only useful for unit tests and demonstrations
+ * of this library.
+ * @param {String} originalCode the original code corresponding to the AST,
+ * used for embedding the source into the coverage object
+ */
+ instrumentASTSync: function (program, filename, originalCode) {
+ var usingStrict = false,
+ codegenOptions,
+ generated,
+ preamble,
+ lineCount,
+ i;
+ filename = filename || String(new Date().getTime()) + '.js';
+ this.sourceMap = null;
+ this.coverState = {
+ path: filename,
+ s: {},
+ b: {},
+ f: {},
+ fnMap: {},
+ statementMap: {},
+ branchMap: {}
+ };
+ this.currentState = {
+ trackerVar: generateTrackerVar(filename, this.omitTrackerSuffix),
+ func: 0,
+ branch: 0,
+ variable: 0,
+ statement: 0,
+ hints: this.filterHints(program.comments),
+ currentHint: null,
+ lastHintPosition: -1,
+ ignoring: 0
+ };
+ if (program.body && program.body.length > 0 && this.isUseStrictExpression(program.body[0])) {
+ //nuke it
+ program.body.shift();
+ //and add it back at code generation time
+ usingStrict = true;
+ }
+ this.walker.startWalk(program);
+ codegenOptions = this.opts.codeGenerationOptions || { format: { compact: !this.opts.noCompact }};
+ codegenOptions.comment = this.opts.preserveComments;
+ //console.log(JSON.stringify(program, undefined, 2));
+
+ generated = ESPGEN.generate(program, codegenOptions);
+ preamble = this.getPreamble(originalCode || '', usingStrict);
+
+ if (generated.map && generated.code) {
+ lineCount = preamble.split(/\r\n|\r|\n/).length;
+ // offset all the generated line numbers by the number of lines in the preamble
+ for (i = 0; i < generated.map._mappings.length; i += 1) {
+ generated.map._mappings[i].generatedLine += lineCount;
+ }
+ this.sourceMap = generated.map;
+ generated = generated.code;
+ }
+
+ return preamble + '\n' + generated + '\n';
+ },
+ /**
+ * Callback based instrumentation. Note that this still executes synchronously in the same process tick
+ * and calls back immediately. It only provides the options for callback style error handling as
+ * opposed to a `try-catch` style and nothing more. Implemented as a wrapper over `instrumentSync`
+ *
+ * @method instrument
+ * @param {String} code the code to be instrumented as a String
+ * @param {String} filename Optional. The name of the file from which
+ * the code was read. A temporary filename is generated when not specified.
+ * Not specifying a filename is only useful for unit tests and demonstrations
+ * of this library.
+ * @param {Function(err, instrumentedCode)} callback - the callback function
+ */
+ instrument: function (code, filename, callback) {
+
+ if (!callback && typeof filename === 'function') {
+ callback = filename;
+ filename = null;
+ }
+ try {
+ callback(null, this.instrumentSync(code, filename));
+ } catch (ex) {
+ callback(ex);
+ }
+ },
+ /**
+ * returns the file coverage object for the code that was instrumented
+ * just before calling this method. Note that this represents a
+ * "zero-coverage" object which is not even representative of the code
+ * being loaded in node or a browser (which would increase the statement
+ * counts for mainline code).
+ * @return {Object} a "zero-coverage" file coverage object for the code last instrumented
+ * by this instrumenter
+ */
+ lastFileCoverage: function () {
+ return this.coverState;
+ },
+ /**
+ * returns the source map object for the code that was instrumented
+ * just before calling this method.
+ * @return {Object} a source map object for the code last instrumented
+ * by this instrumenter
+ */
+ lastSourceMap: function () {
+ return this.sourceMap;
+ },
+ fixColumnPositions: function (coverState) {
+ var offset = LEADER_WRAP.length,
+ fixer = function (loc) {
+ if (loc.start.line === 1) {
+ loc.start.column -= offset;
+ }
+ if (loc.end.line === 1) {
+ loc.end.column -= offset;
+ }
+ },
+ k,
+ obj,
+ i,
+ locations;
+
+ obj = coverState.statementMap;
+ for (k in obj) {
+ /* istanbul ignore else: has own property */
+ if (obj.hasOwnProperty(k)) { fixer(obj[k]); }
+ }
+ obj = coverState.fnMap;
+ for (k in obj) {
+ /* istanbul ignore else: has own property */
+ if (obj.hasOwnProperty(k)) { fixer(obj[k].loc); }
+ }
+ obj = coverState.branchMap;
+ for (k in obj) {
+ /* istanbul ignore else: has own property */
+ if (obj.hasOwnProperty(k)) {
+ locations = obj[k].locations;
+ for (i = 0; i < locations.length; i += 1) {
+ fixer(locations[i]);
+ }
+ }
+ }
+ },
+
+ getPreamble: function (sourceCode, emitUseStrict) {
+ var varName = this.opts.coverageVariable || '__coverage__',
+ file = this.coverState.path.replace(/\\/g, '\\\\'),
+ tracker = this.currentState.trackerVar,
+ coverState,
+ strictLine = emitUseStrict ? '"use strict";' : '',
+ // return replacements using the function to ensure that the replacement is
+ // treated like a dumb string and not as a string with RE replacement patterns
+ replacer = function (s) {
+ return function () { return s; };
+ },
+ code;
+ if (!this.opts.noAutoWrap) {
+ this.fixColumnPositions(this.coverState);
+ }
+ if (this.opts.embedSource) {
+ this.coverState.code = sourceCode.split(/(?:\r?\n)|\r/);
+ }
+ coverState = this.opts.debug ? JSON.stringify(this.coverState, undefined, 4) : JSON.stringify(this.coverState);
+ code = [
+ "%STRICT%",
+ "var %VAR% = (Function('return this'))();",
+ "if (!%VAR%.%GLOBAL%) { %VAR%.%GLOBAL% = {}; }",
+ "%VAR% = %VAR%.%GLOBAL%;",
+ "if (!(%VAR%['%FILE%'])) {",
+ " %VAR%['%FILE%'] = %OBJECT%;",
+ "}",
+ "%VAR% = %VAR%['%FILE%'];"
+ ].join("\n")
+ .replace(/%STRICT%/g, replacer(strictLine))
+ .replace(/%VAR%/g, replacer(tracker))
+ .replace(/%GLOBAL%/g, replacer(varName))
+ .replace(/%FILE%/g, replacer(file))
+ .replace(/%OBJECT%/g, replacer(coverState));
+ return code;
+ },
+
+ startIgnore: function () {
+ this.currentState.ignoring += 1;
+ },
+
+ endIgnore: function () {
+ this.currentState.ignoring -= 1;
+ },
+
+ convertToBlock: function (node) {
+ if (!node) {
+ return { type: 'BlockStatement', body: [] };
+ } else if (node.type === 'BlockStatement') {
+ return node;
+ } else {
+ return { type: 'BlockStatement', body: [ node ] };
+ }
+ },
+
+ ifBlockConverter: function (node) {
+ node.consequent = this.convertToBlock(node.consequent);
+ node.alternate = this.convertToBlock(node.alternate);
+ },
+
+ loopBlockConverter: function (node) {
+ node.body = this.convertToBlock(node.body);
+ },
+
+ withBlockConverter: function (node) {
+ node.body = this.convertToBlock(node.body);
+ },
+
+ statementName: function (location, initValue) {
+ var sName,
+ ignoring = !!this.currentState.ignoring;
+
+ location.skip = ignoring || undefined;
+ initValue = initValue || 0;
+ this.currentState.statement += 1;
+ sName = this.currentState.statement;
+ this.coverState.statementMap[sName] = location;
+ this.coverState.s[sName] = initValue;
+ return sName;
+ },
+
+ skipInit: function (node /*, walker */) {
+ if (node.init) {
+ node.init.skipWalk = true;
+ }
+ },
+
+ skipLeft: function (node /*, walker */) {
+ node.left.skipWalk = true;
+ },
+
+ isUseStrictExpression: function (node) {
+ return node && node.type === SYNTAX.ExpressionStatement.name &&
+ node.expression && node.expression.type === SYNTAX.Literal.name &&
+ node.expression.value === 'use strict';
+ },
+
+ maybeSkipNode: function (node, type) {
+ var alreadyIgnoring = !!this.currentState.ignoring,
+ hint = this.currentState.currentHint,
+ ignoreThis = !alreadyIgnoring && hint && hint.type === type;
+
+ if (ignoreThis) {
+ this.startIgnore();
+ node.postprocessor = this.endIgnore;
+ return true;
+ }
+ return false;
+ },
+
+ coverStatement: function (node, walker) {
+ var sName,
+ incrStatementCount,
+ grandParent;
+
+ this.maybeSkipNode(node, 'next');
+
+ if (this.isUseStrictExpression(node)) {
+ grandParent = walker.ancestor(2);
+ /* istanbul ignore else: difficult to test */
+ if (grandParent) {
+ if ((grandParent.node.type === SYNTAX.FunctionExpression.name ||
+ grandParent.node.type === SYNTAX.FunctionDeclaration.name) &&
+ walker.parent().node.body[0] === node) {
+ return;
+ }
+ }
+ }
+ if (node.type === SYNTAX.FunctionDeclaration.name) {
+ sName = this.statementName(node.loc, 1);
+ } else {
+ sName = this.statementName(node.loc);
+ incrStatementCount = astgen.statement(
+ astgen.postIncrement(
+ astgen.subscript(
+ astgen.dot(astgen.variable(this.currentState.trackerVar), astgen.variable('s')),
+ astgen.stringLiteral(sName)
+ )
+ )
+ );
+ this.splice(incrStatementCount, node, walker);
+ }
+ },
+
+ splice: function (statements, node, walker) {
+ var targetNode = walker.isLabeled() ? walker.parent().node : node;
+ targetNode.prepend = targetNode.prepend || [];
+ pushAll(targetNode.prepend, statements);
+ },
+
+ functionName: function (node, line, location) {
+ this.currentState.func += 1;
+ var id = this.currentState.func,
+ ignoring = !!this.currentState.ignoring,
+ name = node.id ? node.id.name : '(anonymous_' + id + ')';
+ this.coverState.fnMap[id] = { name: name, line: line, loc: location, skip: ignoring || undefined };
+ this.coverState.f[id] = 0;
+ return id;
+ },
+
+ coverFunction: function (node, walker) {
+ var id,
+ body = node.body,
+ blockBody = body.body,
+ popped;
+
+ this.maybeSkipNode(node, 'next');
+
+ id = this.functionName(node, walker.startLineForNode(node), {
+ start: node.loc.start,
+ end: { line: node.body.loc.start.line, column: node.body.loc.start.column }
+ });
+
+ if (blockBody.length > 0 && this.isUseStrictExpression(blockBody[0])) {
+ popped = blockBody.shift();
+ }
+ blockBody.unshift(
+ astgen.statement(
+ astgen.postIncrement(
+ astgen.subscript(
+ astgen.dot(astgen.variable(this.currentState.trackerVar), astgen.variable('f')),
+ astgen.stringLiteral(id)
+ )
+ )
+ )
+ );
+ if (popped) {
+ blockBody.unshift(popped);
+ }
+ },
+
+ branchName: function (type, startLine, pathLocations) {
+ var bName,
+ paths = [],
+ locations = [],
+ i,
+ ignoring = !!this.currentState.ignoring;
+ this.currentState.branch += 1;
+ bName = this.currentState.branch;
+ for (i = 0; i < pathLocations.length; i += 1) {
+ pathLocations[i].skip = pathLocations[i].skip || ignoring || undefined;
+ locations.push(pathLocations[i]);
+ paths.push(0);
+ }
+ this.coverState.b[bName] = paths;
+ this.coverState.branchMap[bName] = { line: startLine, type: type, locations: locations };
+ return bName;
+ },
+
+ branchIncrementExprAst: function (varName, branchIndex, down) {
+ var ret = astgen.postIncrement(
+ astgen.subscript(
+ astgen.subscript(
+ astgen.dot(astgen.variable(this.currentState.trackerVar), astgen.variable('b')),
+ astgen.stringLiteral(varName)
+ ),
+ astgen.numericLiteral(branchIndex)
+ ),
+ down
+ );
+ return ret;
+ },
+
+ locationsForNodes: function (nodes) {
+ var ret = [],
+ i;
+ for (i = 0; i < nodes.length; i += 1) {
+ ret.push(nodes[i].loc);
+ }
+ return ret;
+ },
+
+ ifBranchInjector: function (node, walker) {
+ var alreadyIgnoring = !!this.currentState.ignoring,
+ hint = this.currentState.currentHint,
+ ignoreThen = !alreadyIgnoring && hint && hint.type === 'if',
+ ignoreElse = !alreadyIgnoring && hint && hint.type === 'else',
+ line = node.loc.start.line,
+ col = node.loc.start.column,
+ start = { line: line, column: col },
+ end = { line: line, column: col },
+ bName = this.branchName('if', walker.startLineForNode(node), [
+ { start: start, end: end, skip: ignoreThen || undefined },
+ { start: start, end: end, skip: ignoreElse || undefined }
+ ]),
+ thenBody = node.consequent.body,
+ elseBody = node.alternate.body,
+ child;
+ thenBody.unshift(astgen.statement(this.branchIncrementExprAst(bName, 0)));
+ elseBody.unshift(astgen.statement(this.branchIncrementExprAst(bName, 1)));
+ if (ignoreThen) { child = node.consequent; child.preprocessor = this.startIgnore; child.postprocessor = this.endIgnore; }
+ if (ignoreElse) { child = node.alternate; child.preprocessor = this.startIgnore; child.postprocessor = this.endIgnore; }
+ },
+
+ branchLocationFor: function (name, index) {
+ return this.coverState.branchMap[name].locations[index];
+ },
+
+ switchBranchInjector: function (node, walker) {
+ var cases = node.cases,
+ bName,
+ i;
+
+ if (!(cases && cases.length > 0)) {
+ return;
+ }
+ bName = this.branchName('switch', walker.startLineForNode(node), this.locationsForNodes(cases));
+ for (i = 0; i < cases.length; i += 1) {
+ cases[i].branchLocation = this.branchLocationFor(bName, i);
+ cases[i].consequent.unshift(astgen.statement(this.branchIncrementExprAst(bName, i)));
+ }
+ },
+
+ switchCaseInjector: function (node) {
+ var location = node.branchLocation;
+ delete node.branchLocation;
+ if (this.maybeSkipNode(node, 'next')) {
+ location.skip = true;
+ }
+ },
+
+ conditionalBranchInjector: function (node, walker) {
+ var bName = this.branchName('cond-expr', walker.startLineForNode(node), this.locationsForNodes([ node.consequent, node.alternate ])),
+ ast1 = this.branchIncrementExprAst(bName, 0),
+ ast2 = this.branchIncrementExprAst(bName, 1);
+
+ node.consequent.preprocessor = this.maybeAddSkip(this.branchLocationFor(bName, 0));
+ node.alternate.preprocessor = this.maybeAddSkip(this.branchLocationFor(bName, 1));
+ node.consequent = astgen.sequence(ast1, node.consequent);
+ node.alternate = astgen.sequence(ast2, node.alternate);
+ },
+
+ maybeAddSkip: function (branchLocation) {
+ return function (node) {
+ var alreadyIgnoring = !!this.currentState.ignoring,
+ hint = this.currentState.currentHint,
+ ignoreThis = !alreadyIgnoring && hint && hint.type === 'next';
+ if (ignoreThis) {
+ this.startIgnore();
+ node.postprocessor = this.endIgnore;
+ }
+ if (ignoreThis || alreadyIgnoring) {
+ branchLocation.skip = true;
+ }
+ };
+ },
+
+ logicalExpressionBranchInjector: function (node, walker) {
+ var parent = walker.parent(),
+ leaves = [],
+ bName,
+ tuple,
+ i;
+
+ this.maybeSkipNode(node, 'next');
+
+ if (parent && parent.node.type === SYNTAX.LogicalExpression.name) {
+ //already covered
+ return;
+ }
+
+ this.findLeaves(node, leaves);
+ bName = this.branchName('binary-expr',
+ walker.startLineForNode(node),
+ this.locationsForNodes(leaves.map(function (item) { return item.node; }))
+ );
+ for (i = 0; i < leaves.length; i += 1) {
+ tuple = leaves[i];
+ tuple.parent[tuple.property] = astgen.sequence(this.branchIncrementExprAst(bName, i), tuple.node);
+ tuple.node.preprocessor = this.maybeAddSkip(this.branchLocationFor(bName, i));
+ }
+ },
+
+ findLeaves: function (node, accumulator, parent, property) {
+ if (node.type === SYNTAX.LogicalExpression.name) {
+ this.findLeaves(node.left, accumulator, node, 'left');
+ this.findLeaves(node.right, accumulator, node, 'right');
+ } else {
+ accumulator.push({ node: node, parent: parent, property: property });
+ }
+ },
+ maybeAddType: function (node /*, walker */) {
+ var props = node.properties,
+ i,
+ child;
+ for (i = 0; i < props.length; i += 1) {
+ child = props[i];
+ if (!child.type) {
+ child.type = SYNTAX.Property.name;
+ }
+ }
+ }
+ };
+
+ if (isNode) {
+ module.exports = Instrumenter;
+ } else {
+ window.Instrumenter = Instrumenter;
+ }
+
+}(typeof module !== 'undefined' && typeof module.exports !== 'undefined' && typeof exports !== 'undefined'));
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/object-utils.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/object-utils.js
new file mode 100644
index 0000000..703b78b
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/object-utils.js
@@ -0,0 +1,367 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+/**
+ * utility methods to process coverage objects. A coverage object has the following
+ * format.
+ *
+ * {
+ * "/path/to/file1.js": { file1 coverage },
+ * "/path/to/file2.js": { file2 coverage }
+ * }
+ *
+ * The internals of the file coverage object are intentionally not documented since
+ * it is not a public interface.
+ *
+ * *Note:* When a method of this module has the word `File` in it, it will accept
+ * one of the sub-objects of the main coverage object as an argument. Other
+ * methods accept the higher level coverage object with multiple keys.
+ *
+ * Works on `node` as well as the browser.
+ *
+ * Usage on nodejs
+ * ---------------
+ *
+ * var objectUtils = require('istanbul').utils;
+ *
+ * Usage in a browser
+ * ------------------
+ *
+ * Load this file using a `script` tag or other means. This will set `window.coverageUtils`
+ * to this module's exports.
+ *
+ * @class ObjectUtils
+ * @static
+ */
+(function (isNode) {
+ /**
+ * adds line coverage information to a file coverage object, reverse-engineering
+ * it from statement coverage. The object passed in is updated in place.
+ *
+ * Note that if line coverage information is already present in the object,
+ * it is not recomputed.
+ *
+ * @method addDerivedInfoForFile
+ * @static
+ * @param {Object} fileCoverage the coverage object for a single file
+ */
+ function addDerivedInfoForFile(fileCoverage) {
+ var statementMap = fileCoverage.statementMap,
+ statements = fileCoverage.s,
+ lineMap;
+
+ if (!fileCoverage.l) {
+ fileCoverage.l = lineMap = {};
+ Object.keys(statements).forEach(function (st) {
+ var line = statementMap[st].start.line,
+ count = statements[st],
+ prevVal = lineMap[line];
+ if (count === 0 && statementMap[st].skip) { count = 1; }
+ if (typeof prevVal === 'undefined' || prevVal < count) {
+ lineMap[line] = count;
+ }
+ });
+ }
+ }
+ /**
+ * adds line coverage information to all file coverage objects.
+ *
+ * @method addDerivedInfo
+ * @static
+ * @param {Object} coverage the coverage object
+ */
+ function addDerivedInfo(coverage) {
+ Object.keys(coverage).forEach(function (k) {
+ addDerivedInfoForFile(coverage[k]);
+ });
+ }
+ /**
+ * removes line coverage information from all file coverage objects
+ * @method removeDerivedInfo
+ * @static
+ * @param {Object} coverage the coverage object
+ */
+ function removeDerivedInfo(coverage) {
+ Object.keys(coverage).forEach(function (k) {
+ delete coverage[k].l;
+ });
+ }
+
+ function percent(covered, total) {
+ var tmp;
+ if (total > 0) {
+ tmp = 1000 * 100 * covered / total + 5;
+ return Math.floor(tmp / 10) / 100;
+ } else {
+ return 100.00;
+ }
+ }
+
+ function computeSimpleTotals(fileCoverage, property, mapProperty) {
+ var stats = fileCoverage[property],
+ map = mapProperty ? fileCoverage[mapProperty] : null,
+ ret = { total: 0, covered: 0, skipped: 0 };
+
+ Object.keys(stats).forEach(function (key) {
+ var covered = !!stats[key],
+ skipped = map && map[key].skip;
+ ret.total += 1;
+ if (covered || skipped) {
+ ret.covered += 1;
+ }
+ if (!covered && skipped) {
+ ret.skipped += 1;
+ }
+ });
+ ret.pct = percent(ret.covered, ret.total);
+ return ret;
+ }
+
+ function computeBranchTotals(fileCoverage) {
+ var stats = fileCoverage.b,
+ branchMap = fileCoverage.branchMap,
+ ret = { total: 0, covered: 0, skipped: 0 };
+
+ Object.keys(stats).forEach(function (key) {
+ var branches = stats[key],
+ map = branchMap[key],
+ covered,
+ skipped,
+ i;
+ for (i = 0; i < branches.length; i += 1) {
+ covered = branches[i] > 0;
+ skipped = map.locations && map.locations[i] && map.locations[i].skip;
+ if (covered || skipped) {
+ ret.covered += 1;
+ }
+ if (!covered && skipped) {
+ ret.skipped += 1;
+ }
+ }
+ ret.total += branches.length;
+ });
+ ret.pct = percent(ret.covered, ret.total);
+ return ret;
+ }
+ /**
+ * returns a blank summary metrics object. A metrics object has the following
+ * format.
+ *
+ * {
+ * lines: lineMetrics,
+ * statements: statementMetrics,
+ * functions: functionMetrics,
+ * branches: branchMetrics
+ * }
+ *
+ * Each individual metric object looks as follows:
+ *
+ * {
+ * total: n,
+ * covered: m,
+ * pct: percent
+ * }
+ *
+ * @method blankSummary
+ * @static
+ * @return {Object} a blank metrics object
+ */
+ function blankSummary() {
+ return {
+ lines: {
+ total: 0,
+ covered: 0,
+ skipped: 0,
+ pct: 'Unknown'
+ },
+ statements: {
+ total: 0,
+ covered: 0,
+ skipped: 0,
+ pct: 'Unknown'
+ },
+ functions: {
+ total: 0,
+ covered: 0,
+ skipped: 0,
+ pct: 'Unknown'
+ },
+ branches: {
+ total: 0,
+ covered: 0,
+ skipped: 0,
+ pct: 'Unknown'
+ }
+ };
+ }
+ /**
+ * returns the summary metrics given the coverage object for a single file. See `blankSummary()`
+ * to understand the format of the returned object.
+ *
+ * @method summarizeFileCoverage
+ * @static
+ * @param {Object} fileCoverage the coverage object for a single file.
+ * @return {Object} the summary metrics for the file
+ */
+ function summarizeFileCoverage(fileCoverage) {
+ var ret = blankSummary();
+ addDerivedInfoForFile(fileCoverage);
+ ret.lines = computeSimpleTotals(fileCoverage, 'l');
+ ret.functions = computeSimpleTotals(fileCoverage, 'f', 'fnMap');
+ ret.statements = computeSimpleTotals(fileCoverage, 's', 'statementMap');
+ ret.branches = computeBranchTotals(fileCoverage);
+ return ret;
+ }
+ /**
+ * merges two instances of file coverage objects *for the same file*
+ * such that the execution counts are correct.
+ *
+ * @method mergeFileCoverage
+ * @static
+ * @param {Object} first the first file coverage object for a given file
+ * @param {Object} second the second file coverage object for the same file
+ * @return {Object} an object that is a result of merging the two. Note that
+ * the input objects are not changed in any way.
+ */
+ function mergeFileCoverage(first, second) {
+ var ret = JSON.parse(JSON.stringify(first)),
+ i;
+
+ delete ret.l; //remove derived info
+
+ Object.keys(second.s).forEach(function (k) {
+ ret.s[k] += second.s[k];
+ });
+ Object.keys(second.f).forEach(function (k) {
+ ret.f[k] += second.f[k];
+ });
+ Object.keys(second.b).forEach(function (k) {
+ var retArray = ret.b[k],
+ secondArray = second.b[k];
+ for (i = 0; i < retArray.length; i += 1) {
+ retArray[i] += secondArray[i];
+ }
+ });
+
+ return ret;
+ }
+ /**
+ * merges multiple summary metrics objects by summing up the `totals` and
+ * `covered` fields and recomputing the percentages. This function is generic
+ * and can accept any number of arguments.
+ *
+ * @method mergeSummaryObjects
+ * @static
+ * @param {Object} summary... multiple summary metrics objects
+ * @return {Object} the merged summary metrics
+ */
+ function mergeSummaryObjects() {
+ var ret = blankSummary(),
+ args = Array.prototype.slice.call(arguments),
+ keys = ['lines', 'statements', 'branches', 'functions'],
+ increment = function (obj) {
+ if (obj) {
+ keys.forEach(function (key) {
+ ret[key].total += obj[key].total;
+ ret[key].covered += obj[key].covered;
+ ret[key].skipped += obj[key].skipped;
+ });
+ }
+ };
+ args.forEach(function (arg) {
+ increment(arg);
+ });
+ keys.forEach(function (key) {
+ ret[key].pct = percent(ret[key].covered, ret[key].total);
+ });
+
+ return ret;
+ }
+ /**
+ * returns the coverage summary for a single coverage object. This is
+ * wrapper over `summarizeFileCoverage` and `mergeSummaryObjects` for
+ * the common case of a single coverage object
+ * @method summarizeCoverage
+ * @static
+ * @param {Object} coverage the coverage object
+ * @return {Object} summary coverage metrics across all files in the coverage object
+ */
+ function summarizeCoverage(coverage) {
+ var fileSummary = [];
+ Object.keys(coverage).forEach(function (key) {
+ fileSummary.push(summarizeFileCoverage(coverage[key]));
+ });
+ return mergeSummaryObjects.apply(null, fileSummary);
+ }
+
+ /**
+ * makes the coverage object generated by this library yuitest_coverage compatible.
+ * Note that this transformation is lossy since the returned object will not have
+ * statement and branch coverage.
+ *
+ * @method toYUICoverage
+ * @static
+ * @param {Object} coverage The `istanbul` coverage object
+ * @return {Object} a coverage object in `yuitest_coverage` format.
+ */
+ function toYUICoverage(coverage) {
+ var ret = {};
+
+ addDerivedInfo(coverage);
+
+ Object.keys(coverage).forEach(function (k) {
+ var fileCoverage = coverage[k],
+ lines = fileCoverage.l,
+ functions = fileCoverage.f,
+ fnMap = fileCoverage.fnMap,
+ o;
+
+ o = ret[k] = {
+ lines: {},
+ calledLines: 0,
+ coveredLines: 0,
+ functions: {},
+ calledFunctions: 0,
+ coveredFunctions: 0
+ };
+ Object.keys(lines).forEach(function (k) {
+ o.lines[k] = lines[k];
+ o.coveredLines += 1;
+ if (lines[k] > 0) {
+ o.calledLines += 1;
+ }
+ });
+ Object.keys(functions).forEach(function (k) {
+ var name = fnMap[k].name + ':' + fnMap[k].line;
+ o.functions[name] = functions[k];
+ o.coveredFunctions += 1;
+ if (functions[k] > 0) {
+ o.calledFunctions += 1;
+ }
+ });
+ });
+ return ret;
+ }
+
+ var exportables = {
+ addDerivedInfo: addDerivedInfo,
+ addDerivedInfoForFile: addDerivedInfoForFile,
+ removeDerivedInfo: removeDerivedInfo,
+ blankSummary: blankSummary,
+ summarizeFileCoverage: summarizeFileCoverage,
+ summarizeCoverage: summarizeCoverage,
+ mergeFileCoverage: mergeFileCoverage,
+ mergeSummaryObjects: mergeSummaryObjects,
+ toYUICoverage: toYUICoverage
+ };
+
+ /* istanbul ignore else: windows */
+ if (isNode) {
+ module.exports = exportables;
+ } else {
+ window.coverageUtils = exportables;
+ }
+}(typeof module !== 'undefined' && typeof module.exports !== 'undefined' && typeof exports !== 'undefined'));
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/register-plugins.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/register-plugins.js
new file mode 100644
index 0000000..50462be
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/register-plugins.js
@@ -0,0 +1,15 @@
+
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+var Store = require('./store'),
+ Report = require('./report'),
+ Command = require('./command');
+
+Store.loadAll();
+Report.loadAll();
+Command.loadAll();
+
+
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/clover.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/clover.js
new file mode 100644
index 0000000..bc1dbfc
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/clover.js
@@ -0,0 +1,198 @@
+var path = require('path'),
+ Report = require('./index'),
+ FileWriter = require('../util/file-writer'),
+ TreeSummarizer = require('../util/tree-summarizer'),
+ utils = require('../object-utils');
+
+function CloverReport(opts) {
+ Report.call(this);
+ opts = opts || {};
+ this.projectRoot = process.cwd();
+ this.dir = opts.dir || this.projectRoot;
+ this.file = opts.file || 'clover.xml';
+ this.opts = opts;
+}
+
+CloverReport.TYPE = 'clover';
+
+function asJavaPackage(node) {
+ return node.displayShortName().
+ replace(/\//g, '.').
+ replace(/\\/g, '.').
+ replace(/\.$/, '');
+}
+
+function asClassName(node) {
+ /*jslint regexp: true */
+ return node.fullPath().replace(/.*[\\\/]/, '');
+}
+
+function quote(thing) {
+ return '"' + thing + '"';
+}
+
+function attr(n, v) {
+ return ' ' + n + '=' + quote(v) + ' ';
+}
+
+function branchCoverageByLine(fileCoverage) {
+ var branchMap = fileCoverage.branchMap,
+ branches = fileCoverage.b,
+ ret = {};
+ Object.keys(branchMap).forEach(function (k) {
+ var line = branchMap[k].line,
+ branchData = branches[k];
+ ret[line] = ret[line] || [];
+ ret[line].push.apply(ret[line], branchData);
+ });
+ Object.keys(ret).forEach(function (k) {
+ var dataArray = ret[k],
+ covered = dataArray.filter(function (item) { return item > 0; }),
+ coverage = covered.length / dataArray.length * 100;
+ ret[k] = { covered: covered.length, total: dataArray.length, coverage: coverage };
+ });
+ return ret;
+}
+
+function addClassStats(node, fileCoverage, writer) {
+ var metrics = node.metrics,
+ branchByLine = branchCoverageByLine(fileCoverage),
+ fnMap,
+ lines;
+
+ writer.println('\t\t\t');
+
+ writer.println('\t\t\t\t');
+
+ fnMap = fileCoverage.fnMap;
+ lines = fileCoverage.l;
+ Object.keys(lines).forEach(function (k) {
+ var str = '\t\t\t\t');
+ });
+
+ writer.println('\t\t\t');
+}
+
+function walk(node, collector, writer, level, projectRoot) {
+ var metrics,
+ totalFiles = 0,
+ totalPackages = 0,
+ totalLines = 0,
+ tempLines = 0;
+ if (level === 0) {
+ metrics = node.metrics;
+ writer.println('');
+ writer.println('');
+
+ writer.println('\t');
+
+ node.children.filter(function (child) { return child.kind === 'dir'; }).
+ forEach(function (child) {
+ totalPackages += 1;
+ child.children.filter(function (child) { return child.kind !== 'dir'; }).
+ forEach(function (child) {
+ Object.keys(collector.fileCoverageFor(child.fullPath()).l).forEach(function (k){
+ tempLines = k;
+ });
+ totalLines += Number(tempLines);
+ totalFiles += 1;
+ });
+ });
+
+ writer.println('\t\t');
+ }
+ if (node.packageMetrics) {
+ metrics = node.packageMetrics;
+ writer.println('\t\t');
+
+ writer.println('\t\t\t');
+
+ node.children.filter(function (child) { return child.kind !== 'dir'; }).
+ forEach(function (child) {
+ addClassStats(child, collector.fileCoverageFor(child.fullPath()), writer);
+ });
+ writer.println('\t\t');
+ }
+ node.children.filter(function (child) { return child.kind === 'dir'; }).
+ forEach(function (child) {
+ walk(child, collector, writer, level + 1, projectRoot);
+ });
+
+ if (level === 0) {
+ writer.println('\t');
+ writer.println('');
+ }
+}
+
+Report.mix(CloverReport, {
+ writeReport: function (collector, sync) {
+ var summarizer = new TreeSummarizer(),
+ outputFile = path.join(this.dir, this.file),
+ writer = this.opts.writer || new FileWriter(sync),
+ projectRoot = this.projectRoot,
+ tree,
+ root;
+
+ collector.files().forEach(function (key) {
+ summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key)));
+ });
+ tree = summarizer.getTreeSummary();
+ root = tree.root;
+ writer.writeFile(outputFile, function (contentWriter) {
+ walk(root, collector, contentWriter, 0, projectRoot);
+ });
+ }
+});
+
+module.exports = CloverReport;
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/cobertura.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/cobertura.js
new file mode 100644
index 0000000..c7b09ed
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/cobertura.js
@@ -0,0 +1,207 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var path = require('path'),
+ Report = require('./index'),
+ FileWriter = require('../util/file-writer'),
+ TreeSummarizer = require('../util/tree-summarizer'),
+ utils = require('../object-utils');
+
+/**
+ * a `Report` implementation that produces a cobertura-style XML file that conforms to the
+ * http://cobertura.sourceforge.net/xml/coverage-04.dtd DTD.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('cobertura');
+ *
+ * @class CoberturaReport
+ * @extends Report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to the cobertura-coverage.xml will be written
+ */
+function CoberturaReport(opts) {
+ Report.call(this);
+ opts = opts || {};
+ this.projectRoot = process.cwd();
+ this.dir = opts.dir || this.projectRoot;
+ this.file = opts.file || 'cobertura-coverage.xml';
+ this.opts = opts;
+}
+
+CoberturaReport.TYPE = 'cobertura';
+
+function asJavaPackage(node) {
+ return node.displayShortName().
+ replace(/\//g, '.').
+ replace(/\\/g, '.').
+ replace(/\.$/, '');
+}
+
+function asClassName(node) {
+ /*jslint regexp: true */
+ return node.fullPath().replace(/.*[\\\/]/, '');
+}
+
+function quote(thing) {
+ return '"' + thing + '"';
+}
+
+function attr(n, v) {
+ return ' ' + n + '=' + quote(v) + ' ';
+}
+
+function branchCoverageByLine(fileCoverage) {
+ var branchMap = fileCoverage.branchMap,
+ branches = fileCoverage.b,
+ ret = {};
+ Object.keys(branchMap).forEach(function (k) {
+ var line = branchMap[k].line,
+ branchData = branches[k];
+ ret[line] = ret[line] || [];
+ ret[line].push.apply(ret[line], branchData);
+ });
+ Object.keys(ret).forEach(function (k) {
+ var dataArray = ret[k],
+ covered = dataArray.filter(function (item) { return item > 0; }),
+ coverage = covered.length / dataArray.length * 100;
+ ret[k] = { covered: covered.length, total: dataArray.length, coverage: coverage };
+ });
+ return ret;
+}
+
+function addClassStats(node, fileCoverage, writer, projectRoot) {
+ var metrics = node.metrics,
+ branchByLine = branchCoverageByLine(fileCoverage),
+ fnMap,
+ lines;
+
+ writer.println('\t\t');
+
+ writer.println('\t\t');
+ fnMap = fileCoverage.fnMap;
+ Object.keys(fnMap).forEach(function (k) {
+ var name = fnMap[k].name,
+ hits = fileCoverage.f[k];
+
+ writer.println(
+ '\t\t\t'
+ );
+
+ //Add the function definition line and hits so that jenkins cobertura plugin records method hits
+ writer.println(
+ '\t\t\t\t' +
+ '' +
+ ''
+ );
+
+ writer.println('\t\t\t');
+
+ });
+ writer.println('\t\t');
+
+ writer.println('\t\t');
+ lines = fileCoverage.l;
+ Object.keys(lines).forEach(function (k) {
+ var str = '\t\t\t');
+ });
+ writer.println('\t\t');
+
+ writer.println('\t\t');
+}
+
+function walk(node, collector, writer, level, projectRoot) {
+ var metrics;
+ if (level === 0) {
+ metrics = node.metrics;
+ writer.println('');
+ writer.println('');
+ writer.println('');
+ writer.println('');
+ writer.println('\t');
+ writer.println('');
+ writer.println('');
+ }
+ if (node.packageMetrics) {
+ metrics = node.packageMetrics;
+ writer.println('\t');
+ writer.println('\t');
+ node.children.filter(function (child) { return child.kind !== 'dir'; }).
+ forEach(function (child) {
+ addClassStats(child, collector.fileCoverageFor(child.fullPath()), writer, projectRoot);
+ });
+ writer.println('\t');
+ writer.println('\t');
+ }
+ node.children.filter(function (child) { return child.kind === 'dir'; }).
+ forEach(function (child) {
+ walk(child, collector, writer, level + 1, projectRoot);
+ });
+
+ if (level === 0) {
+ writer.println('');
+ writer.println('');
+ }
+}
+
+Report.mix(CoberturaReport, {
+ writeReport: function (collector, sync) {
+ var summarizer = new TreeSummarizer(),
+ outputFile = path.join(this.dir, this.file),
+ writer = this.opts.writer || new FileWriter(sync),
+ projectRoot = this.projectRoot,
+ tree,
+ root;
+
+ collector.files().forEach(function (key) {
+ summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key)));
+ });
+ tree = summarizer.getTreeSummary();
+ root = tree.root;
+ writer.writeFile(outputFile, function (contentWriter) {
+ walk(root, collector, contentWriter, 0, projectRoot);
+ });
+ }
+});
+
+module.exports = CoberturaReport;
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/common/defaults.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/common/defaults.js
new file mode 100644
index 0000000..48ec5bd
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/common/defaults.js
@@ -0,0 +1,33 @@
+/*
+ Copyright (c) 2013, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+module.exports = {
+ watermarks: function () {
+ return {
+ statements: [ 50, 80 ],
+ lines: [ 50, 80 ],
+ functions: [ 50, 80],
+ branches: [ 50, 80 ]
+ };
+ },
+
+ classFor: function (type, metrics, watermarks) {
+ var mark = watermarks[type],
+ value = metrics[type].pct;
+ return value >= mark[1] ? 'high' : value >= mark[0] ? 'medium' : 'low';
+ },
+
+ colorize: function (str, clazz) {
+ /* istanbul ignore if: untestable in batch mode */
+ if (process.stdout.isTTY) {
+ switch (clazz) {
+ case 'low' : str = '\033[91m' + str + '\033[0m'; break;
+ case 'medium': str = '\033[93m' + str + '\033[0m'; break;
+ case 'high': str = '\033[92m' + str + '\033[0m'; break;
+ }
+ }
+ return str;
+ }
+};
+
diff --git a/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/html.js b/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/html.js
new file mode 100644
index 0000000..e0db77c
--- /dev/null
+++ b/node_modules/grunt-istanbul/node_modules/istanbul/lib/report/html.js
@@ -0,0 +1,538 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+/*jshint maxlen: 300 */
+var handlebars = require('handlebars'),
+ defaults = require('./common/defaults'),
+ path = require('path'),
+ SEP = path.sep || '/',
+ fs = require('fs'),
+ util = require('util'),
+ FileWriter = require('../util/file-writer'),
+ Report = require('./index'),
+ Store = require('../store'),
+ InsertionText = require('../util/insertion-text'),
+ TreeSummarizer = require('../util/tree-summarizer'),
+ utils = require('../object-utils'),
+ templateFor = function (name) { return handlebars.compile(fs.readFileSync(path.resolve(__dirname, 'templates', name + '.txt'), 'utf8')); },
+ headerTemplate = templateFor('head'),
+ footerTemplate = templateFor('foot'),
+ pathTemplate = handlebars.compile('