summaryrefslogtreecommitdiff
path: root/bower_components/bootstrap/test-infra
diff options
context:
space:
mode:
authorThomas Park <thomas@thomaspark.me>2014-02-02 22:34:20 -0500
committerThomas Park <thomas@thomaspark.me>2014-02-02 22:34:20 -0500
commit303ba425d14820c93528555c60b355fcc9e67df8 (patch)
tree2e277027cb9a929fefa84d87b621d3a3940ba863 /bower_components/bootstrap/test-infra
parent9fdf7a68d3f4f89e9ea9aea74c485ed31d767ee4 (diff)
upgrade bootstrap to 3.1.0
Diffstat (limited to 'bower_components/bootstrap/test-infra')
-rw-r--r--bower_components/bootstrap/test-infra/README.md100
-rw-r--r--bower_components/bootstrap/test-infra/npm-shrinkwrap.canonical.json1
-rw-r--r--bower_components/bootstrap/test-infra/requirements.txt1
-rwxr-xr-xbower_components/bootstrap/test-infra/s3_cache.py107
-rw-r--r--bower_components/bootstrap/test-infra/sauce_browsers.yml83
-rw-r--r--bower_components/bootstrap/test-infra/shrinkwrap.js24
-rwxr-xr-xbower_components/bootstrap/test-infra/uncached-npm-install.sh4
7 files changed, 320 insertions, 0 deletions
diff --git a/bower_components/bootstrap/test-infra/README.md b/bower_components/bootstrap/test-infra/README.md
new file mode 100644
index 00000000..2ee7ed9a
--- /dev/null
+++ b/bower_components/bootstrap/test-infra/README.md
@@ -0,0 +1,100 @@
+## What does `s3_cache.py` do?
+
+### In general
+`s3_cache.py` maintains a cache, stored in an Amazon S3 (Simple Storage Service) bucket, of a given directory whose contents are considered non-critical and are completely & solely determined by (and should be able to be regenerated from) a single given file.
+
+The SHA-256 hash of the single file is used as the key for the cache. The directory is stored as a gzipped tarball.
+
+All the tarballs are stored in S3's Reduced Redundancy Storage (RRS) storage class, since this is cheaper and the data is non-critical.
+
+`s3_cache.py` itself never deletes cache entries; deletion should either be done manually or using automatic S3 lifecycle rules on the bucket.
+
+Similar to git, `s3_cache.py` makes the assumption that [SHA-256 will effectively never have a collision](http://stackoverflow.com/questions/4014090/is-it-safe-to-ignore-the-possibility-of-sha-collisions-in-practice).
+
+
+### For Bootstrap specifically
+`s3_cache.py` is used to cache the npm packages that our Grunt tasks depend on and the RubyGems that Jekyll depends on. (Jekyll is needed to compile our docs to HTML so that we can run them thru an HTML5 validator.)
+
+For npm, the `node_modules` directory is cached based on our `npm-shrinkwrap.canonical.json` file.
+
+For RubyGems, the `gemdir` of the current RVM-selected Ruby is cached based on the `pseudo_Gemfile.lock` file generated by our Travis build script.
+`pseudo_Gemfile.lock` contains the versions of Ruby and Jekyll that we're using (read our `.travis.yml` for details).
+
+
+## Why is `s3_cache.py` necessary?
+`s3_cache.py` is used to speed up Bootstrap's Travis builds. Installing npm packages and RubyGems used to take up a significant fraction of our total build times. Also, at the time that `s3_cache.py` was written, npm was occasionally unreliable.
+
+Travis does offer built-in caching on their paid plans, but this do-it-ourselves S3 solution is significantly cheaper since we only need caching and not Travis' other paid features.
+
+
+## Setup
+
+### Overview
+1. Create an Amazon Web Services (AWS) account.
+2. Create an Identity & Access Management (IAM) user, and note their credentials.
+3. Create an S3 bucket.
+4. Set permissions on the bucket to grant the user read+write access.
+5. Set the user credentials as secure Travis environment variables.
+
+### In detail
+1. Create an AWS account.
+2. Login to the [AWS Management Console](https://console.aws.amazon.com).
+3. Go to the IAM Management Console.
+4. Create a new user (named e.g. `travis-ci`) and generate an access key for them. Note both the Access Key ID and the Secret Access Key.
+5. Note the user's ARN (Amazon Resource Name), which can be found in the "Summary" tab of the user browser. This will be of the form: `arn:aws:iam::XXXXXXXXXXXXXX:user/the-username-goes-here`
+6. Note the user's access key, which can be found in the "Security Credentials" tab of the user browser.
+7. Go to the S3 Management Console.
+8. Create a new bucket. For a non-publicly-accessible bucket (like Bootstrap uses), it's recommended that the bucket name be random to increase security. On most *nix machines, you can easily generate a random UUID to use as the bucket name using Python:
+
+ ```bash
+ python -c "import uuid; print(uuid.uuid4())"
+ ```
+
+9. Determine and note what your bucket's ARN is. The ARN for an S3 bucket is of the form: `arn:aws:s3:::the-bucket-name-goes-here`
+10. In the bucket's Properties pane, in the "Permissions" section, click the "Edit bucket policy" button.
+11. Input and submit an IAM Policy that grants the user at least read+write rights to the bucket. AWS has a policy generator and some examples to help with crafting the policy. Here's the policy that Bootstrap uses, with the sensitive bits censored:
+
+ ```json
+ {
+ "Version": "2012-10-17",
+ "Id": "PolicyTravisReadWriteNoAdmin",
+ "Statement": [
+ {
+ "Sid": "StmtXXXXXXXXXXXXXX",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": "arn:aws:iam::XXXXXXXXXXXXXX:user/travis-ci"
+ },
+ "Action": [
+ "s3:AbortMultipartUpload",
+ "s3:GetObjectVersion",
+ "s3:ListBucket",
+ "s3:DeleteObject",
+ "s3:DeleteObjectVersion",
+ "s3:GetObject",
+ "s3:PutObject"
+ ],
+ "Resource": [
+ "arn:aws:s3:::XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
+ "arn:aws:s3:::XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/*"
+ ]
+ }
+ ]
+ }
+ ```
+
+12. If you want deletion from the cache to be done automatically based on age (like Bootstrap does): In the bucket's Properties pane, in the "Lifecycle" section, add a rule to expire/delete files based on creation date.
+13. Install the [`travis` RubyGem](https://github.com/travis-ci/travis): `gem install travis`
+14. Encrypt the environment variables:
+
+ ```bash
+ travis encrypt --repo twbs/bootstrap "AWS_ACCESS_KEY_ID=XXX"
+ travis encrypt --repo twbs/bootstrap "AWS_SECRET_ACCESS_KEY=XXX"
+ travis encrypt --repo twbs/bootstrap "TWBS_S3_BUCKET=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
+ ```
+
+14. Add the resulting secure environment variables to `.travis.yml`.
+
+
+## Usage
+Read `s3_cache.py`'s source code and Bootstrap's `.travis.yml` for how to invoke and make use of `s3_cache.py`.
diff --git a/bower_components/bootstrap/test-infra/npm-shrinkwrap.canonical.json b/bower_components/bootstrap/test-infra/npm-shrinkwrap.canonical.json
new file mode 100644
index 00000000..41b2e201
--- /dev/null
+++ b/bower_components/bootstrap/test-infra/npm-shrinkwrap.canonical.json
@@ -0,0 +1 @@
+{"dependencies":{"browserstack-runner":{"dependencies":{"browserstack":{"from":"browserstack@1.0.1","resolved":"https://registry.npmjs.org/browserstack/-/browserstack-1.0.1.tgz","version":"1.0.1"}},"from":"browserstack-runner@~0.0.15","resolved":"https://registry.npmjs.org/browserstack-runner/-/browserstack-runner-0.0.15.tgz","version":"0.0.15"},"btoa":{"from":"btoa@~1.1.1","version":"1.1.1"},"canonical-json":{"from":"canonical-json@~0.0.3","resolved":"https://registry.npmjs.org/canonical-json/-/canonical-json-0.0.3.tgz","version":"0.0.3"},"grunt":{"dependencies":{"async":{"from":"async@~0.1.22","version":"0.1.22"},"coffee-script":{"from":"coffee-script@~1.3.3","version":"1.3.3"},"colors":{"from":"colors@~0.6.0","version":"0.6.2"},"dateformat":{"from":"dateformat@1.0.2-1.2.3","resolved":"https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz","version":"1.0.2-1.2.3"},"eventemitter2":{"from":"eventemitter2@~0.4.9","version":"0.4.13"},"exit":{"from":"exit@~0.1.1","version":"0.1.2"},"findup-sync":{"dependencies":{"lodash":{"from":"lodash@~1.0.1","version":"1.0.1"}},"from":"findup-sync@~0.1.2","version":"0.1.2"},"getobject":{"from":"getobject@~0.1.0","version":"0.1.0"},"glob":{"dependencies":{"graceful-fs":{"from":"graceful-fs@~1.2.0","version":"1.2.3"},"inherits":{"from":"inherits@1","version":"1.0.0"}},"from":"glob@~3.1.21","version":"3.1.21"},"hooker":{"from":"hooker@~0.2.3","resolved":"https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz","version":"0.2.3"},"iconv-lite":{"from":"iconv-lite@~0.2.11","version":"0.2.11"},"js-yaml":{"dependencies":{"argparse":{"dependencies":{"underscore":{"from":"underscore@~1.4.3","version":"1.4.4"},"underscore.string":{"from":"underscore.string@~2.3.1","version":"2.3.3"}},"from":"argparse@~ 0.1.11","version":"0.1.15"},"esprima":{"from":"esprima@~ 1.0.2","version":"1.0.4"}},"from":"js-yaml@~2.0.5","version":"2.0.5"},"lodash":{"from":"lodash@~0.9.2","version":"0.9.2"},"minimatch":{"dependencies":{"lru-cache":{"from":"lru-cache@2","version":"2.5.0"},"sigmund":{"from":"sigmund@~1.0.0","version":"1.0.0"}},"from":"minimatch@~0.2.11","version":"0.2.14"},"nopt":{"dependencies":{"abbrev":{"from":"abbrev@1","version":"1.0.4"}},"from":"nopt@~1.0.10","version":"1.0.10"},"rimraf":{"dependencies":{"graceful-fs":{"from":"graceful-fs@~1.1","version":"1.1.14"}},"from":"rimraf@~2.0.3","version":"2.0.3"},"underscore.string":{"from":"underscore.string@~2.2.1","version":"2.2.1"},"which":{"from":"which@~1.0.5","version":"1.0.5"}},"from":"grunt@~0.4.2","version":"0.4.2"},"grunt-banner":{"from":"grunt-banner@~0.2.0","version":"0.2.0"},"grunt-contrib-clean":{"dependencies":{"rimraf":{"from":"rimraf@~2.2.1","resolved":"https://registry.npmjs.org/rimraf/-/rimraf-2.2.6.tgz","version":"2.2.6"}},"from":"grunt-contrib-clean@~0.5.0","version":"0.5.0"},"grunt-contrib-concat":{"from":"grunt-contrib-concat@~0.3.0","version":"0.3.0"},"grunt-contrib-connect":{"dependencies":{"connect":{"dependencies":{"batch":{"from":"batch@0.5.0","resolved":"https://registry.npmjs.org/batch/-/batch-0.5.0.tgz","version":"0.5.0"},"buffer-crc32":{"from":"buffer-crc32@0.2.1","resolved":"https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz","version":"0.2.1"},"bytes":{"from":"bytes@0.2.1","resolved":"https://registry.npmjs.org/bytes/-/bytes-0.2.1.tgz","version":"0.2.1"},"cookie":{"from":"cookie@0.1.0","resolved":"https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz","version":"0.1.0"},"cookie-signature":{"from":"cookie-signature@1.0.1","resolved":"https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz","version":"1.0.1"},"debug":{"from":"debug@~0.7.2","version":"0.7.4"},"fresh":{"from":"fresh@0.2.0","resolved":"https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz","version":"0.2.0"},"methods":{"from":"methods@0.1.0","resolved":"https://registry.npmjs.org/methods/-/methods-0.1.0.tgz","version":"0.1.0"},"multiparty":{"dependencies":{"readable-stream":{"dependencies":{"core-util-is":{"from":"core-util-is@~1.0.0","version":"1.0.1"},"debuglog":{"from":"debuglog@0.0.2","resolved":"https://registry.npmjs.org/debuglog/-/debuglog-0.0.2.tgz","version":"0.0.2"}},"from":"readable-stream@~1.1.9","version":"1.1.10"},"stream-counter":{"from":"stream-counter@~0.2.0","version":"0.2.0"}},"from":"multiparty@2.2.0","resolved":"https://registry.npmjs.org/multiparty/-/multiparty-2.2.0.tgz","version":"2.2.0"},"negotiator":{"from":"negotiator@0.3.0","resolved":"https://registry.npmjs.org/negotiator/-/negotiator-0.3.0.tgz","version":"0.3.0"},"pause":{"from":"pause@0.0.1","resolved":"https://registry.npmjs.org/pause/-/pause-0.0.1.tgz","version":"0.0.1"},"qs":{"from":"qs@~0.6.0","resolved":"https://registry.npmjs.org/qs/-/qs-0.6.6.tgz","version":"0.6.6"},"raw-body":{"from":"raw-body@1.1.2","resolved":"https://registry.npmjs.org/raw-body/-/raw-body-1.1.2.tgz","version":"1.1.2"},"send":{"dependencies":{"mime":{"from":"mime@~1.2.9","version":"1.2.11"},"range-parser":{"from":"range-parser@0.0.4","resolved":"https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz","version":"0.0.4"}},"from":"send@0.1.4","resolved":"https://registry.npmjs.org/send/-/send-0.1.4.tgz","version":"0.1.4"},"uid2":{"from":"uid2@0.0.3","resolved":"https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz","version":"0.0.3"}},"from":"connect@~2.12.0","version":"2.12.0"},"connect-livereload":{"from":"connect-livereload@~0.3.0","version":"0.3.2"},"open":{"from":"open@0.0.4","resolved":"https://registry.npmjs.org/open/-/open-0.0.4.tgz","version":"0.0.4"}},"from":"grunt-contrib-connect@~0.6.0","version":"0.6.0"},"grunt-contrib-copy":{"from":"grunt-contrib-copy@~0.5.0","version":"0.5.0"},"grunt-contrib-csslint":{"dependencies":{"csslint":{"dependencies":{"parserlib":{"from":"parserlib@~0.2.2","resolved":"https://registry.npmjs.org/parserlib/-/parserlib-0.2.4.tgz","version":"0.2.4"}},"from":"csslint@~0.10.0","version":"0.10.0"}},"from":"grunt-contrib-csslint@~0.2.0","version":"0.2.0"},"grunt-contrib-cssmin":{"dependencies":{"clean-css":{"dependencies":{"commander":{"from":"commander@2.0.x","version":"2.0.0"}},"from":"clean-css@~2.0.0","version":"2.0.6"},"grunt-lib-contrib":{"dependencies":{"zlib-browserify":{"from":"zlib-browserify@0.0.1","resolved":"https://registry.npmjs.org/zlib-browserify/-/zlib-browserify-0.0.1.tgz","version":"0.0.1"}},"from":"grunt-lib-contrib@~0.6.0","version":"0.6.1"}},"from":"grunt-contrib-cssmin@~0.7.0","version":"0.7.0"},"grunt-contrib-jade":{"dependencies":{"grunt-lib-contrib":{"dependencies":{"zlib-browserify":{"from":"zlib-browserify@0.0.1","resolved":"https://registry.npmjs.org/zlib-browserify/-/zlib-browserify-0.0.1.tgz","version":"0.0.1"}},"from":"grunt-lib-contrib@~0.6.1","version":"0.6.1"},"jade":{"dependencies":{"character-parser":{"from":"character-parser@1.2.0","resolved":"https://registry.npmjs.org/character-parser/-/character-parser-1.2.0.tgz","version":"1.2.0"},"commander":{"from":"commander@2.0.0","resolved":"https://registry.npmjs.org/commander/-/commander-2.0.0.tgz","version":"2.0.0"},"constantinople":{"dependencies":{"uglify-js":{"dependencies":{"async":{"from":"async@~0.2.6","version":"0.2.9"},"optimist":{"dependencies":{"wordwrap":{"from":"wordwrap@~0.0.2","version":"0.0.2"}},"from":"optimist@~0.3.5","version":"0.3.7"},"source-map":{"dependencies":{"amdefine":{"from":"amdefine@>=0.0.4","version":"0.1.0"}},"from":"source-map@~0.1.7","version":"0.1.31"},"uglify-to-browserify":{"from":"uglify-to-browserify@~1.0.0","version":"1.0.1"}},"from":"uglify-js@~2.4.0","resolved":"https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.9.tgz","version":"2.4.9"}},"from":"constantinople@~1.0.2","version":"1.0.2"},"mkdirp":{"from":"mkdirp@~0.3.5","resolved":"https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz","version":"0.3.5"},"monocle":{"dependencies":{"readdirp":{"dependencies":{"minimatch":{"dependencies":{"lru-cache":{"from":"lru-cache@2","version":"2.5.0"},"sigmund":{"from":"sigmund@~1.0.0","version":"1.0.0"}},"from":"minimatch@>=0.2.4","version":"0.2.14"}},"from":"readdirp@~0.2.3","version":"0.2.5"}},"from":"monocle@1.1.50","resolved":"https://registry.npmjs.org/monocle/-/monocle-1.1.50.tgz","version":"1.1.50"},"transformers":{"dependencies":{"css":{"dependencies":{"css-parse":{"from":"css-parse@1.0.4","resolved":"https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz","version":"1.0.4"},"css-stringify":{"from":"css-stringify@1.0.5","resolved":"https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz","version":"1.0.5"}},"from":"css@~1.0.8","version":"1.0.8"},"promise":{"dependencies":{"is-promise":{"from":"is-promise@~1","version":"1.0.0"}},"from":"promise@~2.0","version":"2.0.0"},"uglify-js":{"dependencies":{"optimist":{"dependencies":{"wordwrap":{"from":"wordwrap@~0.0.2","version":"0.0.2"}},"from":"optimist@~0.3.5","version":"0.3.7"},"source-map":{"dependencies":{"amdefine":{"from":"amdefine@>=0.0.4","version":"0.1.0"}},"from":"source-map@~0.1.7","version":"0.1.31"}},"from":"uglify-js@~2.2.5","version":"2.2.5"}},"from":"transformers@2.1.0","resolved":"https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz","version":"2.1.0"},"with":{"dependencies":{"uglify-js":{"dependencies":{"async":{"from":"async@~0.2.6","version":"0.2.9"},"optimist":{"dependencies":{"wordwrap":{"from":"wordwrap@~0.0.2","version":"0.0.2"}},"from":"optimist@~0.3.5","version":"0.3.7"},"source-map":{"dependencies":{"amdefine":{"from":"amdefine@>=0.0.4","version":"0.1.0"}},"from":"source-map@~0.1.7","version":"0.1.31"},"uglify-to-browserify":{"from":"uglify-to-browserify@~1.0.0","version":"1.0.1"}},"from":"uglify-js@2.4.0","resolved":"https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.0.tgz","version":"2.4.0"}},"from":"with@~2.0.0","version":"2.0.0"}},"from":"jade@~1.0.2","version":"1.0.2"},"lodash-node":{"from":"lodash-node@~2.4.1","version":"2.4.1"}},"from":"grunt-contrib-jade@~0.9.1","version":"0.9.1"},"grunt-contrib-jshint":{"dependencies":{"jshint":{"dependencies":{"cli":{"dependencies":{"glob":{"dependencies":{"inherits":{"from":"inherits@2","version":"2.0.1"}},"from":"glob@>= 3.1.4","version":"3.2.8"}},"from":"cli@0.4.x","version":"0.4.5"},"console-browserify":{"from":"console-browserify@0.1.x","version":"0.1.6"},"htmlparser2":{"dependencies":{"domelementtype":{"from":"domelementtype@1","version":"1.1.1"},"domhandler":{"from":"domhandler@2.1","version":"2.1.0"},"domutils":{"from":"domutils@1.1","version":"1.1.6"},"readable-stream":{"from":"readable-stream@1.0","version":"1.0.24"}},"from":"htmlparser2@3.3.x","version":"3.3.0"},"minimatch":{"dependencies":{"lru-cache":{"from":"lru-cache@2","version":"2.5.0"},"sigmund":{"from":"sigmund@~1.0.0","version":"1.0.0"}},"from":"minimatch@0.x.x","version":"0.2.14"},"shelljs":{"from":"shelljs@0.1.x","version":"0.1.4"},"underscore":{"from":"underscore@1.4.x","version":"1.4.4"}},"from":"jshint@~2.4.0","version":"2.4.1"}},"from":"grunt-contrib-jshint@~0.8.0","version":"0.8.0"},"grunt-contrib-less":{"dependencies":{"chalk":{"dependencies":{"ansi-styles":{"from":"ansi-styles@~1.0.0","version":"1.0.0"},"has-color":{"from":"has-color@~0.1.0","version":"0.1.2"},"strip-ansi":{"from":"strip-ansi@~0.1.0","version":"0.1.1"}},"from":"chalk@~0.4.0","version":"0.4.0"},"grunt-lib-contrib":{"dependencies":{"zlib-browserify":{"from":"zlib-browserify@0.0.1","resolved":"https://registry.npmjs.org/zlib-browserify/-/zlib-browserify-0.0.1.tgz","version":"0.0.1"}},"from":"grunt-lib-contrib@~0.6.1","version":"0.6.1"},"less":{"dependencies":{"clean-css":{"dependencies":{"commander":{"from":"commander@2.0.x","resolved":"https://registry.npmjs.org/commander/-/commander-2.0.0.tgz","version":"2.0.0"}},"from":"clean-css@2.0.x","version":"2.0.6"},"mime":{"from":"mime@1.2.x","version":"1.2.11"},"mkdirp":{"from":"mkdirp@~0.3.5","resolved":"https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz","version":"0.3.5"},"request":{"dependencies":{"aws-sign2":{"from":"aws-sign2@~0.5.0","version":"0.5.0"},"forever-agent":{"from":"forever-agent@~0.5.0","version":"0.5.0"},"form-data":{"dependencies":{"async":{"from":"async@~0.2.9","version":"0.2.9"},"combined-stream":{"dependencies":{"delayed-stream":{"from":"delayed-stream@0.0.5","resolved":"https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz","version":"0.0.5"}},"from":"combined-stream@~0.0.4","version":"0.0.4"}},"from":"form-data@~0.1.0","version":"0.1.2"},"hawk":{"dependencies":{"boom":{"from":"boom@0.4.x","version":"0.4.2"},"cryptiles":{"from":"cryptiles@0.2.x","version":"0.2.2"},"hoek":{"from":"hoek@0.9.x","version":"0.9.1"},"sntp":{"from":"sntp@0.2.x","version":"0.2.4"}},"from":"hawk@~1.0.0","version":"1.0.0"},"http-signature":{"dependencies":{"asn1":{"from":"asn1@0.1.11","resolved":"https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz","version":"0.1.11"},"assert-plus":{"from":"assert-plus@0.1.2","resolved":"https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz","version":"0.1.2"},"ctype":{"from":"ctype@0.5.2","resolved":"https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz","version":"0.5.2"}},"from":"http-signature@~0.10.0","version":"0.10.0"},"json-stringify-safe":{"from":"json-stringify-safe@~5.0.0","version":"5.0.0"},"node-uuid":{"from":"node-uuid@~1.4.0","version":"1.4.1"},"oauth-sign":{"from":"oauth-sign@~0.3.0","version":"0.3.0"},"qs":{"from":"qs@~0.6.0","resolved":"https://registry.npmjs.org/qs/-/qs-0.6.6.tgz","version":"0.6.6"},"tough-cookie":{"dependencies":{"punycode":{"from":"punycode@>=0.2.0","version":"1.2.3"}},"from":"tough-cookie@~0.9.15","version":"0.9.15"},"tunnel-agent":{"from":"tunnel-agent@~0.3.0","version":"0.3.0"}},"from":"request@>=2.12.0","version":"2.31.0"},"source-map":{"dependencies":{"amdefine":{"from":"amdefine@>=0.0.4","version":"0.1.0"}},"from":"source-map@0.1.x","version":"0.1.31"}},"from":"less@~1.6.0","resolved":"https://registry.npmjs.org/less/-/less-1.6.1.tgz","version":"1.6.1"}},"from":"grunt-contrib-less@~0.9.0","version":"0.9.0"},"grunt-contrib-qunit":{"dependencies":{"grunt-lib-phantomjs":{"dependencies":{"eventemitter2":{"from":"eventemitter2@~0.4.9","version":"0.4.13"},"phantomjs":{"dependencies":{"adm-zip":{"from":"adm-zip@0.2.1","resolved":"https://registry.npmjs.org/adm-zip/-/adm-zip-0.2.1.tgz","version":"0.2.1"},"kew":{"from":"kew@~0.1.7","version":"0.1.7"},"mkdirp":{"from":"mkdirp@0.3.5","resolved":"https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz","version":"0.3.5"},"ncp":{"from":"ncp@0.4.2","resolved":"https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz","version":"0.4.2"},"npmconf":{"dependencies":{"config-chain":{"dependencies":{"proto-list":{"from":"proto-list@~1.2.1","version":"1.2.2"}},"from":"config-chain@~1.1.1","version":"1.1.8"},"inherits":{"from":"inherits@~1.0.0","version":"1.0.0"},"ini":{"from":"ini@~1.1.0","version":"1.1.0"},"nopt":{"dependencies":{"abbrev":{"from":"abbrev@1","version":"1.0.4"}},"from":"nopt@2","version":"2.1.2"},"once":{"from":"once@~1.1.1","version":"1.1.1"},"osenv":{"from":"osenv@0.0.3","resolved":"https://registry.npmjs.org/osenv/-/osenv-0.0.3.tgz","version":"0.0.3"},"semver":{"from":"semver@~1.1.0","version":"1.1.4"}},"from":"npmconf@0.0.24","resolved":"https://registry.npmjs.org/npmconf/-/npmconf-0.0.24.tgz","version":"0.0.24"},"rimraf":{"from":"rimraf@~2.2.2","version":"2.2.6"},"which":{"from":"which@~1.0.5","version":"1.0.5"}},"from":"phantomjs@~1.9.0-1","resolved":"https://registry.npmjs.org/phantomjs/-/phantomjs-1.9.7-1.tgz","version":"1.9.7-1"},"semver":{"from":"semver@~1.0.14","version":"1.0.14"},"temporary":{"dependencies":{"package":{"from":"package@>= 1.0.0 < 1.2.0","version":"1.0.1"}},"from":"temporary@~0.0.4","version":"0.0.8"}},"from":"grunt-lib-phantomjs@~0.5.0","resolved":"https://registry.npmjs.org/grunt-lib-phantomjs/-/grunt-lib-phantomjs-0.5.0.tgz","version":"0.5.0"}},"from":"grunt-contrib-qunit@~0.4.0","resolved":"https://registry.npmjs.org/grunt-contrib-qunit/-/grunt-contrib-qunit-0.4.0.tgz","version":"0.4.0"},"grunt-contrib-uglify":{"dependencies":{"chalk":{"dependencies":{"ansi-styles":{"from":"ansi-styles@~1.0.0","version":"1.0.0"},"has-color":{"from":"has-color@~0.1.0","version":"0.1.2"},"strip-ansi":{"from":"strip-ansi@~0.1.0","version":"0.1.1"}},"from":"chalk@~0.4.0","version":"0.4.0"},"grunt-lib-contrib":{"dependencies":{"zlib-browserify":{"from":"zlib-browserify@0.0.1","resolved":"https://registry.npmjs.org/zlib-browserify/-/zlib-browserify-0.0.1.tgz","version":"0.0.1"}},"from":"grunt-lib-contrib@~0.6.1","version":"0.6.1"},"uglify-js":{"dependencies":{"async":{"from":"async@~0.2.6","resolved":"https://registry.npmjs.org/async/-/async-0.2.10.tgz","version":"0.2.10"},"optimist":{"dependencies":{"wordwrap":{"from":"wordwrap@~0.0.2","version":"0.0.2"}},"from":"optimist@~0.3.5","version":"0.3.7"},"source-map":{"dependencies":{"amdefine":{"from":"amdefine@>=0.0.4","version":"0.1.0"}},"from":"source-map@~0.1.7","version":"0.1.31"},"uglify-to-browserify":{"from":"uglify-to-browserify@~1.0.0","version":"1.0.1"}},"from":"uglify-js@~2.4.0","resolved":"https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.12.tgz","version":"2.4.12"}},"from":"grunt-contrib-uglify@~0.3.0","resolved":"https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-0.3.2.tgz","version":"0.3.2"},"grunt-contrib-watch":{"dependencies":{"gaze":{"dependencies":{"globule":{"dependencies":{"glob":{"dependencies":{"graceful-fs":{"from":"graceful-fs@~1.2.0","version":"1.2.3"},"inherits":{"from":"inherits@1","version":"1.0.0"}},"from":"glob@~3.1.21","version":"3.1.21"},"lodash":{"from":"lodash@~1.0.1","version":"1.0.1"},"minimatch":{"dependencies":{"lru-cache":{"from":"lru-cache@2","version":"2.5.0"},"sigmund":{"from":"sigmund@~1.0.0","version":"1.0.0"}},"from":"minimatch@~0.2.11","version":"0.2.14"}},"from":"globule@~0.1.0","version":"0.1.0"}},"from":"gaze@~0.4.0","version":"0.4.3"},"tiny-lr":{"dependencies":{"debug":{"from":"debug@~0.7.2","version":"0.7.4"},"faye-websocket":{"from":"faye-websocket@~0.4.3","version":"0.4.4"},"noptify":{"dependencies":{"nopt":{"dependencies":{"abbrev":{"from":"abbrev@1","version":"1.0.4"}},"from":"nopt@~2.0.0","version":"2.0.0"}},"from":"noptify@latest","version":"0.0.3"},"qs":{"from":"qs@~0.5.2","version":"0.5.6"}},"from":"tiny-lr@0.0.4","resolved":"https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz","version":"0.0.4"}},"from":"grunt-contrib-watch@~0.5.3","version":"0.5.3"},"grunt-csscomb":{"dependencies":{"csscomb":{"dependencies":{"commander":{"from":"commander@2.0.0","resolved":"https://registry.npmjs.org/commander/-/commander-2.0.0.tgz","version":"2.0.0"},"gonzales-pe":{"from":"gonzales-pe@2.0.x","resolved":"https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-2.0.2.tgz","version":"2.0.2"},"minimatch":{"dependencies":{"lru-cache":{"from":"lru-cache@2","version":"2.5.0"},"sigmund":{"from":"sigmund@~1.0.0","version":"1.0.0"}},"from":"minimatch@0.2.12","resolved":"https://registry.npmjs.org/minimatch/-/minimatch-0.2.12.tgz","version":"0.2.12"},"vow":{"from":"vow@0.3.11","resolved":"https://registry.npmjs.org/vow/-/vow-0.3.11.tgz","version":"0.3.11"},"vow-fs":{"dependencies":{"node-uuid":{"from":"node-uuid@1.4.0","resolved":"https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.0.tgz","version":"1.4.0"},"vow-queue":{"from":"vow-queue@0.0.2","resolved":"https://registry.npmjs.org/vow-queue/-/vow-queue-0.0.2.tgz","version":"0.0.2"}},"from":"vow-fs@0.2.3","resolved":"https://registry.npmjs.org/vow-fs/-/vow-fs-0.2.3.tgz","version":"0.2.3"}},"from":"csscomb@~2.0.0","resolved":"https://registry.npmjs.org/csscomb/-/csscomb-2.0.4.tgz","version":"2.0.4"}},"from":"grunt-csscomb@~2.0.1","version":"2.0.1"},"grunt-exec":{"from":"grunt-exec@0.4.3","resolved":"https://registry.npmjs.org/grunt-exec/-/grunt-exec-0.4.3.tgz","version":"0.4.3"},"grunt-html-validation":{"dependencies":{"colors":{"from":"colors@~0.6.0","version":"0.6.2"},"request":{"dependencies":{"aws-sign":{"from":"aws-sign@~0.3.0","version":"0.3.0"},"cookie-jar":{"from":"cookie-jar@~0.3.0","version":"0.3.0"},"forever-agent":{"from":"forever-agent@~0.5.0","version":"0.5.0"},"form-data":{"dependencies":{"async":{"from":"async@~0.2.9","version":"0.2.9"},"combined-stream":{"dependencies":{"delayed-stream":{"from":"delayed-stream@0.0.5","resolved":"https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz","version":"0.0.5"}},"from":"combined-stream@~0.0.4","version":"0.0.4"}},"from":"form-data@~0.1.0","version":"0.1.2"},"hawk":{"dependencies":{"boom":{"from":"boom@0.4.x","version":"0.4.2"},"cryptiles":{"from":"cryptiles@0.2.x","version":"0.2.2"},"hoek":{"from":"hoek@0.9.x","version":"0.9.1"},"sntp":{"from":"sntp@0.2.x","version":"0.2.4"}},"from":"hawk@~1.0.0","version":"1.0.0"},"http-signature":{"dependencies":{"asn1":{"from":"asn1@0.1.11","resolved":"https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz","version":"0.1.11"},"assert-plus":{"from":"assert-plus@0.1.2","resolved":"https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz","version":"0.1.2"},"ctype":{"from":"ctype@0.5.2","resolved":"https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz","version":"0.5.2"}},"from":"http-signature@~0.10.0","version":"0.10.0"},"json-stringify-safe":{"from":"json-stringify-safe@~5.0.0","version":"5.0.0"},"mime":{"from":"mime@~1.2.9","version":"1.2.11"},"node-uuid":{"from":"node-uuid@~1.4.0","version":"1.4.1"},"oauth-sign":{"from":"oauth-sign@~0.3.0","version":"0.3.0"},"qs":{"from":"qs@~0.6.0","version":"0.6.6"},"tunnel-agent":{"from":"tunnel-agent@~0.3.0","version":"0.3.0"}},"from":"request@~2.27.0","version":"2.27.0"},"w3cjs":{"dependencies":{"commander":{"from":"commander@2.0.x","version":"2.0.0"},"superagent":{"dependencies":{"cookiejar":{"from":"cookiejar@1.3.0","resolved":"https://registry.npmjs.org/cookiejar/-/cookiejar-1.3.0.tgz","version":"1.3.0"},"debug":{"from":"debug@~0.7.2","version":"0.7.4"},"emitter-component":{"from":"emitter-component@1.0.0","resolved":"https://registry.npmjs.org/emitter-component/-/emitter-component-1.0.0.tgz","version":"1.0.0"},"formidable":{"from":"formidable@1.0.14","resolved":"https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz","version":"1.0.14"},"methods":{"from":"methods@0.0.1","resolved":"https://registry.npmjs.org/methods/-/methods-0.0.1.tgz","version":"0.0.1"},"mime":{"from":"mime@1.2.5","resolved":"https://registry.npmjs.org/mime/-/mime-1.2.5.tgz","version":"1.2.5"},"qs":{"from":"qs@0.6.5","resolved":"https://registry.npmjs.org/qs/-/qs-0.6.5.tgz","version":"0.6.5"},"reduce-component":{"from":"reduce-component@1.0.1","resolved":"https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz","version":"1.0.1"}},"from":"superagent@~0.15.7","version":"0.15.7"},"superagent-proxy":{"dependencies":{"proxy-agent":{"dependencies":{"http-proxy-agent":{"dependencies":{"agent-base":{"from":"agent-base@~1.0.1","version":"1.0.1"},"debug":{"from":"debug@~0.7.0","version":"0.7.4"},"extend":{"from":"extend@~1.2.0","version":"1.2.1"}},"from":"http-proxy-agent@0","resolved":"https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-0.2.4.tgz","version":"0.2.4"},"https-proxy-agent":{"dependencies":{"agent-base":{"from":"agent-base@~1.0.1","version":"1.0.1"},"debug":{"from":"debug@~0.7.0","version":"0.7.4"},"extend":{"from":"extend@~1.2.0","version":"1.2.1"}},"from":"https-proxy-agent@0","resolved":"https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-0.3.3.tgz","version":"0.3.3"},"lru-cache":{"from":"lru-cache@~2.3.1","version":"2.3.1"},"socks-proxy-agent":{"dependencies":{"agent-base":{"from":"agent-base@~1.0.1","version":"1.0.1"},"extend":{"from":"extend@~1.2.0","version":"1.2.1"},"rainbowsocks":{"dependencies":{"debug":{"from":"debug@~0.7.2","version":"0.7.4"}},"from":"rainbowsocks@~0.1.0","version":"0.1.1"}},"from":"socks-proxy-agent@0","version":"0.1.0"}},"from":"proxy-agent@~0.0.2","version":"0.0.2"}},"from":"superagent-proxy@~0.2.0","version":"0.2.0"}},"from":"w3cjs@~0.1.22","version":"0.1.22"}},"from":"grunt-html-validation@~0.1.13","version":"0.1.13"},"grunt-jekyll":{"dependencies":{"tmp":{"from":"tmp@0.0.21","resolved":"https://registry.npmjs.org/tmp/-/tmp-0.0.21.tgz","version":"0.0.21"}},"from":"grunt-jekyll@~0.4.1","version":"0.4.1"},"grunt-jscs-checker":{"dependencies":{"hooker":{"from":"hooker@~0.2.3","resolved":"https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz","version":"0.2.3"},"jscs":{"dependencies":{"colors":{"from":"colors@0.6.0-1","resolved":"https://registry.npmjs.org/colors/-/colors-0.6.0-1.tgz","version":"0.6.0-1"},"commander":{"dependencies":{"keypress":{"from":"keypress@0.1.x","version":"0.1.0"}},"from":"commander@1.2.0","resolved":"https://registry.npmjs.org/commander/-/commander-1.2.0.tgz","version":"1.2.0"},"esprima":{"from":"esprima@1.0.3","resolved":"https://registry.npmjs.org/esprima/-/esprima-1.0.3.tgz","version":"1.0.3"},"glob":{"dependencies":{"inherits":{"from":"inherits@2","version":"2.0.1"}},"from":"glob@3.2.7","resolved":"https://registry.npmjs.org/glob/-/glob-3.2.7.tgz","version":"3.2.7"},"minimatch":{"dependencies":{"lru-cache":{"from":"lru-cache@2","version":"2.5.0"},"sigmund":{"from":"sigmund@~1.0.0","version":"1.0.0"}},"from":"minimatch@0.2.12","resolved":"https://registry.npmjs.org/minimatch/-/minimatch-0.2.12.tgz","version":"0.2.12"},"vow":{"from":"vow@0.3.9","resolved":"https://registry.npmjs.org/vow/-/vow-0.3.9.tgz","version":"0.3.9"},"vow-fs":{"dependencies":{"node-uuid":{"from":"node-uuid@1.4.0","resolved":"https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.0.tgz","version":"1.4.0"},"vow-queue":{"from":"vow-queue@0.0.2","resolved":"https://registry.npmjs.org/vow-queue/-/vow-queue-0.0.2.tgz","version":"0.0.2"}},"from":"vow-fs@0.2.3","resolved":"https://registry.npmjs.org/vow-fs/-/vow-fs-0.2.3.tgz","version":"0.2.3"},"xmlbuilder":{"dependencies":{"underscore":{"from":"underscore@>=1.5.x","version":"1.5.2"}},"from":"xmlbuilder@1.1.2","resolved":"https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-1.1.2.tgz","version":"1.1.2"}},"from":"jscs@~1.2.0","resolved":"https://registry.npmjs.org/jscs/-/jscs-1.2.3.tgz","version":"1.2.3"},"lodash.assign":{"dependencies":{"lodash._basecreatecallback":{"dependencies":{"lodash._setbinddata":{"dependencies":{"lodash._isnative":{"from":"lodash._isnative@~2.4.1","version":"2.4.1"},"lodash.noop":{"from":"lodash.noop@~2.4.1","version":"2.4.1"}},"from":"lodash._setbinddata@~2.4.1","version":"2.4.1"},"lodash.bind":{"dependencies":{"lodash._createwrapper":{"dependencies":{"lodash._basebind":{"dependencies":{"lodash._basecreate":{"dependencies":{"lodash._isnative":{"from":"lodash._isnative@~2.4.1","version":"2.4.1"},"lodash.noop":{"from":"lodash.noop@~2.4.1","version":"2.4.1"}},"from":"lodash._basecreate@~2.4.1","version":"2.4.1"},"lodash.isobject":{"from":"lodash.isobject@~2.4.1","version":"2.4.1"}},"from":"lodash._basebind@~2.4.1","version":"2.4.1"},"lodash._basecreatewrapper":{"dependencies":{"lodash._basecreate":{"dependencies":{"lodash._isnative":{"from":"lodash._isnative@~2.4.1","version":"2.4.1"},"lodash.noop":{"from":"lodash.noop@~2.4.1","version":"2.4.1"}},"from":"lodash._basecreate@~2.4.1","version":"2.4.1"},"lodash.isobject":{"from":"lodash.isobject@~2.4.1","version":"2.4.1"}},"from":"lodash._basecreatewrapper@~2.4.1","version":"2.4.1"},"lodash.isfunction":{"from":"lodash.isfunction@~2.4.1","version":"2.4.1"}},"from":"lodash._createwrapper@~2.4.1","version":"2.4.1"},"lodash._slice":{"from":"lodash._slice@~2.4.1","version":"2.4.1"}},"from":"lodash.bind@~2.4.1","version":"2.4.1"},"lodash.identity":{"from":"lodash.identity@~2.4.1","version":"2.4.1"},"lodash.support":{"dependencies":{"lodash._isnative":{"from":"lodash._isnative@~2.4.1","version":"2.4.1"}},"from":"lodash.support@~2.4.1","version":"2.4.1"}},"from":"lodash._basecreatecallback@~2.4.1","version":"2.4.1"},"lodash._objecttypes":{"from":"lodash._objecttypes@~2.4.1","version":"2.4.1"},"lodash.keys":{"dependencies":{"lodash._isnative":{"from":"lodash._isnative@~2.4.1","version":"2.4.1"},"lodash._shimkeys":{"from":"lodash._shimkeys@~2.4.1","version":"2.4.1"},"lodash.isobject":{"from":"lodash.isobject@~2.4.1","version":"2.4.1"}},"from":"lodash.keys@~2.4.1","version":"2.4.1"}},"from":"lodash.assign@2.4.1","resolved":"https://registry.npmjs.org/lodash.assign/-/lodash.assign-2.4.1.tgz","version":"2.4.1"},"vow":{"from":"vow@0.4.0","resolved":"https://registry.npmjs.org/vow/-/vow-0.4.0.tgz","version":"0.4.0"}},"from":"grunt-jscs-checker@~0.3.0","resolved":"https://registry.npmjs.org/grunt-jscs-checker/-/grunt-jscs-checker-0.3.2.tgz","version":"0.3.2"},"grunt-saucelabs":{"dependencies":{"colors":{"from":"colors@~0.6.0","version":"0.6.2"},"request":{"dependencies":{"aws-sign":{"from":"aws-sign@~0.3.0","version":"0.3.0"},"cookie-jar":{"from":"cookie-jar@~0.3.0","version":"0.3.0"},"forever-agent":{"from":"forever-agent@~0.5.0","version":"0.5.0"},"form-data":{"dependencies":{"async":{"from":"async@~0.2.9","version":"0.2.9"},"combined-stream":{"dependencies":{"delayed-stream":{"from":"delayed-stream@0.0.5","resolved":"https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz","version":"0.0.5"}},"from":"combined-stream@~0.0.4","version":"0.0.4"}},"from":"form-data@~0.1.0","version":"0.1.2"},"hawk":{"dependencies":{"boom":{"from":"boom@0.4.x","version":"0.4.2"},"cryptiles":{"from":"cryptiles@0.2.x","version":"0.2.2"},"hoek":{"from":"hoek@0.9.x","version":"0.9.1"},"sntp":{"from":"sntp@0.2.x","version":"0.2.4"}},"from":"hawk@~1.0.0","version":"1.0.0"},"http-signature":{"dependencies":{"asn1":{"from":"asn1@0.1.11","resolved":"https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz","version":"0.1.11"},"assert-plus":{"from":"assert-plus@0.1.2","resolved":"https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz","version":"0.1.2"},"ctype":{"from":"ctype@0.5.2","resolved":"https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz","version":"0.5.2"}},"from":"http-signature@~0.10.0","version":"0.10.0"},"json-stringify-safe":{"from":"json-stringify-safe@~5.0.0","version":"5.0.0"},"mime":{"from":"mime@~1.2.9","version":"1.2.11"},"node-uuid":{"from":"node-uuid@~1.4.0","version":"1.4.1"},"oauth-sign":{"from":"oauth-sign@~0.3.0","version":"0.3.0"},"qs":{"from":"qs@~0.6.0","resolved":"https://registry.npmjs.org/qs/-/qs-0.6.6.tgz","version":"0.6.6"},"tunnel-agent":{"from":"tunnel-agent@~0.3.0","version":"0.3.0"}},"from":"request@~2.27.0","version":"2.27.0"},"sauce-tunnel":{"dependencies":{"request":{"dependencies":{"aws-sign":{"from":"aws-sign@~0.3.0","version":"0.3.0"},"cookie-jar":{"from":"cookie-jar@~0.3.0","version":"0.3.0"},"forever-agent":{"from":"forever-agent@~0.5.0","version":"0.5.0"},"form-data":{"dependencies":{"async":{"from":"async@~0.2.7","version":"0.2.9"},"combined-stream":{"dependencies":{"delayed-stream":{"from":"delayed-stream@0.0.5","resolved":"https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz","version":"0.0.5"}},"from":"combined-stream@~0.0.4","version":"0.0.4"}},"from":"form-data@0.0.8","resolved":"https://registry.npmjs.org/form-data/-/form-data-0.0.8.tgz","version":"0.0.8"},"hawk":{"dependencies":{"boom":{"dependencies":{"hoek":{"from":"hoek@0.9.x","version":"0.9.1"}},"from":"boom@0.4.x","version":"0.4.2"},"cryptiles":{"from":"cryptiles@0.2.x","version":"0.2.2"},"hoek":{"from":"hoek@0.8.x","version":"0.8.5"},"sntp":{"dependencies":{"hoek":{"from":"hoek@0.9.x","version":"0.9.1"}},"from":"sntp@0.2.x","version":"0.2.4"}},"from":"hawk@~0.13.0","version":"0.13.1"},"http-signature":{"dependencies":{"asn1":{"from":"asn1@0.1.11","resolved":"https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz","version":"0.1.11"},"assert-plus":{"from":"assert-plus@0.1.2","resolved":"https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz","version":"0.1.2"},"ctype":{"from":"ctype@0.5.2","resolved":"https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz","version":"0.5.2"}},"from":"http-signature@~0.9.11","version":"0.9.11"},"json-stringify-safe":{"from":"json-stringify-safe@~4.0.0","version":"4.0.0"},"mime":{"from":"mime@~1.2.9","version":"1.2.11"},"node-uuid":{"from":"node-uuid@~1.4.0","version":"1.4.1"},"oauth-sign":{"from":"oauth-sign@~0.3.0","version":"0.3.0"},"qs":{"from":"qs@~0.6.0","resolved":"https://registry.npmjs.org/qs/-/qs-0.6.6.tgz","version":"0.6.6"},"tunnel-agent":{"from":"tunnel-agent@~0.3.0","version":"0.3.0"}},"from":"request@~2.21.0","version":"2.21.0"}},"from":"sauce-tunnel@~1.0.0","version":"1.0.2"},"saucelabs":{"from":"saucelabs@~0.1.0","version":"0.1.1"},"wd":{"dependencies":{"archiver":{"dependencies":{"iconv-lite":{"from":"iconv-lite@~0.2.11","version":"0.2.11"},"readable-stream":{"from":"readable-stream@~1.0.2","version":"1.0.24"}},"from":"archiver@~0.4.6","version":"0.4.10"},"async":{"from":"async@0.2.x","version":"0.2.9"},"lodash":{"from":"lodash@~1.3.1","version":"1.3.1"},"q":{"from":"q@0.9.x","version":"0.9.7"},"request":{"dependencies":{"aws-sign":{"from":"aws-sign@~0.3.0","version":"0.3.0"},"cookie-jar":{"from":"cookie-jar@~0.3.0","version":"0.3.0"},"forever-agent":{"from":"forever-agent@~0.5.0","version":"0.5.0"},"form-data":{"dependencies":{"combined-stream":{"dependencies":{"delayed-stream":{"from":"delayed-stream@0.0.5","resolved":"https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz","version":"0.0.5"}},"from":"combined-stream@~0.0.4","version":"0.0.4"}},"from":"form-data@0.0.8","resolved":"https://registry.npmjs.org/form-data/-/form-data-0.0.8.tgz","version":"0.0.8"},"hawk":{"dependencies":{"boom":{"dependencies":{"hoek":{"from":"hoek@0.9.x","version":"0.9.1"}},"from":"boom@0.4.x","version":"0.4.2"},"cryptiles":{"from":"cryptiles@0.2.x","version":"0.2.2"},"hoek":{"from":"hoek@0.8.x","version":"0.8.5"},"sntp":{"dependencies":{"hoek":{"from":"hoek@0.9.x","version":"0.9.1"}},"from":"sntp@0.2.x","version":"0.2.4"}},"from":"hawk@~0.13.0","version":"0.13.1"},"http-signature":{"dependencies":{"asn1":{"from":"asn1@0.1.11","resolved":"https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz","version":"0.1.11"},"assert-plus":{"from":"assert-plus@0.1.2","resolved":"https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz","version":"0.1.2"},"ctype":{"from":"ctype@0.5.2","resolved":"https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz","version":"0.5.2"}},"from":"http-signature@~0.9.11","version":"0.9.11"},"json-stringify-safe":{"from":"json-stringify-safe@~4.0.0","version":"4.0.0"},"mime":{"from":"mime@~1.2.9","version":"1.2.11"},"node-uuid":{"from":"node-uuid@~1.4.0","version":"1.4.1"},"oauth-sign":{"from":"oauth-sign@~0.3.0","version":"0.3.0"},"qs":{"from":"qs@~0.6.0","resolved":"https://registry.npmjs.org/qs/-/qs-0.6.6.tgz","version":"0.6.6"},"tunnel-agent":{"from":"tunnel-agent@~0.3.0","version":"0.3.0"}},"from":"request@~2.21.0","version":"2.21.0"},"vargs":{"from":"vargs@~0.1.0","version":"0.1.0"}},"from":"wd@~0.1.0","version":"0.1.5"}},"from":"grunt-saucelabs@~4.1.2","version":"4.1.2"},"grunt-sed":{"dependencies":{"replace":{"dependencies":{"colors":{"from":"colors@0.5.x","version":"0.5.1"},"minimatch":{"dependencies":{"lru-cache":{"from":"lru-cache@2","version":"2.5.0"},"sigmund":{"from":"sigmund@~1.0.0","version":"1.0.0"}},"from":"minimatch@~0.2.9","version":"0.2.14"},"nomnom":{"dependencies":{"underscore":{"from":"underscore@~1.4.4","version":"1.4.4"}},"from":"nomnom@1.6.x","version":"1.6.2"}},"from":"replace@~0.2.4","version":"0.2.7"}},"from":"grunt-sed@~0.1.1","version":"0.1.1"},"load-grunt-tasks":{"dependencies":{"findup-sync":{"dependencies":{"glob":{"dependencies":{"graceful-fs":{"from":"graceful-fs@~1.2.0","version":"1.2.3"},"inherits":{"from":"inherits@1","version":"1.0.0"},"minimatch":{"dependencies":{"lru-cache":{"from":"lru-cache@2","version":"2.5.0"},"sigmund":{"from":"sigmund@~1.0.0","version":"1.0.0"}},"from":"minimatch@~0.2.11","version":"0.2.14"}},"from":"glob@~3.1.21","version":"3.1.21"},"lodash":{"from":"lodash@~1.0.1","version":"1.0.1"}},"from":"findup-sync@~0.1.2","version":"0.1.2"},"globule":{"dependencies":{"glob":{"dependencies":{"inherits":{"from":"inherits@2","version":"2.0.1"}},"from":"glob@~3.2.7","version":"3.2.8"},"lodash":{"from":"lodash@~2.4.1","resolved":"https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz","version":"2.4.1"},"minimatch":{"dependencies":{"lru-cache":{"from":"lru-cache@2","version":"2.5.0"},"sigmund":{"from":"sigmund@~1.0.0","version":"1.0.0"}},"from":"minimatch@~0.2.11","version":"0.2.14"}},"from":"globule@~0.2.0","resolved":"https://registry.npmjs.org/globule/-/globule-0.2.0.tgz","version":"0.2.0"}},"from":"load-grunt-tasks@~0.3.0","resolved":"https://registry.npmjs.org/load-grunt-tasks/-/load-grunt-tasks-0.3.0.tgz","version":"0.3.0"},"markdown":{"dependencies":{"nopt":{"dependencies":{"abbrev":{"from":"abbrev@1","version":"1.0.4"}},"from":"nopt@~2.1.1","version":"2.1.2"}},"from":"markdown@~0.5.0","version":"0.5.0"}},"name":"bootstrap","version":"3.1.0"} \ No newline at end of file
diff --git a/bower_components/bootstrap/test-infra/requirements.txt b/bower_components/bootstrap/test-infra/requirements.txt
new file mode 100644
index 00000000..95fbf1a3
--- /dev/null
+++ b/bower_components/bootstrap/test-infra/requirements.txt
@@ -0,0 +1 @@
+boto==2.20.0
diff --git a/bower_components/bootstrap/test-infra/s3_cache.py b/bower_components/bootstrap/test-infra/s3_cache.py
new file mode 100755
index 00000000..adc466e0
--- /dev/null
+++ b/bower_components/bootstrap/test-infra/s3_cache.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python2.7
+from __future__ import absolute_import, unicode_literals, print_function, division
+
+from sys import argv
+from os import environ, stat, remove as _delete_file
+from os.path import isfile, dirname, basename, abspath
+from hashlib import sha256
+from subprocess import check_call as run
+
+from boto.s3.connection import S3Connection
+from boto.s3.key import Key
+from boto.exception import S3ResponseError
+
+
+NEED_TO_UPLOAD_MARKER = '.need-to-upload'
+BYTES_PER_MB = 1024 * 1024
+try:
+ BUCKET_NAME = environ['TWBS_S3_BUCKET']
+except KeyError:
+ raise SystemExit("TWBS_S3_BUCKET environment variable not set!")
+
+
+def _sha256_of_file(filename):
+ hasher = sha256()
+ with open(filename, 'rb') as input_file:
+ hasher.update(input_file.read())
+ file_hash = hasher.hexdigest()
+ print('sha256({}) = {}'.format(filename, file_hash))
+ return file_hash
+
+
+def _delete_file_quietly(filename):
+ try:
+ _delete_file(filename)
+ except (OSError, IOError):
+ pass
+
+
+def _tarball_size(directory):
+ kib = stat(_tarball_filename_for(directory)).st_size // BYTES_PER_MB
+ return "{} MiB".format(kib)
+
+
+def _tarball_filename_for(directory):
+ return abspath('./{}.tar.gz'.format(basename(directory)))
+
+
+def _create_tarball(directory):
+ print("Creating tarball of {}...".format(directory))
+ run(['tar', '-czf', _tarball_filename_for(directory), '-C', dirname(directory), basename(directory)])
+
+
+def _extract_tarball(directory):
+ print("Extracting tarball of {}...".format(directory))
+ run(['tar', '-xzf', _tarball_filename_for(directory), '-C', dirname(directory)])
+
+
+def download(directory):
+ _delete_file_quietly(NEED_TO_UPLOAD_MARKER)
+ try:
+ print("Downloading {} tarball from S3...".format(friendly_name))
+ key.get_contents_to_filename(_tarball_filename_for(directory))
+ except S3ResponseError as err:
+ open(NEED_TO_UPLOAD_MARKER, 'a').close()
+ print(err)
+ raise SystemExit("Cached {} download failed!".format(friendly_name))
+ print("Downloaded {}.".format(_tarball_size(directory)))
+ _extract_tarball(directory)
+ print("{} successfully installed from cache.".format(friendly_name))
+
+
+def upload(directory):
+ _create_tarball(directory)
+ print("Uploading {} tarball to S3... ({})".format(friendly_name, _tarball_size(directory)))
+ key.set_contents_from_filename(_tarball_filename_for(directory))
+ print("{} cache successfully updated.".format(friendly_name))
+ _delete_file_quietly(NEED_TO_UPLOAD_MARKER)
+
+
+if __name__ == '__main__':
+ # Uses environment variables:
+ # AWS_ACCESS_KEY_ID -- AWS Access Key ID
+ # AWS_SECRET_ACCESS_KEY -- AWS Secret Access Key
+ argv.pop(0)
+ if len(argv) != 4:
+ raise SystemExit("USAGE: s3_cache.py <download | upload> <friendly name> <dependencies file> <directory>")
+ mode, friendly_name, dependencies_file, directory = argv
+
+ conn = S3Connection()
+ bucket = conn.lookup(BUCKET_NAME)
+ if bucket is None:
+ raise SystemExit("Could not access bucket!")
+
+ dependencies_file_hash = _sha256_of_file(dependencies_file)
+
+ key = Key(bucket, dependencies_file_hash)
+ key.storage_class = 'REDUCED_REDUNDANCY'
+
+ if mode == 'download':
+ download(directory)
+ elif mode == 'upload':
+ if isfile(NEED_TO_UPLOAD_MARKER): # FIXME
+ upload(directory)
+ else:
+ print("No need to upload anything.")
+ else:
+ raise SystemExit("Unrecognized mode {!r}".format(mode))
diff --git a/bower_components/bootstrap/test-infra/sauce_browsers.yml b/bower_components/bootstrap/test-infra/sauce_browsers.yml
new file mode 100644
index 00000000..9ec9c4cb
--- /dev/null
+++ b/bower_components/bootstrap/test-infra/sauce_browsers.yml
@@ -0,0 +1,83 @@
+[
+ # Docs: https://saucelabs.com/docs/platforms/webdriver
+
+ {
+ browserName: "safari",
+ platform: "OS X 10.9"
+ },
+ {
+ browserName: "chrome",
+ platform: "OS X 10.9",
+ version: "31"
+ },
+ {
+ browserName: "firefox",
+ platform: "OS X 10.9"
+ },
+
+ # Mac Opera not currently supported by Sauce Labs
+
+ {
+ browserName: "internet explorer",
+ version: "11",
+ platform: "Windows 8.1"
+ },
+ {
+ browserName: "internet explorer",
+ version: "10",
+ platform: "Windows 8"
+ },
+ # {
+ # browserName: "internet explorer",
+ # version: "9",
+ # platform: "Windows 7"
+ # },
+ # {
+ # browserName: "internet explorer",
+ # version: "8",
+ # platform: "Windows 7"
+ # },
+
+ # { # Unofficial
+ # browserName: "internet explorer",
+ # version: "7",
+ # platform: "Windows XP"
+ # },
+
+ {
+ browserName: "chrome",
+ platform: "Windows 8.1"
+ },
+ {
+ browserName: "firefox",
+ platform: "Windows 8.1"
+ },
+
+ # Win Opera 15+ not currently supported by Sauce Labs
+
+ {
+ browserName: "iphone",
+ platform: "OS X 10.9",
+ version: "7"
+ },
+
+ # iOS Chrome not currently supported by Sauce Labs
+
+ # Linux (unofficial)
+ {
+ browserName: "chrome",
+ platform: "Linux"
+ },
+ {
+ browserName: "firefox",
+ platform: "Linux"
+ }
+
+ # Android Chrome not currently supported by Sauce Labs
+
+ # { # Android Browser (super-unofficial)
+ # browserName: "android",
+ # version: "4.0",
+ # platform: "Linux"
+ # }
+]
diff --git a/bower_components/bootstrap/test-infra/shrinkwrap.js b/bower_components/bootstrap/test-infra/shrinkwrap.js
new file mode 100644
index 00000000..7a0328d4
--- /dev/null
+++ b/bower_components/bootstrap/test-infra/shrinkwrap.js
@@ -0,0 +1,24 @@
+/* jshint node: true */
+
+/*
+This Grunt task updates the npm-shrinkwrap.canonical.json file that's used as the key for Bootstrap's npm packages cache.
+This task should be run and the updated file should be committed whenever Bootstrap's dependencies change.
+*/
+
+var canonicallyJsonStringify = require('canonical-json');
+var NON_CANONICAL_FILE = 'npm-shrinkwrap.json';
+var DEST_FILE = 'test-infra/npm-shrinkwrap.canonical.json';
+
+
+function updateShrinkwrap(grunt) {
+ // Assumption: Non-canonical shrinkwrap already generated by prerequisite Grunt task
+ var shrinkwrapData = grunt.file.readJSON(NON_CANONICAL_FILE);
+ grunt.log.writeln('Deleting ' + NON_CANONICAL_FILE.cyan + '...');
+ grunt.file.delete(NON_CANONICAL_FILE);
+ // Output as Canonical JSON in correct location
+ grunt.file.write(DEST_FILE, canonicallyJsonStringify(shrinkwrapData));
+ grunt.log.writeln('File ' + DEST_FILE.cyan + ' updated.');
+}
+
+
+module.exports = updateShrinkwrap;
diff --git a/bower_components/bootstrap/test-infra/uncached-npm-install.sh b/bower_components/bootstrap/test-infra/uncached-npm-install.sh
new file mode 100755
index 00000000..1c562498
--- /dev/null
+++ b/bower_components/bootstrap/test-infra/uncached-npm-install.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+cp test-infra/npm-shrinkwrap.canonical.json npm-shrinkwrap.json
+npm install
+rm npm-shrinkwrap.json